Questões Resolvidas - JAVA
(FCC 2011 TRT 14 TECNICO)Em Java,
(A) é possível criar e manipular objetos, mas não removê-los, pois a remoção é manipulada automaticamente pelo sistema.
(B) classes são definidas através do uso da palavra chave class, seguido do nome da classe, que, entre outras restrições, não pode ser iniciado por um número.
(C) quando uma classe é criada e não há nenhuma referência à sua superclasse, implicitamente a classe criada é derivada diretamente da classe Object.
(D) construtores da superclasse podem ser explicitamente invocados usando a palavra-chave super.
(E) todas as determinações de métodos a executar ocorrem através de ligação tardia
Referências
Características gerais da linguagem
Aplicativos independentes e "applets"
Referências
Livros
Java 2 - Aprenda em 21 dias, L. Lemay e R. Cadenhead, Editora Campus Ltda (1999).
Completo, didático, atual, alguns erros de tradução.
Java: How to Program, 2nd edition, H. M. Deitel e P. J. Deitel, Prentice Hall (1998);
3ra edição em Portugués, Java: Como Programar, Editora Bookman (2000).
Completo, excelente, vem com um CD contendo exemplos comentados.
The Java Programming Language, 3rd edition, K. Arnold, J. Gosling e D. Holmes, Addison-Wesley (2000);
Referência clássica, co-autorada pelo guru mor James Gosling.
Java for Engineers and Scientists, S. J. Chapman, Prentice Hall (2000).
Completo, apresenta aplicações na ciência e na engenharia.
Thinking in Java, 2nd edition, B. Eckel, Prentice Hall (2000).
Disponível na rede: http://www.mindview.net/Books/TIJ/
The Java Tutorial, 3rd edition, M. Campione, K. Walrath e A. Huml, Addison-Wesley (2000).
Disponível na rede: http://java.sun.com/docs/books/tutorial/
Sítios
Sítio oficial Java da Sun: http://java.sun.com/
Gamelan: tudo sobre java: http://softwaredev.earthweb.com/java
Jars: mais sobre java: http://www.jars.com/
avaliação de "applets" de ciência: http://www.jars.com/jars_categories_java_science.html
Ferramentas
Básica: Java Development Kit (JDK), disponível gratuitamente no sítio da Sun.
JDK1.0 (95-96), ultrapassado.
JDK1.1 (97-98), suportado pelos navegadores atuais.
JDK1.2 (99) ainda não suportado pelos navegadores (requer Java Plug-in).
SDK1.3 (01), evolução do anterior. O nome mudou para Software Development Kit. Outro nome usado é J2SE (Java 2, Standard Edition).
Ambientes integrados de desenvolvimento
Forte for Java (Sun, versão Comunidade gratuita) http://www.sun.com/forte/ffj/
Kawa (Allaire, pago) http://www.allaire.com/products/kawa/index.cfm
Simplicity for Java (Data Representations, pago) http://www.datarepresentations.com/
JBuilder (Borland-Inprise, versão Pessoal gratuita) http://www.borland.com/jbuilder/
Visual Café (Webgain, pago) http://www.webgain.com/products/visual_cafe/
VisualAge for Java (IBM, pago) http://www.software.ibm.com/ad/vajava/
JCreator (Xinox Software, versão gratuita) http://www.jcreator.com/
Características gerais da linguagem
Particularidades e vantagens
Programas compilados independentes de plataforma.
Os arquivos de byte são pequenos -> carregados rapidamente pela Internet.
Ampla biblioteca de componentes e recursos - Gratuita!
Componentes gráficas
Tratamento de eventos
Formatação de texto
Funções matemáticas
Entrada e saida
Computação em rede
Bancos de dados
. . .
Programação (relativamente) simples e natural.
Programação "orientada a objeto" - facilita a reutilização de código.
Permite linhas múltiplas de execução.
Comparação com C++:
Possui herança simples. Implementa uma forma segura de herança múltipla através de "interfaces".
Não permite aritmética de ponteiros.
Possui desalocação automática de memória ("coletor de lixo").
Limitações e desvantagens
Velocidade de execução relativamente baixa
Pode ser melhorada usando compiladores JIT ("just in time") que transformam o bytecode em código de máquina antes da execução.
Evolução rápida -> mutiplicidade de versões
A versão mais recente, batizada Java 2 Platform (SDK1.3) ainda não está implementada nos navegadores. Requer o
Java Plug-in http://java.sun.com/products/plugin/
Aplicativos independentes e applets
Aplicativo independente
Programa que é executado fora de um navegador. Utilize um editor (o Bloco de Notas ou o WordPad se você está trabalhando no Windows) para digitar e editar o arquivo fonte. Cuidado ao digitar: Java distingue maiúsculas e minúsculas. O esquema mínimo para o programa de um aplicativo independente é:
public class MeuAplicativo
{
public static void main( String args[] )
{
. . .
}
}
onde . . . indica comandos que definem o que o programa vai fazer. O programa deve ser salvo num arquivo chamado: MeuAplicativo.java . Vê-se que para construir um programa em Java, precisamos definir uma classe. O corpo da classe, contido entre as chaves mais externas, contem em geral definições de variáveis e métodos, que especificam aquilo que a classe pode fazer. No exemplo acima, o único método presente é o método principal, que deve estar presente em qualquer aplicativo independente e é executado automaticamente quando o aplicativo é executado. Parâmetros podem ser passados ao programa no comando de execução, na forma de uma lista de cadeias de caráters (tipo String) chamada args[] no exemplo acima. Os colchetes indicam que trata-se de uma lista.
Não detalharemos agora o significado das demais palavras chaves presentes no código acima. Por enquanto, estamos apenas "dando a receita" para construir o aplicativo o mais simples possível.
Para compilar o programa, digite na linha de comando (do DOS se você está usando Windows):
javac MeuAplicativo.java
Se a compilação for bem sucedida, isto gera uma arquivo MeuAplicativo.class na mesma pasta.
Para executar o programa, digite na linha de comando:
java MeuAplicativo
Se o seu programa requer parâmetros de entrada (digamos a1 e a2), estes devem ser acrescentados ao comando:
java MeuAplicativo a1 a2
Applet
Programa que é executado dentro de uma navegador. Utilize um editor para digitar e editar o arquivo fonte. O esquema mínimo para o programa de um applet é:
import java.applet.*;
public class MeuApplet extends Applet
{
public void init( )
{
. . .
}
}
onde . . . indica comandos que definem o que o programa vai fazer. O programa deve ser salvo num arquivo chamado MeuApplet.java . O método init() é chamado automaticamente pelo navegador quando ele carrega o applet. Note que o pacote java.applet deve ser importado para ter acesso à classe Applet.
Para compilar o programa, digite na linha de comando:
javac MeuApplet.java
Se a compilação for bem sucedida, isto gera uma arquivo MeuApplet.class na mesma pasta.
O applet é carregado por um navegador através de um arquivo HTML. O arquivo mínimo é:
HTML
HEAD
TITLEMeu Primeiro Applet/TITLE
/HEAD
BODY>
APPLET CODE="MeuApplet.class" WIDTH=400 HEIGHT=160
/APPLET
/BODY
/HTML
Digamos que o nome deste arquivo seja MeuApplet.htm . Para testar o seu applet sem ter que abrir um navegador, digite na linha de comando:
appletviewer MeuApplet.htm
Dicas
Console Java
Procure este item no menu do seu navegador para abrir uma janela que dá informaçães sobre a máquina virtual e mostra eventuais mensagens de erro.
Documentação:
A pasta /docs do JDK contem uma documentação completa em formato HTML. Em especial, a pasta docs/api documenta as bibliotecas.
Demonstrações
A pasta /demo do JDK contem vários applets demonstrativos, com arquivos fontes incluídos.
Caminho
Para que você possa executar o compilador (javac), o interpretador (java) e o visualizador de applet (appletviewer), é necessário que a pasta /bin do JDK esteja incluída no seu PATH. No Windows, isto é feito editando o arquivo Autoexec.bat.
Prática
Você pode agora construir os seus primeiros aplicativos e applets, seguindo o roteiro apresentado na Prática 1.
________________________________________________________________
Elementos básicos da síntaxe
Tipos de dados primitivos
Expressões e operadores
Estruturas de controle de fluxo
Elementos básicos da síntaxe
Comandos
Comandos em Java são separadas por um ponto-vírgula. É permitido colocar vários comandos sobre a mesma limha.
Comandos são organizadas em blocos definidos por chaves {...}. Em especial, classes e métodos são blocos. Blocos podem conter outros blocos.
Variáveis
Nomes de variáveis devem começar com uma letra, um caráter de sublinhado _ ou um cifrão $. Não podem começar por um número.
É convencional (mas não obrigatório) usar uma letra minúscula para a primeira letra do nome de uma variável.
Todas as variáveis de tipos primitivos precisam ser declaradas e inicializadas, através de uma instrução da forma
nomeDaVariahvel = ;
onde representa um dos tipos primitivos (discutidos na próxima seção) e um valor adequado para este tipo. Repare a notação <...> utilizada nestas notas para indicar um elemento de código que deve ser substituido por uma palavra chave, ou um valor concreto. Existem valores "default" para os tipos primitivos.
As variáveis associadas a objetos, que são referências aos mesmos, precisam ser declaradas. Os objetos por sua vez precisam ser criados. Declaração e criação podem sem realizadas por comandos separados:
NomeDaClasse nomeDoObjeto;
nomeDoObjeto = new NomeDaClasse();
A primeira linha informa que a variável nomeDoObjeto vai designar um objeto que é uma instância da classe NomeDaClasse. A segunda linha cria um novo objeto pertencente àquela classe e atribui a este objeto a referência nomeDoObjeto. A parte NomeDaClasse() do comando é na verdade uma chamada ao método construtor da classe. Em muitos casos, este método pode receber parâmetros que são incluídos nas parénteses e servem para especificar a inicialização do objeto.
As duas linhas acima podem ser combinadas numa só:
NomeDaClasse nomeDoObjeto = new NomeDaClasse();
O escopo de uma variável, ou seja a região do programa na qual ela está definida, é limitado ao bloco no qual ela foi declarada.
Comentários
Um comentário curto relativo a uma linha de código pode ser incluido no fim da linha, precedido de // :
... ; // Comentário sobre o comando ...
Um comentário precedido de /* e seguido de */ pode ocupar várias linhas:
/* Comentário maior,
que pode ocupar várias linhas. */
Obviamente, isto serve também para forçar o compilador a ignorar temporariamente um trecho de código.
O JDK oferece um recurso para gerar automaticamente um arquivo HTML documentando uma classe. Comentários que forem precedidos de /** e seguidos de */ serão incluidos neste arquivo:
/** Este comentário será incluído no arquivo HTML
documentando a classe. */
Para gerar a documentação relativa à(s) classe(s) cuja(s) fonte(s) estam no arquivo MeuPrograma.java, digita-se na linha de comando:
javadoc MeuPrograma.java
Tipos de dados primitivos
Números inteiros
Há quatro tipos de inteiros em Java:
Tipo Tamanho Valor
byte 8 bits -128 a 127
short 16 bits -32.768 a 32.767
int 32 bits -2.147.483.648 a 2.147.483.647
long 64 bits -9.223.372.036.854.775.808 a 9.223.372.036.854.775.807
Um número inteiro pode sempre ser atribuído a outro de maior precisão:
int a = 274;
long b = a;
A operação inversa requer coerção explicita ("casting"):
long a = 274;
int b = ( int ) a;
Sem esta coerção, haverá erro de compilação. Note que, mesmo com a coerção, ainda pode haver problema na execução, caso o valor do número atribuído ultrapassar o maior valor possível para o tipo em questão.
Números em ponto flutuante
Há dois tipos, de precisão simples e de precisão dupla:
Tipo Tamanho Valor
float 32 bits -3.40292347E+38 a +3.40292347E+38
double 64 bits -1.79769313486231570E+308 a +1.79769313486231570E+308
Um número de precisão simples pode sempre ser atribuído a outro de precisão dupla:
float a = 2.74F; // F (ou f) após o literal indica precisão simples
double b = a;
A operação inversa requer coerção explicita:
double a = 2.74e12;
float b = ( float ) a;
Sem esta coerção, haverá erro de compilação. De novo, mesmo com a coerção, ainda pode haver problema na execução, caso o valor do número de precisão dupla ultrapassar o maior valor possível para um número de precisão simples.
Note que Java não possui tipos primitivos representando números complexos. Não há recursos para lidar com números complexos nas bibliotecas fornecidas com o JDK, mas vários pacotes avulsos são disponíveis. Não sendo tipos primitivos, números complexos devem ser objetos em Java.
Caráteres
Há um tipo primitivo que representa um caráter:
Tipo Tamanho Valor
char 16 bits '\u0000' a '\uFFFF'
Java utiliza o padrão de caráteres Unicode, que abrange os conjuntos de caráteres de muitas linguas.
Um literal de caráter é especificado por um único caráter entre apóstrofes simples:
char exemplo = 'a';
Para caráteres de escape, usa-se a barra invertida:
Escape Significado
\n nova linha
\t tabulação
\b passo para trás
\r retorno do carro
\\ barra invertida
\' apóstrofe
\" aspas
Booleanos
São variáveis lógicas que podem assumir os valores verdadeiro e falso:
Tipo Tamanho Valor
boolean 1 bit true ou false
Note que, em Java, estas variáveis não podem ser interpretadas como os números inteiros 0 e 1.
Expressões e operadores
Uma expressão é uma instrução de realizar uma operação que produz um valor (valor de retorno). Operadores são símbolos especiais utilizados para operações matemáticas, atribuições, comparações e operações lógicas.
Operadores aritméticos
Os operadores aritméticos disponíveis em Java são:
Operador Significado
+ adição
- subtração
* multiplicação
/ divisão
% resto da divisão (módulo)
Note que Java não possui um operador específico para potência (tal como ** em FORTRAN). Para calcular uma potência, você deve usar o método pow da classe Math do pacote lang, ou seja, para calcular xy, você escreve Math.pow(x, y), onde x e y são de tipo double, e o resultado também.
Operadores de atribuição
Estes operadores são simplesmente uma notação compacta para uma operação aritmética seguida da atribuição do valor de retorno à variável que continha o primeiro termo da operação.
Operador Exemplo Expressão equivalente
+= x += y x = x + y
-= x -= y x = x - y
*= x *= y x = x * y
/= x /= y x = x / y
%= x %= y x = x % y
Operadores de incremento e decremento
São operadores que atuam sobre uma única variável numérica, aumentando ou diminuindo o seu valor de uma unidade:
Operador Exemplo Significado
++ ++a adicionar 1 à variável a e depois calcular a expressão na qual a reside
a++ calcular a expressão na qual a reside e depois adicionar 1 à variável a
-- --a subtrair 1 da variável a e depois calcular a expressão na qual a reside
a-- calcular a expressão na qual a reside e depois subtrair 1 da variável a
Exemplo:
int x = 5, y = 7; // x vale 5 e y vale 7
y += ++x; // x agora vale 5 + 1 = 6 e y vale 7 + 6 = 13
x -= y--; // x agora vale 6 - 13 = - 7 e y vale 13 - 1 = 12
Operadores de comparação
Estes operadores atuam sobre valores numéricos e retornam valores booleanos, true (verdadeiro) ou false (falso):
Operador Significado
== igual a
!= diferente de
< menor que
> maior que
<= menor ou igual a
>= maior ou igual a
O operador == também serve para comparar outros tipos de dados, inclusive objetos.
Operadores lógicos
Estes operadores atuam sobre valores booleanos e retornam valores booleanos, true ou false.
Operador Significado Exemplo Explicação
&& E ("logical AND") a && b retorna true se a e b forem ambos true. Senão retorna false. Se a for false, b não é avaliada.
& E ("boolean logical AND") a & b retorna true se a e b forem ambos true. Senão retorna false. Ambas expressões a e b são sempre avaliadas.
|| OU ("logical OR") a || b retorna true se a ou b for true. Senão retorna false. Se a for true, b não é avaliada.
| OU ("boolean logical inclusive OR") a | b retorna true se a ou b for true. Senão retorna false. Ambas expressões a e b são sempre avaliadas.
^ OU EXCLUSIVO ("boolean logical exclusive OR") a ^ b retorna true se a for true e b for false ou vice-versa. Senão retorna false
! NÃO ("logical NOT") !a retorna true se a for false. Senão retorna false
Precedência e associatividade dos operadores
Os operadores estão listados na tabela abaixo em ordem decrescente de prioridade.
Operador Associatividade
() da esquerda para a direita
++ -- + - ! unários; da direita para a esquerda
* / % da esquerda para a direita
+ - da esquerda para a direita
< <= > >= da esquerda para a direita
== != da esquerda para a direita
& da esquerda para a direita
^ da esquerda para a direita
| da esquerda para a direita
&& da esquerda para a direita
|| da esquerda para a direita
= += -= *= /= %= da direita para a esquerda
Estruturas de controle de fluxo
Estruturas de seleção
Para executar um comando sujeita a uma condição:
if( ) ;
A variável ou expressão condição deve ser do tipo boolean. O comando é executado se condição possuir o valor true.
Para executar vários comandos sujeitos a uma condição, coloca-se os comandos num bloco:
if( ){
;
;
<...
}
Para executar certos comandos caso uma condição for satisfeita, e outros comandos caso contrário:
if( ){
;
;
}
else{
;
;
}
No este exemplo, comando 1 e comando 2 são executados se condição for true. Caso contrário, comando 3 e comando 4 são executados.
O operador ?: pode ser utilizado em certos casos simples como um atalho para a estrutura if...else:
= ? : ;
Aqui, recebe se for true e se for false.
Escolhas múltiplas de comandos a serem executados, dependendo do valor de uma variável, podem ser implementadas da seguinte maneira:
switch( ){
case:
;
;
break;
case: case :
;
;
break;
case:
...
break;
default:
;
}
O deve ser de tipo int ou char. Se ele assume o , e são executados. O comando break determina então a saida do bloco. Se o assume o ou o , e são executados. O comando break de novo determina a saida do bloco, etc. O é executado se o valor do é diferente de todos os valores listados.
Estruturas de repetição (laços)
Para repetir um conjunto de instruções, controlando o número de repetições com um contador:
for( int i =; i <= ; i++ ){
;
;
}
Note a declaração int i do contador. O teste i <= é realizado antes de cada entrada no laço. O incremento i++ é realizado no fim de cada passagem pelo laço.
Para repetir um conjunto de instruções enquanto uma certa condição for satisfeita:
while( ){
;
;
}
Obviamente a deve ser do tipo boolean. Uma forma um pouco diferente é:
do{
;
;
}
while( )
A diferença é que a é testada após a execução do bloco de instruções. Portanto, este bloco é executado no mínimo uma vez.
Para interromper um laço, usa-se o comando break:
while( ){
;
if( ) break;
;
}
O laço é interrompido quando a assume o valor true.
Para passar à iteração seguinte de um laço, usa-se o comando continue:
for( int i =; i <= ; i++ ){
;
if( ) continue;
;
<}
Se for true, o programa "pula por cima" do e passa à iteração seguinte.
Os comandos break e continue sem rótulos atuam sobre o laço mais interno no qual eles se encontram. No caso de laços aninhados, comandos rotulados podem ser utilizados para saltar para um laço mais externo:
rohtulo:{
while( ){
;
for( int i =; i <= ; i++ ){
if( ) break rohtulo;
;
}
}
;
}
Se for true, o programa sai do bloco rotulado, pulando por cima de e .
rohtulo:{
for( int k =; k <= ; k++ ){
;
for( int i =; i <= ; i++ ){
if( ) continue rohtulo;
;
}
}
}
Se for true, o programa sai de ambos os laços.
Elementos básicos da síntaxe
Tipos de dados primitivos
Expressões e operadores
Estruturas de controle de fluxo
Elementos básicos da síntaxe
Comandos
Comandos em Java são separadas por um ponto-vírgula. É permitido colocar vários comandos sobre a mesma limha.
Comandos são organizadas em blocos definidos por chaves {...}. Em especial, classes e métodos são blocos. Blocos podem conter outros blocos.
Variáveis
Nomes de variáveis devem começar com uma letra, um caráter de sublinhado _ ou um cifrão $. Não podem começar por um número.
É convencional (mas não obrigatório) usar uma letra minúscula para a primeira letra do nome de uma variável.
Todas as variáveis de tipos primitivos precisam ser declaradas e inicializadas, através de uma instrução da forma
nomeDaVariahvel = ;
onde representa um dos tipos primitivos (discutidos na próxima seção) e um valor adequado para este tipo. Repare a notação <...> utilizada nestas notas para indicar um elemento de código que deve ser substituido por uma palavra chave, ou um valor concreto. Existem valores "default" para os tipos primitivos.
As variáveis associadas a objetos, que são referências aos mesmos, precisam ser declaradas. Os objetos por sua vez precisam ser criados. Declaração e criação podem sem realizadas por comandos separados:
NomeDaClasse nomeDoObjeto;
nomeDoObjeto = new NomeDaClasse();
A primeira linha informa que a variável nomeDoObjeto vai designar um objeto que é uma instância da classe NomeDaClasse. A segunda linha cria um novo objeto pertencente àquela classe e atribui a este objeto a referência nomeDoObjeto. A parte NomeDaClasse() do comando é na verdade uma chamada ao método construtor da classe. Em muitos casos, este método pode receber parâmetros que são incluídos nas parénteses e servem para especificar a inicialização do objeto.
As duas linhas acima podem ser combinadas numa só:
NomeDaClasse nomeDoObjeto = new NomeDaClasse();
O escopo de uma variável, ou seja a região do programa na qual ela está definida, é limitado ao bloco no qual ela foi declarada.
Comentários
Um comentário curto relativo a uma linha de código pode ser incluido no fim da linha, precedido de // :
... ; // Comentário sobre o comando ...
Um comentário precedido de /* e seguido de */ pode ocupar várias linhas:
/* Comentário maior,
que pode ocupar várias linhas. */
Obviamente, isto serve também para forçar o compilador a ignorar temporariamente um trecho de código.
O JDK oferece um recurso para gerar automaticamente um arquivo HTML documentando uma classe. Comentários que forem precedidos de /** e seguidos de */ serão incluidos neste arquivo:
/** Este comentário será incluído no arquivo HTML
documentando a classe. */
Para gerar a documentação relativa à(s) classe(s) cuja(s) fonte(s) estam no arquivo MeuPrograma.java, digita-se na linha de comando:
javadoc MeuPrograma.java
Tipos de dados primitivos
Números inteiros
Há quatro tipos de inteiros em Java:
Tipo Tamanho Valor
byte 8 bits -128 a 127
short 16 bits -32.768 a 32.767
int 32 bits -2.147.483.648 a 2.147.483.647
long 64 bits -9.223.372.036.854.775.808 a 9.223.372.036.854.775.807
Um número inteiro pode sempre ser atribuído a outro de maior precisão:
int a = 274;
long b = a;
A operação inversa requer coerção explicita ("casting"):
long a = 274;
int b = ( int ) a;
Sem esta coerção, haverá erro de compilação. Note que, mesmo com a coerção, ainda pode haver problema na execução, caso o valor do número atribuído ultrapassar o maior valor possível para o tipo em questão.
Números em ponto flutuante
Há dois tipos, de precisão simples e de precisão dupla:
Tipo Tamanho Valor
float 32 bits -3.40292347E+38 a +3.40292347E+38
double 64 bits -1.79769313486231570E+308 a +1.79769313486231570E+308
Um número de precisão simples pode sempre ser atribuído a outro de precisão dupla:
float a = 2.74F; // F (ou f) após o literal indica precisão simples
double b = a;
A operação inversa requer coerção explicita:
double a = 2.74e12;
float b = ( float ) a;
Sem esta coerção, haverá erro de compilação. De novo, mesmo com a coerção, ainda pode haver problema na execução, caso o valor do número de precisão dupla ultrapassar o maior valor possível para um número de precisão simples.
Note que Java não possui tipos primitivos representando números complexos. Não há recursos para lidar com números complexos nas bibliotecas fornecidas com o JDK, mas vários pacotes avulsos são disponíveis. Não sendo tipos primitivos, números complexos devem ser objetos em Java.
Caráteres
Há um tipo primitivo que representa um caráter:
Tipo Tamanho Valor
char 16 bits '\u0000' a '\uFFFF'
Java utiliza o padrão de caráteres Unicode, que abrange os conjuntos de caráteres de muitas linguas.
Um literal de caráter é especificado por um único caráter entre apóstrofes simples:
char exemplo = 'a';
Para caráteres de escape, usa-se a barra invertida:
Escape Significado
\n nova linha
\t tabulação
\b passo para trás
\r retorno do carro
\\ barra invertida
\' apóstrofe
\" aspas
Booleanos
São variáveis lógicas que podem assumir os valores verdadeiro e falso:
Tipo Tamanho Valor
boolean 1 bit true ou false
Note que, em Java, estas variáveis não podem ser interpretadas como os números inteiros 0 e 1.
Expressões e operadores
Uma expressão é uma instrução de realizar uma operação que produz um valor (valor de retorno). Operadores são símbolos especiais utilizados para operações matemáticas, atribuições, comparações e operações lógicas.
Operadores aritméticos
Os operadores aritméticos disponíveis em Java são:
Operador Significado
+ adição
- subtração
* multiplicação
/ divisão
% resto da divisão (módulo)
Note que Java não possui um operador específico para potência (tal como ** em FORTRAN). Para calcular uma potência, você deve usar o método pow da classe Math do pacote lang, ou seja, para calcular xy, você escreve Math.pow(x, y), onde x e y são de tipo double, e o resultado também.
Operadores de atribuição
Estes operadores são simplesmente uma notação compacta para uma operação aritmética seguida da atribuição do valor de retorno à variável que continha o primeiro termo da operação.
Operador Exemplo Expressão equivalente
+= x += y x = x + y
-= x -= y x = x - y
*= x *= y x = x * y
/= x /= y x = x / y
%= x %= y x = x % y
Operadores de incremento e decremento
São operadores que atuam sobre uma única variável numérica, aumentando ou diminuindo o seu valor de uma unidade:
Operador Exemplo Significado
++ ++a adicionar 1 à variável a e depois calcular a expressão na qual a reside
a++ calcular a expressão na qual a reside e depois adicionar 1 à variável a
-- --a subtrair 1 da variável a e depois calcular a expressão na qual a reside
a-- calcular a expressão na qual a reside e depois subtrair 1 da variável a
Exemplo:
int x = 5, y = 7; // x vale 5 e y vale 7
y += ++x; // x agora vale 5 + 1 = 6 e y vale 7 + 6 = 13
x -= y--; // x agora vale 6 - 13 = - 7 e y vale 13 - 1 = 12
Operadores de comparação
Estes operadores atuam sobre valores numéricos e retornam valores booleanos, true (verdadeiro) ou false (falso):
Operador Significado
== igual a
!= diferente de
< menor que
> maior que
<= menor ou igual a
>= maior ou igual a
O operador == também serve para comparar outros tipos de dados, inclusive objetos.
Operadores lógicos
Estes operadores atuam sobre valores booleanos e retornam valores booleanos, true ou false.
Operador Significado Exemplo Explicação
&& E ("logical AND") a && b retorna true se a e b forem ambos true. Senão retorna false. Se a for false, b não é avaliada.
& E ("boolean logical AND") a & b retorna true se a e b forem ambos true. Senão retorna false. Ambas expressões a e b são sempre avaliadas.
|| OU ("logical OR") a || b retorna true se a ou b for true. Senão retorna false. Se a for true, b não é avaliada.
| OU ("boolean logical inclusive OR") a | b retorna true se a ou b for true. Senão retorna false. Ambas expressões a e b são sempre avaliadas.
^ OU EXCLUSIVO ("boolean logical exclusive OR") a ^ b retorna true se a for true e b for false ou vice-versa. Senão retorna false
! NÃO ("logical NOT") !a retorna true se a for false. Senão retorna false
Precedência e associatividade dos operadores
Os operadores estão listados na tabela abaixo em ordem decrescente de prioridade.
Operador Associatividade
() da esquerda para a direita
++ -- + - ! unários; da direita para a esquerda
* / % da esquerda para a direita
+ - da esquerda para a direita
< <= > >= da esquerda para a direita
== != da esquerda para a direita
& da esquerda para a direita
^ da esquerda para a direita
| da esquerda para a direita
&& da esquerda para a direita
|| da esquerda para a direita
= += -= *= /= %= da direita para a esquerda
Estruturas de controle de fluxo
Estruturas de seleção
Para executar um comando sujeita a uma condição:
if( ) ;
A variável ou expressão condição deve ser do tipo boolean. O comando é executado se condição possuir o valor true.
Para executar vários comandos sujeitos a uma condição, coloca-se os comandos num bloco:
if( ){
;
;
<...
}
Para executar certos comandos caso uma condição for satisfeita, e outros comandos caso contrário:
if( ){
;
;
}
else{
;
;
}
No este exemplo, comando 1 e comando 2 são executados se condição for true. Caso contrário, comando 3 e comando 4 são executados.
O operador ?: pode ser utilizado em certos casos simples como um atalho para a estrutura if...else:
= ? : ;
Aqui, recebe se for true e se for false.
Escolhas múltiplas de comandos a serem executados, dependendo do valor de uma variável, podem ser implementadas da seguinte maneira:
switch( ){
case:
;
;
break;
case: case :
;
;
break;
case:
...
break;
default:
;
}
O deve ser de tipo int ou char. Se ele assume o , e são executados. O comando break determina então a saida do bloco. Se o assume o ou o , e são executados. O comando break de novo determina a saida do bloco, etc. O é executado se o valor do é diferente de todos os valores listados.
Estruturas de repetição (laços)
Para repetir um conjunto de instruções, controlando o número de repetições com um contador:
for( int i =; i <= ; i++ ){
;
;
}
Note a declaração int i do contador. O teste i <= é realizado antes de cada entrada no laço. O incremento i++ é realizado no fim de cada passagem pelo laço.
Para repetir um conjunto de instruções enquanto uma certa condição for satisfeita:
while( ){
;
;
}
Obviamente a deve ser do tipo boolean. Uma forma um pouco diferente é:
do{
;
;
}
while( )
A diferença é que a é testada após a execução do bloco de instruções. Portanto, este bloco é executado no mínimo uma vez.
Para interromper um laço, usa-se o comando break:
while( ){
;
if( ) break;
;
}
O laço é interrompido quando a assume o valor true.
Para passar à iteração seguinte de um laço, usa-se o comando continue:
for( int i =; i <= ; i++ ){
;
if( ) continue;
;
<}
Se for true, o programa "pula por cima" do e passa à iteração seguinte.
Os comandos break e continue sem rótulos atuam sobre o laço mais interno no qual eles se encontram. No caso de laços aninhados, comandos rotulados podem ser utilizados para saltar para um laço mais externo:
rohtulo:{
while( ){
;
for( int i =; i <= ; i++ ){
if( ) break rohtulo;
;
}
}
;
}
Se for true, o programa sai do bloco rotulado, pulando por cima de e .
rohtulo:{
for( int k =; k <= ; k++ ){
;
for( int i =; i <= ; i++ ){
if( ) continue rohtulo;
;
}
}
}
Se for true, o programa sai de ambos os laços.
Cadeias de caráteres ("Strings")
Listas ("Arrays")
Cadeias de caráteres
Construção
Cadeias de caráteres são objetos, instâncias da classe String do pacote java.lang. Para declarar uma cadeia de caráteres e atribuir à mesma um valor literal, coloca-se este valor entre aspas:
String s = "Olá!";
Pode-se também criar explicitamente um novo objeto, chamando-se o método construtor da classe:
String s = new String( "Olá!" );
O método construtor pode receber outros tipos de argumentos, por exemplo uma lista dos caráteres que vão constituir a cadeia. Veja os detalhes na documentação.
Uma vez a cadeia criada, o seu conteudo não pode mudar. Se percisarmos de uma cadeia de caráteres cujo conteudo possa mudar, precisamos usar a classe StringBuffer do pacote java.lang, que não será discutida nestas notas.
Obviamente, uma dada referência do tipo String não precisa apontar sempre para o mesmo objeto. Assim, se tivermos
String s = new String( "Olá!" );
s = "Olá Mundo!";
a referência s
deixa de referir-se ao primeiro objeto criado (cujo conteudo erá "Olá!") e passa a referir-se ao objeto cujo conteudo é "Olá Mundo!"). Se não houver mais nenhuma referência apontando para o primeiro objeto, este estará marcado para ser varrido pelo coletor de lixo tão logo este resolver entrar em ação.
Comprimento
O método length permite descobrir o número de caráteres contidos numa String
. Por exemplo:
String s = new String( "Olá!" );
int nuhmero = s.length();
resulta no valor 4 para o inteiro chamado nuhmero.
Há outros métodos que permitem obter informação a respeito de uma String, por exemplo, descobrir qual o caráter numa dada posição. Veja a documentação.
Comparação
Para determinar se duas cadeias de caráteres possuem o mesmo conteudo, usa-se o método equals. Este método considera maiúsculas e minúsculas como diferentes. Se não desejamos fazer esta distinção, devemos usar o método equalsIgnoreCase.
É importante não confundir estas comparações de conteudo com o uso do operador ==, que, quando usado entre duas variáveis que são referências a objetos, serve para determinar se as duas variáveis apontam para o mesmo objeto.
Como ilustração, considere o seguinte trecho de código:
String s1, s2, s3, s4, s5, s6;
s1 = "Olá!";
s2 = "Olá!";
s3 = "olá!":
s4 = new String( "Olá!" );
s5 = s4;
s6 = new String( "Olá!" );
boolean b1, b2, b3, b4, b5, b6, b7, b8;
b1 = s2.equals( s1 );
b2 = s3.equals( s1 );
b3 = s3.equalsIgnoreCase( s1 );
b4 = s4.equals( s1 );
b5 = ( s2 == s1 );
b6 = ( s4 == s1 );
b7 = ( s4 == s5 );
b8 = ( s4 == s6 );
b9 = s4.equals( s6 );
Convença-se de que, após execução deste código, os seguintes booleanos possuem o valor true: b1, b3, b4, b5, b7 e b9. Já b2, b6 e b8 possuem o valor false.
Consulte a documentação para obter informação sobre métodos que permitem comparar pedaços de cadeias de caráteres e também determinar qual de duas cadeias vem primeira na ordem alfabética.
Concatenação
Para concatenar duas String, usa-se o método concat. Note que, já que o tamanho de uma String não pode mudar, a concatenação resulta na criação de um novo objeto.
Também pode-se usar o operador + para a concatenação. Por exemplo, no trecho de código abaixo, a mesma linha é impressa duas vezes:
String s1 = "Olá ";
String s2 = "Mundo!";
System.out.println( s1.concat( s2 ) );
System.out.println( s1 + s2 );
Consulte a documentação para obter informação sobre métodos que permitem modificar cadeias de caráteres, por exemplo alterando um caráter ou passando de minúsculas para maiúsculas. Lembre-se de que, na verdade, estes métodos criam uma nova cadeia.
Conversão de dados numéricos
O método estático valueOf da classe String permite obter uma cadeia de caráter que representa um dado tipo numérico. Um método estático está associado à classe, e não a uma instância particular da mesma. É fácil entender que o método em questão deve ser estático, pois quando resolvemos expressar um número na forma de caráteres, ainda não temos uma String. O método a ser chamado é que vai criá-la. Por exemplo:
double a = 3.1415927;
int i = 123;
System.out.println( "a = " + String.valueOf( a ) );
System.out.println( "i = " + String.valueOf( i ) );
Este código resulta na impressão de a = 3.1415927 seguido de i = 123 na saida padrão. Na verdade, o mesmo resultado pode ser obtido com uma sintaxe simplificada, pois numa concatenação, números são transformados automaticamente em caráteres. Assim, as dua últimas linhas acima podem ser substituidas por
System.out.println( "a = " + a );
System.out.println( "i = " + i );
Para realizar a operação inversa, ou seja transformar caráteres em tipos numéricos, é de se esperar que precisamos utilizar um método estático associado ao tipo numérico em questão. Porém, como um tipo primitivo em si não é um objeto, precisamos primeiro descobrir como associar uma classe a um tipo primitivo. As classes adequadas são chamadas de classes de embrulhamento (("wrapper classes") e podem ser encontradas no pacote java.lang. Por exemplo, a classe de embrulhamento de um tipo double é Double. Portanto, precisamos primeiro traduzir a nossa cadeia de caráteres num objeto Double. Para tanto, chamamos o método estático valueOf desta classe. Ainda precisaremos "desembrulhar" o nosso número, ou seja traduzir o nosso objeto Double num tipo primitivo double. Isto é feito chamando o método doubleValue da classe Double. O trecho de código abaixo ilustra este procedimento, assim como o semelhante para o caso de um tipo inteiro. (Outros tipos podem ser tratados também da mesma maneira.)
String atxt = "3.1415927";
String itxt = "123";
double a = Double.valueOf( atxt ).doubleValue();
int i = Integer.valueOf( itxt ).intValue();
Após execução, o valor da variável de precisão dupla a é 3.1415927 e o valor da variável inteira i é 123. Num exemplo mais realístico, os valores expressos na forma de caráteres poderiam ser lidos de um arquivo ou digitados pelo usuário.
No caso de inteiros, há uma maneira mais simples de realizar a operação acima: o método parseInt da classe Integer transforma diretamente uma cadeia de caráteres num tipo int. Portanto, o comando
int i = Integer.parseInt( itxt );
pode ser usado em vez da última linha do trecho de código anterior.
Formatação
A transformação de números em caráteres descrita na seção anterior resulta numa representação completa do número, sem arredondamento nem controle do formato pelo programador. Recursos para formatar texto, e em especial números, podem ser encontrados no pacote java.text. Como é de se esperar, formatos são objetos em Java. Como ilustração, o trecho de código abaixo resulta na impressão de a = 3.1416 na saida padrão:
double a = 3.1415927;
DecimaFormat meuFormato = new DecimalFormat( "0.####" );
System.out.println( "a = " + meuFormato.format( a ) );
O formato desejado é especificado pelo argumento do método construtor (veja os detalhes na documentação). O método format transforma o número (neste caso, de precisão dupla) numa cadeia da caráteres, respeitando este formato.
Decomposição em palavras
Se uma cadeia de caráteres representa uma sucessão de palavras chaves, pode ser necessário decompô-la em palavras individuais. Semelhantemente, se dados númericos foram armazenados num arquivo na forma de uma tabela com várias colunas e estamos lendo linha por linha, precisamos decompor cada linha em números individuais. A classe StringTokenizer do pacote java.util permite realizar este tipo de operação. Cria-se um objeto StringTokenizer associado à cadeia de caráteres a ser decomposta. O método countTokens conta o número de palavras (ou outros elementos) da cadeia. As palavras podem ser extraidas sucessivamente com o método nextToken. O método hasMoreTokens testa a presença de palavras ainda não separadas. Por exemplo:
String linhaDeDados = "0.225 4.32 6.17";
StringTokenizer separador = new StringTokenizer( linhaDeDados );
int i = separador.countTokens();
String n1 = separador.nextToken();
String n2 = separador.nextToken();
boolean b = separador.hasMoreTokens();
Após execução, o inteiro i vale 3; as cadeias de caráteres n1 e n2 contém "0.225" e "4.32", respectivamente. O booleano b possui o valor true, pois há ainda um número que não foi separado.
Listas
Construção
Listas ("Arrays") são utilizadas para agrupar variáveis do mesmo tipo. O tipo pode ser qualquer tipo primitivo ou qualquer classe de objetos. Para declarar que uma variável representa uma lista, acrescenta-se colchetes após o nome da variável. A lista é criada utilizando-se a palavra-chave new e indicando o número de elementos entre colchetes:
int c[];
c = new int[ 12 ];
double d[] = new double[ 30 ];
No caso de uma lista de tipos primitivos, a lista pode ser criada e inicializada através de uma lista de valores entre chaves:
int b[] = { 3, 5, 7, 9 };
O índice especificando um elemento de uma lista é contado a partir de 0. Por exemplo, o comando acima é equivalente ao seguinte:
int b[] = new int[ 4 ];
b[ 0 ] = 3;
b[ 1 ] = 5;
b[ 2 ] = 7;
b[ 3 ] = 9;
No caso de uma lista de objetos, deve-se notar que a criação da lista não cria os objetos. Cria simplesmente uma lista de referências que ainda não apontam para nada (null). Os objetos ainda precisam ser criados e as suas referências atribuidas aos elementos da lista. Por exemplo, o código abaixo cria 3 rótulos organizados numa lista:
Label llb[] = new Label[ 3 ];
llb[ 0 ] = new Label( "Vetor a" );
llb[ 1 ] = new Label( "Vetor b" );
llb[ 2 ] = new Label( "Produto" );
Comprimento
Qualquer lista possui uma variável inteira length que fornece o número de elementos da lista. Como exemplo de uso desta variável, podemos acrescentar ao trecho de código acima um laço que faz com que a cor de fundo dos três rótulos seja azul:
for( int i = 0; i < llb.length; i++){
llb[ i ].setBackground( Color.blue );
}
Referência e valor
Uma lista pode ser passada como argumento a um método. Deve-se notar que listas são objetos e, na linguagem Java, objetos são sempre passados aos métodos por referência, ao passo que tipos primitivos são sempre passados por valor. Isto quer dizer que uma modificação à lista realizada no método chamado implicará na mesma modificação à lista definida no programa que chamou o método em questão.
Indices múltiplos
Listas de múltiplos indices podem ser definidas, sendo consideradas como listas de listas. Os seguintes exemplos criam objetos que são essencialmente matrizes:
int b[][];
b = new int[ 2 ][ 3 ];
int c[][] = { { 1, 2, 3 }, { 4, 5, 6} };
Porém, listas de listas podem ser mais gerais de que matrizes, pois as "linhas" podem ter tamanhos diferentes:
int b[][];
b = int[ 2 ][ ];
b[ 0 ] = new int[ 2 ];
b[ 1 ] = new int[ 3 ];
int c[][] = { { 1, 2 }, { 3, 4, 5} };
Objetos e classes / Variáveis / Métodos
Classes
Uma classe é um modelo usado para definir vários objetos com características semelhantes. Um programa é constituído de uma classe ou de um conjunto de classes. Os elementos básicos de uma classe são chamados membros da classe e podem ser divididos em duas categorias:
1) As variáveis, que especificam o estado da classe ou de um objeto instância desta classe.
2) Os métodos, que especificam os mecanismos pelos quais a classe ou um objeto instância desta classe podem operar.
O esqueleto de uma classe apresenta-se da seguinte maneira:
class NomeDaClasse{
...
TipoDaVariahvel1 variahvel1;
TipoDaVariahvel2 variahvel2;
...
TipoDeRetorno1 mehtodo1(){
...
}
TipoDeRetorno2 mehtodo2(){
...
}
...
}
É claro que, além das variáveis mencionadas acima, que estão definidas fora de qualquer método, haverá em geral também variáveis definidas dentro de um determinado método e cujo escopo será limitado a este método, ou possivelmente a um sub-bloco deste método. Estas variáveis são chamadas locais. Em contradistinção, as variáveis das quais tratamos aqui são chamadas globais. Veja adiante nesta aula uma discussão um pouco mais completa desta distinção.
Para que uma instância de uma classe possa ser criada por qualquer outra classe, a classe em questão deve ser declarada pública, o que é feito acrescentando-se a palavra chave public à declaração da classe:
public class NomeDaClasse{
...
}
Classes que constituem aplicativos independentes e applets devem ser públicas. Uma classe que não for declarada pública somente poderá ser acessada por outras classes do mesmo pacote. Deve-se notar que cada arquivo fonte pode conter somente uma classe pública. Caso o arquivo contenha uma classe pública, ele deve possuir o mesmo nome que esta classe, com a terminação .java.
Objetos
Um objeto é uma instância de uma classe, ou seja uma realização concreta e particular da mesma. Um objeto precisa ser criado. Para que seja possível acessar as variáveis e os métodos de um objeto, é preciso atribuir uma referência ao objeto. O tipo de uma referência, ou seja a classe à qual pertence o objeto ao qual ela vai referir-se, precisa ser declarado.
Declaração: a seguinte instrução declara que a variável nomeDoObjeto refere-se a um objeto instância da classe NomeDaClasse:
NomeDaClasse nomeDoObjeto;
Criação: a seguinte instrução cria (em memória) um novo objeto instância da classe NomeDaClasse, que será referenciado pela variável nomeDoObjeto previamente declarada:
nomeDoObjeto = new NomeDaClasse();
As duas instruções acima podem ser combinadas numa só:
NomeDaClasse nomeDoObjeto = new NomeDaClasse();
Vale notar que a atribuição de uma referência a um objeto não é obrigatória. Há casos em qua basta criar o objeto e passá-lo como argumento de um método, com um comando do tipo
algumMehtodo( new AlgumaClasse() );
Variáveis
Variáveis de instância e variáveis de classe
Uma variável de instância é uma variável cujo valor é específico ao objeto e não à classe. Uma variável de instância em geral possui uma valor diferente em cada objeto representante da classe.
Uma variável de classe é uma variável cujo valor é comum a todos os objetos representantes da classe. Mudar o valor de uma variável de classe em um objeto automaticamente muda o valor para todos os objetos instâncias da mesma classe. Um exemplo óbvio de uma variável de classe seria o número de instâncias desta classe que já foram criadas.
Uma variável é considerada como de instância por "default". Para declarar uma variável de classe, acrescenta-se a palavra-chave static. Alias, outra expressão utilizada para indicar uma variável de classe é variável estática. Exemplo:
static int nuhmeroDeInstahnciasDestaClasse;
Restrições de acesso
Algumas palavras-chaves são diponíveis para ampliar ou restringir o acesso a uma variável. Estas palavras-chaves são acrescentadas à declaração da variável.
Uma variável que pode ser acessada por qualquer outra classe é dita pública, e é declarada usando-se a palavra-chave public.
Uma variável que pode ser acessada somente por métodos da própria classe é dita privada, e é declarada usando-se a palavra-chave private.
Uma variável que, além de poder ser acessada por métodos da própria classe, também pode ser acessada pelas subclasses da classe na qual ela é declarada, é dita protegida e é declarada usando-se a palavra-chave protected. [O conceito de subclasse será desenvolvido na próxima aula.]
Uma variável para a qual não foi especificada nenhuma destas palavras-chaves é dita amigável e pode ser acessada por todas as classes que pertencem ao mesmo pacote. Pacotes são agrupamentos de classes. Como já sabemos, as classes de biblioteca da SUN estão organizadas em pacotes. O programador também pode criar os seus próprios pacotes.
Os vários tipos de declaração de acesso estão exemplificados abaixo:
/* a classe definida neste arquivo pertence ao pacote chamado algumPacote */
package algumPacote;
public class NomeDaClasse{
/* a variável w é acessível por qualquer classe *
public int w;
/* a variável x é acessível pelos métodos da classe NomeDaClasse e das suas subclasses */
protected int x;
/* a variável y é acessível pelos métodos da classe NomeDaClasse e das outras classes que pertencem ao pacote chamado algumPacote */
int y;
/* a variável z é acessível somente pelos métodos da classe NomeDaClasse */
private int z;
...
}
As restrições de acesso desempenham um papel fundamental na programação orientada a objeto. Embora possa parecer mais simples e simpático permitir a qualquer objeto o acesso a todas as variáveis de qualquer outro objeto, isto resultaria em código muito pouco robusto. Recomenda-se, pelo contrário, limitar o acesso a qualquer variável o quanto for possível, dada a função da variável em questão. Mesmo no caso de variáveis que precisam ser acessíveis a todos os objetos, há uma alternativa preferível ao acesso irrestrito outorgado pela palavra chave public, como veremos na próxima seção.
Como um objeto acessa as variáveis de outro objeto
Supondo que as restrições de acesso discutidas acima o permitam, uma classe pode utilizar ou modificar uma variável pertencente a um objeto através do operador ponto.
Por exemplo, após declarar e criar o objeto nomeDoObjeto, representante da classe NomeDaclasse, podemos ir buscar a variável variahvel deste objeto e atribui-la à variável var da classe que estamos desenvolvendo:
TipoDaVariahvel var;
var = nomeDoObjeto.variahvel;
Evidentemente, var e variahvel devem ser do mesmo tipo (chamado TipoDaVariahvel no exemplo acima).
Também podemos atribuir à variável variahvel do objeto nomeDoObjeto o valor de uma variável var da nossa classe [entende-se aqui "valor" no sentido geral, podendo ser uma referência a um objeto]:
TipoDaVariahvel var = algumValor;
nomeDoObjeto.variahvel = var;
Se variahvel for uma referência a um outro objeto, pode-se concatenar operadores pontos para alcançar variáveis deste objeto:
TipoDaVariahvel var;
var = nomeDoObjeto.variahvel.outraVariahvel;
Neste caso, é claro, var e outraVariahvel devem ser do mesmo tipo.
Se a variável a ser acessada for uma variável de classe, pode-se usar a classe, em vez de uma instância particular, para acessar a variável:
TipoDaVariahvel var;
var = NomeDaClasse.variahvel;
Embora esta seja a maneira a mais direta de permitir que um objeto tenha acesso às variáveis de outro objeto, não é a mais segura, pois permite que um objeto modifique uma variável do outro sem restrição. Uma maneira mais segura consiste em declarar a variável como privada, mas incluir na classe métodos especiais controlando o acesso à variável:
public class NomeDaClasse{
private TipoDaVariahvel variahvel;
...
public TipoDaVariahvel getVariahvel(){
return variahvel;
}
public void setVariahvel( TipoDaVariahvel valor ){
/* aqui pode-se colocar testes para determinar se o valor é aceitável */
...
variahvel = valor;
}
}
Assim, pode-se colocar no método setVariahvel código para conferir que o valor passado como argumento é adequado. Omitindo o método setVariahvel, impedimos qua a variável seja modificada por qualquer outro objeto, embora ela possa ser lida chamando o método getVariahvel.
O operador ponto também serve para chamar os métodos de outro objeto (veja adiante nesta aula), de maneira que a classe que deseja manipular as variáveis do outro objeto conterá agora comandos do tipo:
TipoDaVariahvel var, varp;
var = nomeDoObjeto.getVariahvel();
...
nomeDoObjeto.setVariahvel( varp );
Esta técnica de programação é chamada encapsulação de variáveis.
Variáveis globais e variáveis locais
As variáveis discutidas acima são declaradas fora de qualquer método (usualmente no cabeçalho da classe) e são acessíveis por qualquer método da classe. Tais variáveis são chamadas globais. Muitas vezes, variáveis auxiliares são declaradas dentro de um determinado método, ou até dentro de um bloco menor. Tais variáveis são chamadas locais. Elas existem somente durante a execução daquele método ou bloco. A parte de código que "enxerga" uma determinada variável é chamada o escopo da variável. Assim, o escopo de uma variável global é a classe inteira, e o escopo de uma variável local é o método, ou um bloco contido dentro do método, ao qual ela pertence.
Exemplo:
class NomeDaClasse{
/* a variável abaixo é global */
TipoDaVariahvel variahvel1;
...
TipoDeRetorno nomeDoMehtodo()
{
/* a variável abaixo é local; está definida somente dentro deste método */
TipoDaVariahvel variahvel2;
for( int i = 0; i < 10; i++ ){
/* a variável i é local, definida só dentro deste bloco */
... //
}
}
}
Embora não pareça uma boa ideia, é possível dar a uma variável local um nome que já foi atribuído a uma variável global. Neste caso, a variável local "encobre" a variável global, mas o acesso à variável global é possível usando a palavra-chave this (que fornece uma referência ao próprio objeto) e o operador ponto:
class NomeDaClasse{
TipoDaVariahvel variahvel; // variável global
...
TipoDeRetorno nomeDoMehtodo()
{
TipoDaVariahvel variahvel; // variável local
...
/* a variável abaixo recebe o valor de variahvel local */
variahvel2 = variahvel;
/* a variável abaixo recebe o valor de variahvel global */
variahvel3 = this.variahvel;
}
}
Alguns programadores fazem uso desta construção em métodos set:
public class NomeDaClasse{
private TipoDaVariahvel variahvel;
public void setVariahvel( TipoDaVariahvel variahvel )
{
this.variahvel = variahvel;
}
}
Variáveis constantes
Esta expressão um tanto paradoxal refere-se a variáveis cujo valor não pode mudar durante a execução do programa. Tais variáveis são caracterizadas pela palavra-chave final, e por convenção recebem usualmente nomes escritos inteiramente em maiúsculas.
Por exemplo, na classe Math do pacote lang, encontramos
public static final double PI;
que contem o valor da famosa constante matemática.
Métodos
Valor de retorno e argumentos
Em geral, um método recebe argumentos cujos valores lhe são passados pelo objeto que o chamou, efetua um conjunto de operações e retorna algum resultado. A declaração do método especifica o nome do método, o tipo de retorno, o nome e o tipo de cada argumento. Os argumentos são variáveis locais do método em questão. O valor de retorno é devolvido utilizando-se a palavra chave return:
TipDeRetorno nomeDoMehtodo( TipoDoArg1 arg1, TipoDoArg2 arg2, ...){
TipoDeRetorno valorDeRetorno;
...
return valorDeRetorno;
}
Se o método não utiliza nenhum argumento, parénteses vazias devem ser incluídas na declaração.
Se o método não retorna nenhum valor, isto deve ser declarado usando-se a palavra-chave void:
void nomeDoMehtodo( TipoDoArg1 arg1, TipoDoArg2 arg2, ... ){
...
}
Métodos estáticos
Por "default", um método efetua uma determinada operação sobre um determinado objeto, ou seja uma instância da classe na qual o método está declarado. Existem métodos que realizam operações genéricas, não relativas a uma instância particular. Tais métodos são chamados estáticos e são declarados acrescentando-se a palavra-chave static à declaração do método.
Por exemplo, os métodos da classe Math do pacote lang, que realizam operações matemáticas sobre números, são estáticos:
public static int min( int a, int b ){
... // retorna o menor dos 2 inteiros a e b
}
Restrições de acesso
Algumas palavras-chaves são diponíveis para ampliar ou restringir o acesso a um método. Estas palavras-chaves são acrescentadas à declaração do método.
Um método que pode ser acessado por qualquer outra classe é dito público, e é declarado usando-se a palavra-chave public.
Um método que pode ser acessado somente por métodos da própria classe é dito privado, e é declarado usando-se a palavra-chave private.
Um método que, além de poder ser acessado por todas as classes do mesmo pacote, também pode ser acessado pelas subclasses da classe na qual ele é declarado, é dito protegido e é declarado usando-se a palavra-chave protected.
Um método para o qual não foi especificada nenhuma destas palavras-chaves é dito amigável e pode ser chamado por todas as classes que pertencem ao mesmo pacote.
Exemplo:
package algumPacote;
public class NomeDaClasse{
public int mehtodo1( ){
... // acessível por qualquer classe
}
protected int mehtodo2( ){
... // acessível por esta classe e suas subclasses
}
int mehtodo3( ){
... // acessível pelas classes deste pacote
}
private int mehtodo4( ){
... // acessível por esta classe
}
}
Como um objeto chama um método de outro objeto
Supondo que as restrições de acesso discutidas acima o permitam, uma classe que possui uma referência a um objeto pode chamar (executar) um método pertencente este objeto através do operador ponto":
/* o método abaixo não requer argumento e não retorna nada */
nomeDoObjeto.mehtodo1( );
/* o método abaixo requer dois argumentos e não retorna nada */
nomeDoObjeto.mehtodo2( var1, var2 );
/* o método abaixo não requer argumento e retorna um valor do tipo Resultado */
Resultado resultado = objeto.mehtodo3( );
Se a variável de retorno for um objeto, pode-se concatenar operadores pontos para chamar métodos deste objeto:
nomeDoObjeto.mehtodo3().mehtodo4();
Se o método for estático, usa-se a classe, em vez de uma instância particular, para chamar o método:
int x = Math.min( a, b );
Como criar um objeto - Método construtor
Para criar uma objeto, usa-se a palavra chave new, seguida de uma chamada ao método construtor, cujo nome é idêntico ao da classe:
NomeDaClasse nomeDoObjeto = new NomeDaClasse( );
O método construtor não precisa ser incluído explicitamente na classe, mas pode ser incluído para realizar tarefas no ato da criação. A sintaxe é:
class NomeDaClasse{
NomeDaClasse( ){
... // comandos executados na criação do objeto
}
}
ou, para uma classe pública:
public class NomeDaClasse{
public NomeDaClasse( ){
... // comandos executados na criação do objeto
}
}
O método construtor pode receber argumentos:
public class NomeDaClasse{
public NomeDaClasse( Tipo1 arg1, Tipo2 arg2 ){
... // comandos executados na criação do objeto
}
}
Valores para estes argumentos devem então ser passados ao construtor no comando de criação:
NomeDaClasse nomeDoObjeto = new NomeDaClasse( valorArg1, valorArg2 );
Programação orientada a objetos:
Aspectos fundamentais
Recursos adicionais
Aspectos fundamentais
Sobrecarga de métodos
É permitido incluir numa classe métodos que possuem o mesmo nome e o mesmo tipo de retorno, mas que diferem pelo número e/ou pelos tipos dos argumentos.
Qual destes métodos é executado depende do número e/ou dos tipos dos argumentos passados pelo programa que chama o método. Esta técnica é chamada sobrecarga ("overloading") de métodos. Ela é frequentemente utilizada, por exemplo para definir vários construtores para uma determinada classe.
Exemplo:
public class Ponto{
protected double x, y;
public Ponto(){
setPonto( 0, 0 );
}
public Ponto( double a, double b ){
setPonto( a, b );
}
public void setPonto( double a, double b ){
x = a;
y = b;
}
public String emPalavras(){
return "[" + x + ", " + y + "]";
}
}
Herança
Pode-se criar uma nova classe, acrescentando recursos a uma classe já construída, ou modificando alguns recursos desta classe. A nova classe herda (possivelmente com modificações) os recursos já disponíveis na classe anterior.
A classe herdeira é chamada subclasse da classe anterior, que é a sua superclasse.
Este processo de herança pode ser repetido em cascata, criando várias gerações de classes.
Vale notar que a linguagem Java permite somente herança simples, i.e. uma classe pode herdar diretamente de uma única classe (possui uma única superclasse direta). Em contrapartida, uma classe pode possuir várias subclasses diretas.
A relação de herança é indicada através da palavra-chave extends. Por "default", uma classe herda da classe Object.
Exemplo: O exemplo abaixo cria uma subclasse da classe definida no exemplo anterior:
public class Circulo extends Ponto{
protected double r;
public Circulo(){
/* aqui ocorre uma chamada implicita ao construtor da superclasse */
setRaio( 0 );
}
public Circulo( double a, double b, double r ){
super( a, b );
setRaio( r );
}
public void setRaio( double r ){
this.r = r;
}
}
No exemplo acima, as variáveis x e y da superclasse são acessíveis à subclasse pois foram declaradas protected.
A palavra-chave super permite referência à superclasse, e em especial, se for utilizada como nome de método, ao construtor da superclasse.
Nota-se que se não houver, no construtor da subclasse, nenhuma chamada explicita ao construtor da superclasse, o construtor sem argumento é chamado por "default". Se for incluída uma chamada ao construtor da superclasse, ela deve ser o primeiro comando executado no construtor da subclasse.
Superação de métodos
Na construção de uma subclasse a partir de uma superclasse, pode-se também substituir um método já presente na superclasse por outro, de mesmo nome, mesmo tipo de retorno, igual número e mesmos tipos de argumentos. Este procedimento é chamado "overriding" em inglés, o que será traduzido aqui por superação.
Exemplo: na classe Circulo acima, podemos substituir o método emPalavras por uma versão incrementada cujo valor de retorno inclui também o raio do círculo:
public class Circulo extends Ponto{
protected double r;
public Circulo(){
setRaio( 0 );
}
public Circulo( double a, double b, double r ){
super( a, b );
setRaio( r );
}
public void setRaio( double r ){
this.r = r;
}
public String emPalavras(){
return "[" + x + ", " + y + ", " + r + "]";
}
}
Vale notar que o método superado ainda pode ser utilizado, caso for necessário, fazendo-se uso da palavra-chave super. Por exemplo, uma possível alternativa à construção acima (produzindo porém um resultado ligeiramente diferente) seria:
public class Circulo extends Ponto{
protected double r;
public Circulo(){
setRaio( 0 );
}
public Circulo( double a, double b, double r ){
super( a, b );
setRaio( r );
}
public void setRaio( double r ){
this.r = r;
}
public String emPalavras(){
return super.emPalavras() + "[" + r + "]";
}
}
Polimorfismo
Já que uma subclasse herda as propriedades da superclasse, uma instância de uma subclasse é também uma instância da sua superclasse (e da superclasse desta, etc... até a classe raiz Object).
Assim, no exemplo acima, um Circulo também é um Ponto e uma instância de Circulo pode ser atribuída a uma referência declarada como Ponto.
Exemplo:
import java.awt.Label;
import java.applet.Applet;
public class Test extends Applet{
private Ponto meusPontos[];
private Label rohtulos[];
public void init(){
meusPontos = new Ponto[ 2 ];
meusPontos[ 0 ] = new Ponto( 3, 5 );
meusPontos[ 1 ] = new Circulo( 15, 5, 1.5 );
rohtulos = new Label[ 2 ];
for( int i = 0; i < meusPontos.length; i++ )
{
rohtulos[ i ] = new Label( meusPontos[ i ].emPalavras() );
add( rohtulos[ i ] );
}
}
}
Neste exemplo, apesar de ambos objetos serem declarados como Ponto, o segundo é de fato uma instância da subclasse Circulo e portanto, quando for chamado o método emPalavras deste objeto, será executado o método da classe Circulo (que retorna informação também sobre o raio). Este recurso da linguagem é chamado polimorfismo, pois ele faz com que objetos declarados como instâncias de uma classe (no caso, a classe Ponto) possam assumir diversas formas (no caso, um deles é um mero Ponto e o outro é um Circulo, que é de verdade um ponto incrementado).
Se uma instância de uma subclasse for atribuída a uma referência do tipo da superclasse, somente os métodos que já estão definidos na superclasse podem ser chamados como descrito acima. Para chamar um método definido na subclasse, mas não na superclasse, é necessário efetuar uma coerção explicita. Por exemplo, se acrescentarmos à classe Circulo um método para calcular a área, ou seja,
public class Circulo extends Ponto{
protected double r;
public Circulo(){
setRaio( 0 );
}
public Circulo( double a, double b, double r ){
super( a, b );
setRaio( r );
}
public void setRaio( double r ){
this.r = r;
}
public double ahrea( ){
return Math.PI * r * r;
}
public String emPalavras(){
return "[" + x + ", " + y + ", " + r + "]";
}
}
devemos usar a coerção para chamar este método numa instância de Circulo que foi atribuída a uma referência de tipo Ponto:
import java.awt.Label;
import java.applet.Applet;
public class Test extends Applet{
private Ponto meusPontos[];
private Label rohtulos[];
public void init(){
meusPontos = new Ponto[ 2 ];
meusPontos[ 0 ] = new Ponto( 3, 5 );
meusPontos[ 1 ] = new Circulo( 15, 5, 1.5 );
rohtulos = new Label[ 2 ];
for( int i = 0; i < meusPontos.length; i++ ){
rohtulos[ i ] = new Label( meusPontos[ i ].emPalavras() );
add( rohtulos[ i ] );
}
double a = ( ( Circulo ) meusPontos[ 1 ] ).ahrea();
rohtulos[ 1 ].setText( rohtulos[ 1 ].getText() + "[" + a + "]" );
}
}
Em linguagens orientadas a objeto, a palavra polimorfismo é também usada num sentido um tanto diferente, qual seja para referir-se às várias "formas" que pode tomar uma classe que herda de várias outras. Embora Java não permite herança múltipla, ele oferece um recurso alternativo, chamado interface, que possibilita este tipo de polimorfismo (veja abaixo).
Recursos adicionais
Interfaces
Como já mencionado, Java não permite que uma classe seja herdeira dos recursos de mais de uma outra classe. Esta limitação é imposta para evitar que o programador possa cometer erros sutís e perigosos, caso ele quiser construir uma classe que herde de duas outras que possuem um método de mesmo nome (e tipos de argumentos), mas com implementações diferentes nas duas classes. Neste caso, haveria ambiguidade no funcionamento do método herdado. Do outro lado, a herança múltipla é um recurso poderoso, cujo total abandono limitaria bastante a linguagem.
A saida encontrada é o conceito de interface, que nada mais é que um conjunto de declarações de métodos (nome, tipo de retorno, tipos dos argumentos) desprovidos de implementação. Cabe ao programador que deseja implementar a interface em questão providenciar uma implementação destes métodos na classe que ele está desenvolvendo.
Desta forma, além de estender alguma superclasse, a classe em desenvolvimento pode implementar várias interfaces.
A palavra chave implements é utilizada para indicar que uma classe implementa uma determinada interface.
Exemplo: definimos a interface:
interface Forma{
public double ahrea();
public String emPalavras();
}
Podemos então declarar que a classe Circulo construida acima implementa esta interface:
public class Circulo extends Ponto implements Forma{
protected double r;
public Circulo(){
setRaio( 0 );
}
public Circulo( double a, double b, double r ){
super( a, b );
setRaio( r );
}
public void setRaio( double r ){
this.r = r;
}
public double ahrea( ){
return Math.PI * r * r;
}
public String emPalavras(){
return "[" + x + ", " + y + ", " + r + "]";
}
}
Poderiamos definir uma outra classe que implementa a mesma interface:
public class Quadrado extends Ponto implements Forma{
protected double l;
public Quadrado(){
setLado( 0 );
}
public Quadrado( double a, double b, double l ){
super( a, b );
setLado( l );
}
public void setLado( l ){
this.l = l;
}
public double ahrea( ){
return l * l;
}
public String emPalavras(){
return "[" + x + ", " + y + ", " + l + "]";
}
}
No exemplo acima, um objeto de tipo Circulo é também uma instância de uma Forma. O mesmo é verdade de um objeto de tipo Quadrado. O uso deste polimorfismo está ilustardo no exemplo abaixo:
import java.awt.Label;
import java.applet.Applet;
public class Test extends Applet{
private Forma minhasFormas[];
private Label rohtulos[];
public void init(){
minhasFormas = new Forma[ 2 ];
minhasFormas[ 0 ] = new Circulo( 3, 5, 1.5 );
minhasFormas[ 1 ] = new Quadrado( 15, 5, 2.5 );
rohtulos = new Label[ 2 ];
for( int i = 0; i < minhasFormas.length; i++ ){
String descr = minhasFormas[ i ].emPalavras();
double ar = minhasFormas[ i ].ahrea();
rohtulos[ i ] = new Label( descr + "; área: " + ar );
add( rohtulos[ i ] );
}
}
}
Classes abstratas
A linguagem Java ainda oferece um meio termo entre uma classe na qual está providenciada uma implementação para todos os métodos declarados, e uma interface na qual nenhum dos métodos declarados está implementado. Trata-se da classe abstrata, na qual alguns dos métodos declarados estão implementados e outros não.
Uma classe abstrata deve ser declarada tal usando-se a palavra chave abstract.
Uma classe abstrata não pode ser instânciada diretamente. Pode-se construir subclasses da classe abstrata, nas quais os métodos declarados mas ainda não implementados devem receber uma implementação.
Exemplo: uma alternativa ao esquema do exemplo anterior seria definir uma classe abstrata Forma:
public abstract class Forma extends Ponto{
abstract double ahrea( );
}
Podemos então definir as classes Circulo e Quadrado como subclasses concretas desta classe abstrata:
public class Circulo extends Forma{
protected double r;
public Circulo(){
setRaio( 0 );
}
public Circulo( double a, double b, double r ){
super( a, b );
setRaio( r );
}
public void setRaio( double r ){
this.r = r;
}
public double ahrea( ){
return Math.PI * r * r;
}
public String emPalavras(){
return "[" + x + ", " + y + ", " + r + "]";
}
}
public class Quadrado extends Forma{
protected double l;
public Quadrado(){
setLado( 0 );
}
public Quadrado( double a, double b, double l ){
super( a, b );
setLado( l );
}
public void setLado( l ){
this.l = l;
}
public double ahrea( ){
return l * l;
}
public String emPalavras(){
return "[" + x + ", " + y + ", " + l + "]";
}
}
Aula 6
Componentes gráficas
Passeio pela documentação sobre componentes gráficas
Tratamento de eventos
Gerenciadores de disposição
Passeio pela documentação
A seguir listamos as componentes gráficas mais usuais encontradas no pacote java.awt. Limitamo-nos alguns poucos comentários, em especial a respeito dos eventos gerados por estas componentes, cujo tratamento será detalhado na seção seguinte.
Component
Uma classe abstrata, superclasse de todas as componentes gráficas.
Container
Uma classe abstrata, definindo propriedades gerais de componentes que podem conter outras componentes.
Panel
O tipo o mais simples de Container. Vale notar que a classe Applet é subclasse de Panel.
Canvas
Uma classe utilizada como fundo para pintar. O método paint deve ser sobre-escrito para realizar a pintura desejada.
Label
Um rôtulo, i.e. uma linha de texto que fornece informação ao usuário. O texto pode ser modificado pelo programa mas não pelo usuário diretamente.
Button
Um botão simples. A ação normalmente realizada pelo usuário é clicar, o que gera um evento do tipo ActionEvent. O objeto encarregado de tratar este evento (i.e. fazer o que o programador deseja que seja feito caso o usuário clicar o botão) deve implementar a interface ActionListener. O método addActionListener do botão deve ser chamado para registrar este objeto como "ouvidor de eventos" do botão. A referência ao ouvidor em questão é passada com argumento do método.
TextComponent
Esta é a superclasse das classes destinadas a tratar texto (veja o dois itens seguintes). Se o texto for editável, a digitação ou alteração do mesmo gera eventos do tipo TextEvent. O objeto encarregado de tratar estes eventos deve implementar a interface TextListener. O método addTextListener da componente de texto deve ser chamado para registrar este objeto como "ouvidor de eventos" da componente. A referência ao ouvidor em questão é passada com argumento do método.
TextField
Subclasse de TextComponent definindo um campo de texto, i.e. uma única linha de texto editável. Além dos eventos do tipo TextEvent discutidos acima, um campo de texto pode também gerar eventos do tipo ActionEvent, por exemplo quando aperta-se a tecla Enter. Tais eventos são tratados seguindo o modelo descrito para o botão.
TextArea
Subclasse de TextComponent definindo uma área de texto, i.e. mais de uma linha de texto editável.
Choice
Uma componente permitindo a escolha entre vários itens de uma lista. Um clique do mouse em cima de um pequeno botão faz aparecer a lista. Um item da lista pode ser selecionado com o mouse. Isto gera um evento do tipo ItemEvent. O objeto encarregado de tratar estes eventos deve implementar a interface ItemListener. A classe Choice implementa a interface ItemSelectable, que define alguns métodos relacionado com escolha, em especial o método addItemListener, que deve ser chamado para registrar o "ouvidor de eventos de escolha de item" da componente Choice. A referência ao ouvidor em questão é passada como argumento do método.
Checkbox
Este tipo de botão permite uma escolha do tipo sim/não e vem acompanhado de alguma marca indicando a opção. Vários botões deste tipo podem ser agrupados num objeto CheckboxGroup. Neste caso, somente um dos botões pode estar selecionado (opção sim), os outros estando então automaticamente deselecionados (opção não). Botões assim agrupos são usualmente chamados botões de rádio. A classe Checkbox implementa a interface ItemSelectable e os seus eventos, do tipo ItemEvent, são tratados como descrito no item anterior.
List
Uma lista de itens entre os quais um, ou vários, podem ser selecionados. Uma barra de rolagem aparece automaticamente se o número de itens for maior que o número de linhas visíveis. Esta classe também implementa a interface ItemSelectable. Um evento do tipo ItemEvent é gerado por um clique simples do mouse. Um evento do tipo ActionEvent é gerado por um clique duplo do mouse. Assim, esta componente pode utilizar dois ouvidores de eventos, um ItemListener e um ActionListener. Evidentemente, estas duas funcionalidades podem ser incorporadas numa única classe que implementa ambos interfaces.
Scrollbar
Uma barra de rolagem que permite "rolar" um cursor sobre um dado intervalo de valores inteiros. A rolagem é realizada arrastando o cursor com o mouse ou clicando. Isto gera eventos do tipo AdjustmentEvent. O objeto encarregado de tratar estes eventos deve implementar a interface AjustmentListener. A classe Scrollbar implementa a interface Adjustable, que define alguns métodos relacionado com rolagem, em especial o método addAdjustmentListener, que deve ser chamado para registrar o "ouvidor de eventos de rolagem" da componente Scrollbar. A referência ao ouvidor em questão é passada como argumento do método.
ScrollPane
Esta classe herda de Container pode conter uma única componente, que pode ser rolada horizontalmente e verticalmente. As barras de rolagem são partes intregrantes da componente.
Frame
Um janela com uma barra de título e uma borda. Esta classe herda da classe Window, que representa o tipo mais rudimentar de janela e herda por sua vez da classe Container. A classe Frame implementa a interface MenuContainer, que permite que ela receba uma barra de menu. Os eventos associados à manipulação de uma janela (abertura, fechamento, iconização, redimensionamento...) são do tipo WindowEvent. O objeto encarregado de tratar estes eventos deve implementar a interface WindowListener. O método addWindowListener, da superclasse Window, deve ser chamado para registrar o "ouvidor de eventos de janela" da componente Frame. A referência ao ouvidor em questão é passada como argumento do método.
Dialog
Uma caixa de diálogo é uma janela com uma barra de título, usualmente utilizada para permitir a entrada de dados ou para fornecer informação ao usuário. Uma caixa de diálogo pode ser modal ou não. Se ela for modal, nenhuma outra janela pode ser acessada enquanto a caixa de diálogo estiver aberta.
MenuComponent
As componentes associadas a menus não herdam da classe Component e sim da classe MenuComponent, uma classe abstrata. As subclasses diretas de MenuComponent são MenuBar e MenuItem.
MenuBar
Uma barra de menu, que pode ser acrescentada a um Frame, passando-a como argumento do método setMenuBar deste frame. Menus podem ser acrescentados à barra chamando-se o método add da mesma.
Menu
Um menu, que pode ser acrescentado a uma barra de menu ou, tratando-se de um submenu, a outro menu. Já que um objeto deste tipo pode ser acrescentado a um menu, a classe Menu herda da classe MenuItem descrita abaixo. Um item pode ser acrescentado a um menu passando-o como argumento do método add do mesmo.
MenuItem
Uma item de menu, que pode ser acrescentada a um Menu, passando-a como argumento do método add deste menu. Um clique em cima de um item de menu gera um evento do tipo ActionEvent, cujo tratamento já foi descrito acima.
CheckboxMenuItem
Subclasse de MenuItem que fornece um item de menu que permite uma escolha do tipo sim/não e mostra uma marca especial se a selação for sim. Um clique em cima de um item de menu deste tipo gera um evento do tipo ItemEvent, cujo tratamento já foi descrito acima.
PopupMenu
Subclasse de Menu que fornece um menu contextual, i.e. um menu que aparece quando o botão direito do mouse é clicado. O clique produz ume evento do tipo MouseEvent. o objeto encarregado de tratar este evento deve implementar a interface MouseListener. Deve comandar a aparição da caixa de diálogo chamando o método show da mesma.
Tratamento de eventos
O tratamento de eventos tipicamente segue o padrão seguinte:
O objeto responsável pelo tratamento dos eventos ("ouvidor") deve implementar uma determinada interface característica do tipo de eventos a serem tratados. Lembramos que uma interface é uma prescrição de um conjunto de métodos (de nome, acesso, valor de retorno e tipos de argumentos definidos) que devem ser todos implementados pela classe que implementa a interface. Métodos que não vão ser utilizados devem receber uma implementação trivial (não fazer nada). Os tipos de eventos e as interfaces de tratamento de eventos são definidos no pacote java.awt.event. Vale notar que existem classes de biblioteca, também definidas neste pacote, que fornecem implementações triviais destas interfaces. Estas classes são chamadas adaptadores (Adapter). Uma alternativa à implementação da interface é a criação de uma subclasse do adaptador na qual os métodos que vão ser utilizados são sobre-escritos. Lembramos porém que uma classe só poder herdar de uma única classe, embora possa implementar várias interfaces.
O objeto responsável pelo tratamento dos eventos deve ser registrado como "ouvidor" junto à componente que vai receber os eventos.
O tipos de eventos e interfaces de tratamento correspondentes, eoncontrados nas componentes listadas acima são:
ActionEvent - ActionListener
public void actionPerformed( ActionEvent e )
{
// ação a ser realizada
}
TextEvent - TextListener
public void textValueChanged( TextEvent e )
{
// ação a ser realizada se o texto foi mudado
}
ItemEvent - ItemListener
public void itemStateChanged( ItemEvent e )
{
// ação a ser realizada se um novo item foi selecionado
}
AdjustmentEvent - AdjustmentListener
public void adjustmentValueChanged( AdjustmentEvent e )
{
// ação a ser realizada se houve rolagem
}
WindowEvent - WindowListener
public void windowActivated( WindowEvent e )
{
// ação a ser realizada se a janela foi ativada
}
public void windowClosed( WindowEvent e )
{
// ação a ser realizada se a janela foi fechada
}
public void windowClosing( WindowEvent e )
{
// ação a ser realizada se a janela está sendo fechada
}
public void windowDeactivated( WindowEvent e )
{
// ação a ser realizada se a janela foi desativada
}
public void windowDeiconified( WindowEvent e )
{
// ação a ser realizada se a janela foi de-iconificada
}
public void windowIconified( WindowEvent e )
{
// ação a ser realizada se a janela foi iconificada
}
public void windowOpened( WindowEvent e )
{
// ação a ser realizada se a janela foi aberta
}
MouseEvent - MouseListener
public void mouseClicked( MouseEvent e )
{
// ação a ser realizada se o mouse foi clicado
}
public void mousePressed( MouseEvent e )
{
// ação a ser realizada se o botão do mouse foi apertado
}
public void mouseReleased( MouseEvent e )
{
// ação a ser realizada se o botão do mouse foi solto
}
public void mouseEntered( MouseEvent e )
{
// ação a ser realizada se o mouse entrou em cima da componente
}
public void mouseExited( MouseEvent e )
{
// ação a ser realizada se o mouse sai de cima da componente
}
Movimentos do mouse também ser tratados por
MouseEvent - MouseMotionListener
public void mouseDragged( MouseEvent e )
{
// ação a ser realizada se o mouse foi arrastado (com botão apertado)
}
public void mouseMoved( MouseEvent e )
{
// ação a ser realizada se o mouse foi movido (com botão solto)
}
Gerenciadores de disposição
O pacote java.awt fornece classes destinadas a organizar as componentes gráficas sobre a tela. Estas classes implementam a interface LayoutManager (ou a interface LayoutManager2), que será traduzido aqui em "gerenciador de disposição". Um gerenciador de disposição é atribuido a uma instância de um classe que pode conter componentes (i.e. uma subclasse da classe Container), chamando-se o método setLayout deste Container, com a referência ao LayoutManager desejado passada como argumento.
Os gerenciadores de disposição mais comuns são listados abaixo.
FlowLayout
Este é o tipo mais básico de gerenciador de disposição. As componentes são colocadas sucessivamente da esquerda para a direita sobre a mesma linha até que não haja mais espaço suficiente. Passa-se então a linha seguinte. É possível especificar o alinhamento das componentes numa linha como centrado, a esquerda ou a direita. É possível também especificar um intervalo horizontal e vertical entre as componentes. Este é o gerenciador de disposição "default" das classes Panel e Applet.
GridLayout
Este gerenciador de disposição divide o Container numa grade que organiza as componentes em linhas e colunas. As componentes são adicionadas linha por linha, da esquerda para a direita. É possível especificar uma intervalo horizontal e vertical entre as componentes.
BorderLayout
Este gerenciador de disposição divide o Container em cinco regiões: central, norte, sul, leste e oeste. As regiões norte e sul extendem-se sobre toda a largura do Container e possuem a altura das componentes que elas contem. As regiões leste e oeste extendem-se em altura sobre toda a região entre as regiões norte e sul. Elas possuem a largura das componentes que elas contém. A região central ocupa o espaço que sobra. É possível definir um intervalo horizontal e vertical entre as componentes. Este é o gerenciador de disposição "default" das classes Window e Frame.
CardLayout
Este gerenciador de disposição arranja as componentes na forma de um baralho, de maneira que uma só das componentes está visível num determinado momento, as outras estando escondidas. Evidentemente, é necessário incluir um dispositivo (por exemplo, um ou vários botões) que permita mudar a componente visível. O ouvidor de eventos deste dispositivo chamará métodos do objeto CardLayout para efetuar esta operação. Os métodos first e last selecionam respectivamente a primeira e a última componente do baralho. Os métodos next e previous selecionam respectivamente a componente seguinte e a componente anterior. A referência ao Container deve ser passada como argumento a estes métodos. Quando uma componente é acrescentada ao Container, pode-se passar ao método add deste, além da referência à componente, um nome para esta (na forma de uma cadeia de caráteres). Isto possibilita ao ouvidor de eventos ir buscar uma componente qualquer dentro do baralho, através de uma chamada ao método show do CardLayout, método este que recebe como argumentos a referência ao Container e o nome da componente.
Aula 7
Animação
Linhas de execução
Animação num applet - Interface "Runnable"
Eliminação da tremulação - Bufferização dupla
Linhas de execução
Para controlar o fluxo de execução de um programa, utiliza-se uma (ou várias) linha de execução que, em Java, é representada por uma instância da classe Thread, encontrada no pacote java.lang. Os métodos desta classe, de maior relevância para o controle de uma animação simples, são:
public void run()
Especifica as operações a serem realizadas pela linha de execução. Este método deve ser sobre-escrito pelo programador, codificando as operações em questão.
start()
Deslancha a execução da linha de execução; não pode ser sobre-escrito pelo programador.
suspend()
Suspende a execução da linha de execução; não pode ser sobre-escrito pelo programador.
resume()
Retoma a execução de uma linha de execução que foi suspensa; não pode ser sobre-escrito pelo programador.
stop()
Encerra a execução da linha de execução; não pode ser sobre-escrito pelo programador.
sleep( long tempo )
Este é um método estático, ou seja cuja chamada afeta todas as instâncias da classe. O resultado da chamada é que todas as linhas de execução "dormem" durante o intervalo especificado pelo argumento, em milisegundos. Este método pode "lançar" uma exceção do tipo InterruptedException que deve ser capturada ou relançada pelo objeto que efetua a chamada. O lançamento e a captura de exceções é assunto para uma próxima aula, mas o código necessário no caso discutido aqui pode ser encontrado no exemplo abaixo.
Outros métodos, de interesse para o desenvolvimento de programas contendo várias linhas de execução, serão discutidos numa outra aula.
Animação num applet - Interface "Runnable"
Como visto acima, o método mais geral de criar uma linha de execução consiste em construir uma subclasse da classe Thread, na qual o método run é sobre-escrito para realizar as operações desejadas. Porém, como em Java uma classe só pode herdar de uma única superclasse, a classe assim construída não poderia simultaneamente ser um applet ou outro tipo de componente gráfica. Esta complicação pode ser contornada com o uso da interface Runnable, que é definida como contendo um único método, exatamente o método public void run(). Pode-se então criar uma instância da classe Thread, passando como argumento do construtor uma referência ao objeto Runnable que implementa a interface. Este objeto passa então a atuar como "alvo" da linha de execução, o que significa que o método run() associado à linha de execução é na verdade aquele implementado no objeto Runnable. Por exemplo, no caso de um applet:
public class MeuApplet extends Applet implements Runnable
{
Thread meuThread;
public void init();
{
// o próprio applet é o objeto Runnable alvo da linha de execução
meuThread = new Thread( this );
...
}
public void start();
{
// iniciar a execução
meuThread.start();
...
}
// este método é executado pela linha de execução
public void run();
{
// aqui ficaria o laço de animação
while( true )
{
... // mexer os elementos da imagem;
repaint(); // repintar
try
{
// paradinha do processador
Thread.sleep( 20 );
}
catch( InterruptedException e )
{
// o que houve?
showStatus( e.toString() ) ;
}
}
}
public void stop();
{
// encerrar a execução
meuThread.stop();
...
}
}
Note que a estrutura acima é só um exemplo muito rudimentar. Em especial, ele produz uma animação que começa tão logo o applet for carregado pelo navegador e só é encerrada quando o applet parar (normalemente quando o usuário navegar para outra página). Um controle mais sofisticado pode ser obtido com chamadas aos métodos start, stop, suspend e resume do Thread geradas por eventos produzidos pelo usuário (por exemplo cliques sobre botões).
Eliminação da tremulação - Bufferização dupla
O laço de animação normalmente contem uma chamada ao método repaint da componente gráfica que apresenta a animação. Lembramos que este método chama o método update que por sua vez chama o método paint.
O método update, por default, limpa a tela da animação. Isto pode ser indesejável, por exemplo no caso de uma animação que pretende mostrar a elaboração progressiva de uma imagem final. Neste caso, o método update deve ser sobre-escrito para evitar a limpeza da tela. A forma mínima de tal método seria
public void update( Graphics g );
{
paint( g );
}
Mesmo nos casos nos quais a imagem precisa ser inteiramente repintada a cada passo da animação, o método update precisa ser sobre-escrito para obter uma animação de boa qualidade. Isto porque a operação de limpar e repintar continuamente a imagem, se feita diretamente sobre a tela do computador, produz uma tremulação no mínimo desagradável. A solução, chamada "bufferização dupla" consiste em utilizar uma imagem auxiliar "fora da tela", para fazer a operação de limpeza e repintagem. Esta imagem é transferida para a tela quando estiver pronta. A imagem auxiliar é uma instância da classe Image do pacote java.awt, criada através de uma chamada ao método createImage da componente gráfica. O método drawImage do contexto gráfico da tela é usado para transferir a imagem para a mesma. Eis o código que deve ser incluído na componente gráfica para tanto:
private Image offScreenImage; // imagem auxiliar declarada na classe
public void update( Graphics g )
{
// Criar a imagem auxiliar e buscar o seu context gráfico
if( offScreenImage == null )
offScreenImage = createImage( getSize().width, getSize().height );
Graphics offScreenGraphics = offScreenImage.getGraphics();
// limpar a imagem auxiliar
offScreenGraphics.setColor( getBackground() );
offScreenGraphics.fillRect( 0, 0, getSize().width, getSize().height );
// pintar a imagem auxiliar
offScreenGraphics.setColor( g.getColor() );
paint( offScreenGraphics );
// transferir a imagem auxiliar para a tela
g.drawImage( offScreenImage, 0, 0, this );
offScreenGraphics.dispose();
}
Aula 8
Tratamento de exceções
Considerações gerais
Classes Throwable e Exception
Lançamento de exceções
Captura de exceções
Considerações gerais
Exceções são situações excepcionais e geralmente indesejáveis que podem ocorrer durante a execução de um programa. Exceções podem ser tratadas incluindo-se código adequado no programa; não são portanto erros fatais.
Exemplos típicos de exceções são:
Índice de uma lista (Array) fora do intervalo permitido.
Problemas em operações aritméticas, tais como "overflows" e divisões por zero.
Argumentos inválidos numa chamada a um método.
Uso de uma referência que não aponta para nenhum objeto.
Falta de memória (relativamente improvável em Java graças ao coletor de lixo).
O modelo de tratamento de exceções Java permite tratar uma exceção num escopo (bloco de código) diferente daquele que gerou a exceção. Isto permite uma melhor organização do código.
Naturalmente, exceções são objetos em Java. A classe Exception é a superclasse de todas as exceções. Existem vários tipos de exceções já definidos nas bibliotecas. O programador pode também construir as suas próprias exceções.
Muitas classes de biblioteca possuem métodos que podem gerar ("lançar") exceções. Estas exceções podem ser tratadas ("capturadas") por código escrito pelo programador. Além disto, métodos escritos pelo programador também podem lançar exceções, tanto de tipos definidos nas bibliotecas como de novos tipos construidos pelo programador.
Classes Throwable e Exception
Objetos que podem ser lançados para indicar que algo anormal aconteceu, e capturados para lidar com esta situação, devem estender a classe Throwable do pacote java.lang. Os métodos mais úteis desta classe são:
public String getMessage()
// retorna uma mensagem de erro
public void printStackTrace()
// imprime uma descrição da pilha no instante em que o problema ocorreu
Como já mencionado, a superclasse de todas as exceções é a classe Exception do pacote java.lang. Conforme indicado acima, esta classe é uma subclasse da classe Throwable. Existem um grande número de subclasses de Exception espalhadas pelos vários pacotes de biblioteca. O programador pode estender a classe Exception ou uma das suas subclasses para construir as suas próprias exceções.
Nem todos os problemas que podem ocorrer são considerados exceções. Problemas mais sérios, que em geral não podem ser tratados pelo programador, são chamados erros e são objetos da classe Error do pacote java.lang (ou das subclasses desta classe, que também estão espalhadas pelos vários pacotes). Erros não serão considerados aqui.
Lançamento de exceções
Uma exceção é lançada usando-se a palavra chave throw seguinda da referência à exceção. Exemplo:
Exception opa = new Exception( "deu zebra" );
...
if( temProblema ) throw opa;
Se o programador desejar que a exceção assim lançada seja tratada fora do método que a gerou, ele deve explicitar isto usando a palavra chave throws seguida do tipo de exceção, na declaração do método. Por exemplo, o código acima estaria inserido no corpo de um método declarado como segue:
TipoDeRetorno nomeDoMetodo( ) throws Exception
{
// aqui vai o código acima
}
Na verdade, existe uma classe de exceções, RuntimeException (do pacote java.lang) que não precisam ser listadas explicitamente após a palavra chave throws. Correspondentemente, não é mandatório para o programador incluir código para manipular tais exceções.
Uma vez que a exceção foi lançada, a execução do método é interrompida e o controle volta ao objeto que chamou este método. Este objeto deve capturar a exceção como descrito a seguir, ou eventualmente relançar a exceção para que ela seja capturada mais alto na hierarquia das chamadas de métodos.
Captura de exceções
Para capturar uma exceção, é necessário montar a seguinte estrutura de código.
O código que pode lançar a exceção deve ser inserido num bloco precedido da palavra chave try. O processador então tentará executar o bloco, até que eventualmente uma exceção seja lançada, seja por um comando contido dentro do bloco, seja por um método chamado dentro do bloco.
O bloco try descrito acima deve ser seguido de um bloco que será executado caso houver de fato lançamento de uma exceção do tipo em questão. Este bloco deve ser anunciado pela palavra chave catch seguida (entre parénteses) do tipo de exceção em questão. Se vários tipos de exceções podem ser lançadas no bloco try, deve-se fornecer um bloco catch para cada tipo de exceção. Se uma exceção do tipo em questão for lançada no bloco try, o bloco try é encerrado e a execução salta para o bloco catch apropriado. Os blocos catch são chamados manipuladores de exceções.
Ainda é possível acrescentar, após o(s) bloco(s) catch, um bloco precedido da palavra chave finally, que será executado em todos se casos, após a execução completa do bloco try ou após a execução de um bloco catch, conforme o caso.
Resumindo, a estrura é:
try
{
// aqui vai código que pode gerar exceções dos tipos
// ExceptionType1 e ExceptionType2
}
catch( ExceptionType1 opa1 )
{
// aqui vai código para lidar com uma exceção do tipo
// ExceptionType1
}
catch( ExceptionType2 opa2 )
{
// aqui vai código para lidar com uma exceção do tipo
// ExceptionType2
}
finally
{
// aqui vai código que deve ser executado em qualquer caso
}
Quando ocorre uma exceção, os blocos catch são examinados sucessivamente até que o argumento corresponda ao tipo da exceção. Note que, no exemplo acima, se ExceptionType2 for uma subclasse de ExceptionType1, o segundo bloco catch nunca será alcançado. Neste caso, deve-se inverter a ordem dos blocos catch.
Um bloco catch pode também lançar novas exceções e relançar a exceção que ele manipulou. Neste caso, todo o conjunto de blocos acima deve estar inserido num bloco try mais externo e as exceções em questão devem ser manipuladas por blocos catch seguindo este bloco try externo.
Em Java,
(A) é possível criar e manipular objetos, mas não removê-los, pois a remoção é manipulada automaticamente pelo sistema. - quem remove é o garbage collector
(B) classes são definidas através do uso da palavra chave class, seguido do nome da classe, que, entre outras restrições, não pode ser iniciado por um número. - a declaração de classe é Class tal
(C) quando uma classe é criada e não há nenhuma referência à sua superclasse, implicitamente a classe criada é derivada diretamente da classe Object. - quando uma classe é definida, a criação já é a instanciação
(D) construtores da superclasse podem ser explicitamente invocados usando a palavra-chave super. - não vi erro algum aqui
(E) todas as determinações de métodos a executar ocorrem através de ligação tardia. - apenas métodos polimorficos é feito tardiamente.
Se tivesse no enunciado "Em java é incorreto afirmar que," eu marcaria a letra E, mesmo colocando essas considerações...
Resposta: Letra E
(A) é possível criar e manipular objetos, mas não removê-los, pois a remoção é manipulada automaticamente pelo sistema.
(B) classes são definidas através do uso da palavra chave class, seguido do nome da classe, que, entre outras restrições, não pode ser iniciado por um número.
(C) quando uma classe é criada e não há nenhuma referência à sua superclasse, implicitamente a classe criada é derivada diretamente da classe Object.
(D) construtores da superclasse podem ser explicitamente invocados usando a palavra-chave super.
(E) todas as determinações de métodos a executar ocorrem através de ligação tardia
Referências
Características gerais da linguagem
Aplicativos independentes e "applets"
Referências
Livros
Java 2 - Aprenda em 21 dias, L. Lemay e R. Cadenhead, Editora Campus Ltda (1999).
Completo, didático, atual, alguns erros de tradução.
Java: How to Program, 2nd edition, H. M. Deitel e P. J. Deitel, Prentice Hall (1998);
3ra edição em Portugués, Java: Como Programar, Editora Bookman (2000).
Completo, excelente, vem com um CD contendo exemplos comentados.
The Java Programming Language, 3rd edition, K. Arnold, J. Gosling e D. Holmes, Addison-Wesley (2000);
Referência clássica, co-autorada pelo guru mor James Gosling.
Java for Engineers and Scientists, S. J. Chapman, Prentice Hall (2000).
Completo, apresenta aplicações na ciência e na engenharia.
Thinking in Java, 2nd edition, B. Eckel, Prentice Hall (2000).
Disponível na rede: http://www.mindview.net/Books/TIJ/
The Java Tutorial, 3rd edition, M. Campione, K. Walrath e A. Huml, Addison-Wesley (2000).
Disponível na rede: http://java.sun.com/docs/books/tutorial/
Sítios
Sítio oficial Java da Sun: http://java.sun.com/
Gamelan: tudo sobre java: http://softwaredev.earthweb.com/java
Jars: mais sobre java: http://www.jars.com/
avaliação de "applets" de ciência: http://www.jars.com/jars_categories_java_science.html
Ferramentas
Básica: Java Development Kit (JDK), disponível gratuitamente no sítio da Sun.
JDK1.0 (95-96), ultrapassado.
JDK1.1 (97-98), suportado pelos navegadores atuais.
JDK1.2 (99) ainda não suportado pelos navegadores (requer Java Plug-in).
SDK1.3 (01), evolução do anterior. O nome mudou para Software Development Kit. Outro nome usado é J2SE (Java 2, Standard Edition).
Ambientes integrados de desenvolvimento
Forte for Java (Sun, versão Comunidade gratuita) http://www.sun.com/forte/ffj/
Kawa (Allaire, pago) http://www.allaire.com/products/kawa/index.cfm
Simplicity for Java (Data Representations, pago) http://www.datarepresentations.com/
JBuilder (Borland-Inprise, versão Pessoal gratuita) http://www.borland.com/jbuilder/
Visual Café (Webgain, pago) http://www.webgain.com/products/visual_cafe/
VisualAge for Java (IBM, pago) http://www.software.ibm.com/ad/vajava/
JCreator (Xinox Software, versão gratuita) http://www.jcreator.com/
Características gerais da linguagem
Particularidades e vantagens
Programas compilados independentes de plataforma.
Os arquivos de byte são pequenos -> carregados rapidamente pela Internet.
Ampla biblioteca de componentes e recursos - Gratuita!
Componentes gráficas
Tratamento de eventos
Formatação de texto
Funções matemáticas
Entrada e saida
Computação em rede
Bancos de dados
. . .
Programação (relativamente) simples e natural.
Programação "orientada a objeto" - facilita a reutilização de código.
Permite linhas múltiplas de execução.
Comparação com C++:
Possui herança simples. Implementa uma forma segura de herança múltipla através de "interfaces".
Não permite aritmética de ponteiros.
Possui desalocação automática de memória ("coletor de lixo").
Limitações e desvantagens
Velocidade de execução relativamente baixa
Pode ser melhorada usando compiladores JIT ("just in time") que transformam o bytecode em código de máquina antes da execução.
Evolução rápida -> mutiplicidade de versões
A versão mais recente, batizada Java 2 Platform (SDK1.3) ainda não está implementada nos navegadores. Requer o
Java Plug-in http://java.sun.com/products/plugin/
Aplicativos independentes e applets
Aplicativo independente
Programa que é executado fora de um navegador. Utilize um editor (o Bloco de Notas ou o WordPad se você está trabalhando no Windows) para digitar e editar o arquivo fonte. Cuidado ao digitar: Java distingue maiúsculas e minúsculas. O esquema mínimo para o programa de um aplicativo independente é:
public class MeuAplicativo
{
public static void main( String args[] )
{
. . .
}
}
onde . . . indica comandos que definem o que o programa vai fazer. O programa deve ser salvo num arquivo chamado: MeuAplicativo.java . Vê-se que para construir um programa em Java, precisamos definir uma classe. O corpo da classe, contido entre as chaves mais externas, contem em geral definições de variáveis e métodos, que especificam aquilo que a classe pode fazer. No exemplo acima, o único método presente é o método principal, que deve estar presente em qualquer aplicativo independente e é executado automaticamente quando o aplicativo é executado. Parâmetros podem ser passados ao programa no comando de execução, na forma de uma lista de cadeias de caráters (tipo String) chamada args[] no exemplo acima. Os colchetes indicam que trata-se de uma lista.
Não detalharemos agora o significado das demais palavras chaves presentes no código acima. Por enquanto, estamos apenas "dando a receita" para construir o aplicativo o mais simples possível.
Para compilar o programa, digite na linha de comando (do DOS se você está usando Windows):
javac MeuAplicativo.java
Se a compilação for bem sucedida, isto gera uma arquivo MeuAplicativo.class na mesma pasta.
Para executar o programa, digite na linha de comando:
java MeuAplicativo
Se o seu programa requer parâmetros de entrada (digamos a1 e a2), estes devem ser acrescentados ao comando:
java MeuAplicativo a1 a2
Applet
Programa que é executado dentro de uma navegador. Utilize um editor para digitar e editar o arquivo fonte. O esquema mínimo para o programa de um applet é:
import java.applet.*;
public class MeuApplet extends Applet
{
public void init( )
{
. . .
}
}
onde . . . indica comandos que definem o que o programa vai fazer. O programa deve ser salvo num arquivo chamado MeuApplet.java . O método init() é chamado automaticamente pelo navegador quando ele carrega o applet. Note que o pacote java.applet deve ser importado para ter acesso à classe Applet.
Para compilar o programa, digite na linha de comando:
javac MeuApplet.java
Se a compilação for bem sucedida, isto gera uma arquivo MeuApplet.class na mesma pasta.
O applet é carregado por um navegador através de um arquivo HTML. O arquivo mínimo é:
HTML
HEAD
TITLEMeu Primeiro Applet/TITLE
/HEAD
BODY>
APPLET CODE="MeuApplet.class" WIDTH=400 HEIGHT=160
/APPLET
/BODY
/HTML
Digamos que o nome deste arquivo seja MeuApplet.htm . Para testar o seu applet sem ter que abrir um navegador, digite na linha de comando:
appletviewer MeuApplet.htm
Dicas
Console Java
Procure este item no menu do seu navegador para abrir uma janela que dá informaçães sobre a máquina virtual e mostra eventuais mensagens de erro.
Documentação:
A pasta /docs do JDK contem uma documentação completa em formato HTML. Em especial, a pasta docs/api documenta as bibliotecas.
Demonstrações
A pasta /demo do JDK contem vários applets demonstrativos, com arquivos fontes incluídos.
Caminho
Para que você possa executar o compilador (javac), o interpretador (java) e o visualizador de applet (appletviewer), é necessário que a pasta /bin do JDK esteja incluída no seu PATH. No Windows, isto é feito editando o arquivo Autoexec.bat.
Prática
Você pode agora construir os seus primeiros aplicativos e applets, seguindo o roteiro apresentado na Prática 1.
________________________________________________________________
Elementos básicos da síntaxe
Tipos de dados primitivos
Expressões e operadores
Estruturas de controle de fluxo
Elementos básicos da síntaxe
Comandos
Comandos em Java são separadas por um ponto-vírgula. É permitido colocar vários comandos sobre a mesma limha.
Comandos são organizadas em blocos definidos por chaves {...}. Em especial, classes e métodos são blocos. Blocos podem conter outros blocos.
Variáveis
Nomes de variáveis devem começar com uma letra, um caráter de sublinhado _ ou um cifrão $. Não podem começar por um número.
É convencional (mas não obrigatório) usar uma letra minúscula para a primeira letra do nome de uma variável.
Todas as variáveis de tipos primitivos precisam ser declaradas e inicializadas, através de uma instrução da forma
onde
As variáveis associadas a objetos, que são referências aos mesmos, precisam ser declaradas. Os objetos por sua vez precisam ser criados. Declaração e criação podem sem realizadas por comandos separados:
NomeDaClasse nomeDoObjeto;
nomeDoObjeto = new NomeDaClasse();
A primeira linha informa que a variável nomeDoObjeto vai designar um objeto que é uma instância da classe NomeDaClasse. A segunda linha cria um novo objeto pertencente àquela classe e atribui a este objeto a referência nomeDoObjeto. A parte NomeDaClasse() do comando é na verdade uma chamada ao método construtor da classe. Em muitos casos, este método pode receber parâmetros que são incluídos nas parénteses e servem para especificar a inicialização do objeto.
As duas linhas acima podem ser combinadas numa só:
NomeDaClasse nomeDoObjeto = new NomeDaClasse();
O escopo de uma variável, ou seja a região do programa na qual ela está definida, é limitado ao bloco no qual ela foi declarada.
Comentários
Um comentário curto relativo a uma linha de código pode ser incluido no fim da linha, precedido de // :
... ; // Comentário sobre o comando ...
Um comentário precedido de /* e seguido de */ pode ocupar várias linhas:
/* Comentário maior,
que pode ocupar várias linhas. */
Obviamente, isto serve também para forçar o compilador a ignorar temporariamente um trecho de código.
O JDK oferece um recurso para gerar automaticamente um arquivo HTML documentando uma classe. Comentários que forem precedidos de /** e seguidos de */ serão incluidos neste arquivo:
/** Este comentário será incluído no arquivo HTML
documentando a classe. */
Para gerar a documentação relativa à(s) classe(s) cuja(s) fonte(s) estam no arquivo MeuPrograma.java, digita-se na linha de comando:
javadoc MeuPrograma.java
Tipos de dados primitivos
Números inteiros
Há quatro tipos de inteiros em Java:
Tipo Tamanho Valor
byte 8 bits -128 a 127
short 16 bits -32.768 a 32.767
int 32 bits -2.147.483.648 a 2.147.483.647
long 64 bits -9.223.372.036.854.775.808 a 9.223.372.036.854.775.807
Um número inteiro pode sempre ser atribuído a outro de maior precisão:
int a = 274;
long b = a;
A operação inversa requer coerção explicita ("casting"):
long a = 274;
int b = ( int ) a;
Sem esta coerção, haverá erro de compilação. Note que, mesmo com a coerção, ainda pode haver problema na execução, caso o valor do número atribuído ultrapassar o maior valor possível para o tipo em questão.
Números em ponto flutuante
Há dois tipos, de precisão simples e de precisão dupla:
Tipo Tamanho Valor
float 32 bits -3.40292347E+38 a +3.40292347E+38
double 64 bits -1.79769313486231570E+308 a +1.79769313486231570E+308
Um número de precisão simples pode sempre ser atribuído a outro de precisão dupla:
float a = 2.74F; // F (ou f) após o literal indica precisão simples
double b = a;
A operação inversa requer coerção explicita:
double a = 2.74e12;
float b = ( float ) a;
Sem esta coerção, haverá erro de compilação. De novo, mesmo com a coerção, ainda pode haver problema na execução, caso o valor do número de precisão dupla ultrapassar o maior valor possível para um número de precisão simples.
Note que Java não possui tipos primitivos representando números complexos. Não há recursos para lidar com números complexos nas bibliotecas fornecidas com o JDK, mas vários pacotes avulsos são disponíveis. Não sendo tipos primitivos, números complexos devem ser objetos em Java.
Caráteres
Há um tipo primitivo que representa um caráter:
Tipo Tamanho Valor
char 16 bits '\u0000' a '\uFFFF'
Java utiliza o padrão de caráteres Unicode, que abrange os conjuntos de caráteres de muitas linguas.
Um literal de caráter é especificado por um único caráter entre apóstrofes simples:
char exemplo = 'a';
Para caráteres de escape, usa-se a barra invertida:
Escape Significado
\n nova linha
\t tabulação
\b passo para trás
\r retorno do carro
\\ barra invertida
\' apóstrofe
\" aspas
Booleanos
São variáveis lógicas que podem assumir os valores verdadeiro e falso:
Tipo Tamanho Valor
boolean 1 bit true ou false
Note que, em Java, estas variáveis não podem ser interpretadas como os números inteiros 0 e 1.
Expressões e operadores
Uma expressão é uma instrução de realizar uma operação que produz um valor (valor de retorno). Operadores são símbolos especiais utilizados para operações matemáticas, atribuições, comparações e operações lógicas.
Operadores aritméticos
Os operadores aritméticos disponíveis em Java são:
Operador Significado
+ adição
- subtração
* multiplicação
/ divisão
% resto da divisão (módulo)
Note que Java não possui um operador específico para potência (tal como ** em FORTRAN). Para calcular uma potência, você deve usar o método pow da classe Math do pacote lang, ou seja, para calcular xy, você escreve Math.pow(x, y), onde x e y são de tipo double, e o resultado também.
Operadores de atribuição
Estes operadores são simplesmente uma notação compacta para uma operação aritmética seguida da atribuição do valor de retorno à variável que continha o primeiro termo da operação.
Operador Exemplo Expressão equivalente
+= x += y x = x + y
-= x -= y x = x - y
*= x *= y x = x * y
/= x /= y x = x / y
%= x %= y x = x % y
Operadores de incremento e decremento
São operadores que atuam sobre uma única variável numérica, aumentando ou diminuindo o seu valor de uma unidade:
Operador Exemplo Significado
++ ++a adicionar 1 à variável a e depois calcular a expressão na qual a reside
a++ calcular a expressão na qual a reside e depois adicionar 1 à variável a
-- --a subtrair 1 da variável a e depois calcular a expressão na qual a reside
a-- calcular a expressão na qual a reside e depois subtrair 1 da variável a
Exemplo:
int x = 5, y = 7; // x vale 5 e y vale 7
y += ++x; // x agora vale 5 + 1 = 6 e y vale 7 + 6 = 13
x -= y--; // x agora vale 6 - 13 = - 7 e y vale 13 - 1 = 12
Operadores de comparação
Estes operadores atuam sobre valores numéricos e retornam valores booleanos, true (verdadeiro) ou false (falso):
Operador Significado
== igual a
!= diferente de
< menor que
> maior que
<= menor ou igual a
>= maior ou igual a
O operador == também serve para comparar outros tipos de dados, inclusive objetos.
Operadores lógicos
Estes operadores atuam sobre valores booleanos e retornam valores booleanos, true ou false.
Operador Significado Exemplo Explicação
&& E ("logical AND") a && b retorna true se a e b forem ambos true. Senão retorna false. Se a for false, b não é avaliada.
& E ("boolean logical AND") a & b retorna true se a e b forem ambos true. Senão retorna false. Ambas expressões a e b são sempre avaliadas.
|| OU ("logical OR") a || b retorna true se a ou b for true. Senão retorna false. Se a for true, b não é avaliada.
| OU ("boolean logical inclusive OR") a | b retorna true se a ou b for true. Senão retorna false. Ambas expressões a e b são sempre avaliadas.
^ OU EXCLUSIVO ("boolean logical exclusive OR") a ^ b retorna true se a for true e b for false ou vice-versa. Senão retorna false
! NÃO ("logical NOT") !a retorna true se a for false. Senão retorna false
Precedência e associatividade dos operadores
Os operadores estão listados na tabela abaixo em ordem decrescente de prioridade.
Operador Associatividade
() da esquerda para a direita
++ -- + - ! unários; da direita para a esquerda
* / % da esquerda para a direita
+ - da esquerda para a direita
< <= > >= da esquerda para a direita
== != da esquerda para a direita
& da esquerda para a direita
^ da esquerda para a direita
| da esquerda para a direita
&& da esquerda para a direita
|| da esquerda para a direita
= += -= *= /= %= da direita para a esquerda
Estruturas de controle de fluxo
Estruturas de seleção
Para executar um comando sujeita a uma condição:
if(
A variável ou expressão condição deve ser do tipo boolean. O comando é executado se condição possuir o valor true.
Para executar vários comandos sujeitos a uma condição, coloca-se os comandos num bloco:
if(
<...
}
Para executar certos comandos caso uma condição for satisfeita, e outros comandos caso contrário:
if(
}
else{
}
No este exemplo, comando 1 e comando 2 são executados se condição for true. Caso contrário, comando 3 e comando 4 são executados.
O operador ?: pode ser utilizado em certos casos simples como um atalho para a estrutura if...else:
Aqui,
Escolhas múltiplas de comandos a serem executados, dependendo do valor de uma variável, podem ser implementadas da seguinte maneira:
switch(
case
break;
case
break;
case
...
break;
default:
}
O
Estruturas de repetição (laços)
Para repetir um conjunto de instruções, controlando o número de repetições com um contador:
for( int i =
}
Note a declaração int i do contador. O teste i <=
Para repetir um conjunto de instruções enquanto uma certa condição for satisfeita:
while(
}
Obviamente a
do{
}
while(
A diferença é que a
Para interromper um laço, usa-se o comando break:
while(
if(
}
O laço é interrompido quando a
Para passar à iteração seguinte de um laço, usa-se o comando continue:
for( int i =
if(
<}
Se
Os comandos break e continue sem rótulos atuam sobre o laço mais interno no qual eles se encontram. No caso de laços aninhados, comandos rotulados podem ser utilizados para saltar para um laço mais externo:
rohtulo:{
while(
for( int i =
if(
}
}
}
Se
rohtulo:{
for( int k =
for( int i =
if(
}
}
}
Se
Elementos básicos da síntaxe
Tipos de dados primitivos
Expressões e operadores
Estruturas de controle de fluxo
Elementos básicos da síntaxe
Comandos
Comandos em Java são separadas por um ponto-vírgula. É permitido colocar vários comandos sobre a mesma limha.
Comandos são organizadas em blocos definidos por chaves {...}. Em especial, classes e métodos são blocos. Blocos podem conter outros blocos.
Variáveis
Nomes de variáveis devem começar com uma letra, um caráter de sublinhado _ ou um cifrão $. Não podem começar por um número.
É convencional (mas não obrigatório) usar uma letra minúscula para a primeira letra do nome de uma variável.
Todas as variáveis de tipos primitivos precisam ser declaradas e inicializadas, através de uma instrução da forma
onde
As variáveis associadas a objetos, que são referências aos mesmos, precisam ser declaradas. Os objetos por sua vez precisam ser criados. Declaração e criação podem sem realizadas por comandos separados:
NomeDaClasse nomeDoObjeto;
nomeDoObjeto = new NomeDaClasse();
A primeira linha informa que a variável nomeDoObjeto vai designar um objeto que é uma instância da classe NomeDaClasse. A segunda linha cria um novo objeto pertencente àquela classe e atribui a este objeto a referência nomeDoObjeto. A parte NomeDaClasse() do comando é na verdade uma chamada ao método construtor da classe. Em muitos casos, este método pode receber parâmetros que são incluídos nas parénteses e servem para especificar a inicialização do objeto.
As duas linhas acima podem ser combinadas numa só:
NomeDaClasse nomeDoObjeto = new NomeDaClasse();
O escopo de uma variável, ou seja a região do programa na qual ela está definida, é limitado ao bloco no qual ela foi declarada.
Comentários
Um comentário curto relativo a uma linha de código pode ser incluido no fim da linha, precedido de // :
... ; // Comentário sobre o comando ...
Um comentário precedido de /* e seguido de */ pode ocupar várias linhas:
/* Comentário maior,
que pode ocupar várias linhas. */
Obviamente, isto serve também para forçar o compilador a ignorar temporariamente um trecho de código.
O JDK oferece um recurso para gerar automaticamente um arquivo HTML documentando uma classe. Comentários que forem precedidos de /** e seguidos de */ serão incluidos neste arquivo:
/** Este comentário será incluído no arquivo HTML
documentando a classe. */
Para gerar a documentação relativa à(s) classe(s) cuja(s) fonte(s) estam no arquivo MeuPrograma.java, digita-se na linha de comando:
javadoc MeuPrograma.java
Tipos de dados primitivos
Números inteiros
Há quatro tipos de inteiros em Java:
Tipo Tamanho Valor
byte 8 bits -128 a 127
short 16 bits -32.768 a 32.767
int 32 bits -2.147.483.648 a 2.147.483.647
long 64 bits -9.223.372.036.854.775.808 a 9.223.372.036.854.775.807
Um número inteiro pode sempre ser atribuído a outro de maior precisão:
int a = 274;
long b = a;
A operação inversa requer coerção explicita ("casting"):
long a = 274;
int b = ( int ) a;
Sem esta coerção, haverá erro de compilação. Note que, mesmo com a coerção, ainda pode haver problema na execução, caso o valor do número atribuído ultrapassar o maior valor possível para o tipo em questão.
Números em ponto flutuante
Há dois tipos, de precisão simples e de precisão dupla:
Tipo Tamanho Valor
float 32 bits -3.40292347E+38 a +3.40292347E+38
double 64 bits -1.79769313486231570E+308 a +1.79769313486231570E+308
Um número de precisão simples pode sempre ser atribuído a outro de precisão dupla:
float a = 2.74F; // F (ou f) após o literal indica precisão simples
double b = a;
A operação inversa requer coerção explicita:
double a = 2.74e12;
float b = ( float ) a;
Sem esta coerção, haverá erro de compilação. De novo, mesmo com a coerção, ainda pode haver problema na execução, caso o valor do número de precisão dupla ultrapassar o maior valor possível para um número de precisão simples.
Note que Java não possui tipos primitivos representando números complexos. Não há recursos para lidar com números complexos nas bibliotecas fornecidas com o JDK, mas vários pacotes avulsos são disponíveis. Não sendo tipos primitivos, números complexos devem ser objetos em Java.
Caráteres
Há um tipo primitivo que representa um caráter:
Tipo Tamanho Valor
char 16 bits '\u0000' a '\uFFFF'
Java utiliza o padrão de caráteres Unicode, que abrange os conjuntos de caráteres de muitas linguas.
Um literal de caráter é especificado por um único caráter entre apóstrofes simples:
char exemplo = 'a';
Para caráteres de escape, usa-se a barra invertida:
Escape Significado
\n nova linha
\t tabulação
\b passo para trás
\r retorno do carro
\\ barra invertida
\' apóstrofe
\" aspas
Booleanos
São variáveis lógicas que podem assumir os valores verdadeiro e falso:
Tipo Tamanho Valor
boolean 1 bit true ou false
Note que, em Java, estas variáveis não podem ser interpretadas como os números inteiros 0 e 1.
Expressões e operadores
Uma expressão é uma instrução de realizar uma operação que produz um valor (valor de retorno). Operadores são símbolos especiais utilizados para operações matemáticas, atribuições, comparações e operações lógicas.
Operadores aritméticos
Os operadores aritméticos disponíveis em Java são:
Operador Significado
+ adição
- subtração
* multiplicação
/ divisão
% resto da divisão (módulo)
Note que Java não possui um operador específico para potência (tal como ** em FORTRAN). Para calcular uma potência, você deve usar o método pow da classe Math do pacote lang, ou seja, para calcular xy, você escreve Math.pow(x, y), onde x e y são de tipo double, e o resultado também.
Operadores de atribuição
Estes operadores são simplesmente uma notação compacta para uma operação aritmética seguida da atribuição do valor de retorno à variável que continha o primeiro termo da operação.
Operador Exemplo Expressão equivalente
+= x += y x = x + y
-= x -= y x = x - y
*= x *= y x = x * y
/= x /= y x = x / y
%= x %= y x = x % y
Operadores de incremento e decremento
São operadores que atuam sobre uma única variável numérica, aumentando ou diminuindo o seu valor de uma unidade:
Operador Exemplo Significado
++ ++a adicionar 1 à variável a e depois calcular a expressão na qual a reside
a++ calcular a expressão na qual a reside e depois adicionar 1 à variável a
-- --a subtrair 1 da variável a e depois calcular a expressão na qual a reside
a-- calcular a expressão na qual a reside e depois subtrair 1 da variável a
Exemplo:
int x = 5, y = 7; // x vale 5 e y vale 7
y += ++x; // x agora vale 5 + 1 = 6 e y vale 7 + 6 = 13
x -= y--; // x agora vale 6 - 13 = - 7 e y vale 13 - 1 = 12
Operadores de comparação
Estes operadores atuam sobre valores numéricos e retornam valores booleanos, true (verdadeiro) ou false (falso):
Operador Significado
== igual a
!= diferente de
< menor que
> maior que
<= menor ou igual a
>= maior ou igual a
O operador == também serve para comparar outros tipos de dados, inclusive objetos.
Operadores lógicos
Estes operadores atuam sobre valores booleanos e retornam valores booleanos, true ou false.
Operador Significado Exemplo Explicação
&& E ("logical AND") a && b retorna true se a e b forem ambos true. Senão retorna false. Se a for false, b não é avaliada.
& E ("boolean logical AND") a & b retorna true se a e b forem ambos true. Senão retorna false. Ambas expressões a e b são sempre avaliadas.
|| OU ("logical OR") a || b retorna true se a ou b for true. Senão retorna false. Se a for true, b não é avaliada.
| OU ("boolean logical inclusive OR") a | b retorna true se a ou b for true. Senão retorna false. Ambas expressões a e b são sempre avaliadas.
^ OU EXCLUSIVO ("boolean logical exclusive OR") a ^ b retorna true se a for true e b for false ou vice-versa. Senão retorna false
! NÃO ("logical NOT") !a retorna true se a for false. Senão retorna false
Precedência e associatividade dos operadores
Os operadores estão listados na tabela abaixo em ordem decrescente de prioridade.
Operador Associatividade
() da esquerda para a direita
++ -- + - ! unários; da direita para a esquerda
* / % da esquerda para a direita
+ - da esquerda para a direita
< <= > >= da esquerda para a direita
== != da esquerda para a direita
& da esquerda para a direita
^ da esquerda para a direita
| da esquerda para a direita
&& da esquerda para a direita
|| da esquerda para a direita
= += -= *= /= %= da direita para a esquerda
Estruturas de controle de fluxo
Estruturas de seleção
Para executar um comando sujeita a uma condição:
if(
A variável ou expressão condição deve ser do tipo boolean. O comando é executado se condição possuir o valor true.
Para executar vários comandos sujeitos a uma condição, coloca-se os comandos num bloco:
if(
<...
}
Para executar certos comandos caso uma condição for satisfeita, e outros comandos caso contrário:
if(
}
else{
}
No este exemplo, comando 1 e comando 2 são executados se condição for true. Caso contrário, comando 3 e comando 4 são executados.
O operador ?: pode ser utilizado em certos casos simples como um atalho para a estrutura if...else:
Aqui,
Escolhas múltiplas de comandos a serem executados, dependendo do valor de uma variável, podem ser implementadas da seguinte maneira:
switch(
case
break;
case
break;
case
...
break;
default:
}
O
Estruturas de repetição (laços)
Para repetir um conjunto de instruções, controlando o número de repetições com um contador:
for( int i =
}
Note a declaração int i do contador. O teste i <=
Para repetir um conjunto de instruções enquanto uma certa condição for satisfeita:
while(
}
Obviamente a
do{
}
while(
A diferença é que a
Para interromper um laço, usa-se o comando break:
while(
if(
}
O laço é interrompido quando a
Para passar à iteração seguinte de um laço, usa-se o comando continue:
for( int i =
if(
<}
Se
Os comandos break e continue sem rótulos atuam sobre o laço mais interno no qual eles se encontram. No caso de laços aninhados, comandos rotulados podem ser utilizados para saltar para um laço mais externo:
rohtulo:{
while(
for( int i =
if(
}
}
}
Se
rohtulo:{
for( int k =
for( int i =
if(
}
}
}
Se
Cadeias de caráteres ("Strings")
Listas ("Arrays")
Cadeias de caráteres
Construção
Cadeias de caráteres são objetos, instâncias da classe String do pacote java.lang. Para declarar uma cadeia de caráteres e atribuir à mesma um valor literal, coloca-se este valor entre aspas:
String s = "Olá!";
Pode-se também criar explicitamente um novo objeto, chamando-se o método construtor da classe:
String s = new String( "Olá!" );
O método construtor pode receber outros tipos de argumentos, por exemplo uma lista dos caráteres que vão constituir a cadeia. Veja os detalhes na documentação.
Uma vez a cadeia criada, o seu conteudo não pode mudar. Se percisarmos de uma cadeia de caráteres cujo conteudo possa mudar, precisamos usar a classe StringBuffer do pacote java.lang, que não será discutida nestas notas.
Obviamente, uma dada referência do tipo String não precisa apontar sempre para o mesmo objeto. Assim, se tivermos
String s = new String( "Olá!" );
s = "Olá Mundo!";
a referência s
deixa de referir-se ao primeiro objeto criado (cujo conteudo erá "Olá!") e passa a referir-se ao objeto cujo conteudo é "Olá Mundo!"). Se não houver mais nenhuma referência apontando para o primeiro objeto, este estará marcado para ser varrido pelo coletor de lixo tão logo este resolver entrar em ação.
Comprimento
O método length permite descobrir o número de caráteres contidos numa String
. Por exemplo:
String s = new String( "Olá!" );
int nuhmero = s.length();
resulta no valor 4 para o inteiro chamado nuhmero.
Há outros métodos que permitem obter informação a respeito de uma String, por exemplo, descobrir qual o caráter numa dada posição. Veja a documentação.
Comparação
Para determinar se duas cadeias de caráteres possuem o mesmo conteudo, usa-se o método equals. Este método considera maiúsculas e minúsculas como diferentes. Se não desejamos fazer esta distinção, devemos usar o método equalsIgnoreCase.
É importante não confundir estas comparações de conteudo com o uso do operador ==, que, quando usado entre duas variáveis que são referências a objetos, serve para determinar se as duas variáveis apontam para o mesmo objeto.
Como ilustração, considere o seguinte trecho de código:
String s1, s2, s3, s4, s5, s6;
s1 = "Olá!";
s2 = "Olá!";
s3 = "olá!":
s4 = new String( "Olá!" );
s5 = s4;
s6 = new String( "Olá!" );
boolean b1, b2, b3, b4, b5, b6, b7, b8;
b1 = s2.equals( s1 );
b2 = s3.equals( s1 );
b3 = s3.equalsIgnoreCase( s1 );
b4 = s4.equals( s1 );
b5 = ( s2 == s1 );
b6 = ( s4 == s1 );
b7 = ( s4 == s5 );
b8 = ( s4 == s6 );
b9 = s4.equals( s6 );
Convença-se de que, após execução deste código, os seguintes booleanos possuem o valor true: b1, b3, b4, b5, b7 e b9. Já b2, b6 e b8 possuem o valor false.
Consulte a documentação para obter informação sobre métodos que permitem comparar pedaços de cadeias de caráteres e também determinar qual de duas cadeias vem primeira na ordem alfabética.
Concatenação
Para concatenar duas String, usa-se o método concat. Note que, já que o tamanho de uma String não pode mudar, a concatenação resulta na criação de um novo objeto.
Também pode-se usar o operador + para a concatenação. Por exemplo, no trecho de código abaixo, a mesma linha é impressa duas vezes:
String s1 = "Olá ";
String s2 = "Mundo!";
System.out.println( s1.concat( s2 ) );
System.out.println( s1 + s2 );
Consulte a documentação para obter informação sobre métodos que permitem modificar cadeias de caráteres, por exemplo alterando um caráter ou passando de minúsculas para maiúsculas. Lembre-se de que, na verdade, estes métodos criam uma nova cadeia.
Conversão de dados numéricos
O método estático valueOf da classe String permite obter uma cadeia de caráter que representa um dado tipo numérico. Um método estático está associado à classe, e não a uma instância particular da mesma. É fácil entender que o método em questão deve ser estático, pois quando resolvemos expressar um número na forma de caráteres, ainda não temos uma String. O método a ser chamado é que vai criá-la. Por exemplo:
double a = 3.1415927;
int i = 123;
System.out.println( "a = " + String.valueOf( a ) );
System.out.println( "i = " + String.valueOf( i ) );
Este código resulta na impressão de a = 3.1415927 seguido de i = 123 na saida padrão. Na verdade, o mesmo resultado pode ser obtido com uma sintaxe simplificada, pois numa concatenação, números são transformados automaticamente em caráteres. Assim, as dua últimas linhas acima podem ser substituidas por
System.out.println( "a = " + a );
System.out.println( "i = " + i );
Para realizar a operação inversa, ou seja transformar caráteres em tipos numéricos, é de se esperar que precisamos utilizar um método estático associado ao tipo numérico em questão. Porém, como um tipo primitivo em si não é um objeto, precisamos primeiro descobrir como associar uma classe a um tipo primitivo. As classes adequadas são chamadas de classes de embrulhamento (("wrapper classes") e podem ser encontradas no pacote java.lang. Por exemplo, a classe de embrulhamento de um tipo double é Double. Portanto, precisamos primeiro traduzir a nossa cadeia de caráteres num objeto Double. Para tanto, chamamos o método estático valueOf desta classe. Ainda precisaremos "desembrulhar" o nosso número, ou seja traduzir o nosso objeto Double num tipo primitivo double. Isto é feito chamando o método doubleValue da classe Double. O trecho de código abaixo ilustra este procedimento, assim como o semelhante para o caso de um tipo inteiro. (Outros tipos podem ser tratados também da mesma maneira.)
String atxt = "3.1415927";
String itxt = "123";
double a = Double.valueOf( atxt ).doubleValue();
int i = Integer.valueOf( itxt ).intValue();
Após execução, o valor da variável de precisão dupla a é 3.1415927 e o valor da variável inteira i é 123. Num exemplo mais realístico, os valores expressos na forma de caráteres poderiam ser lidos de um arquivo ou digitados pelo usuário.
No caso de inteiros, há uma maneira mais simples de realizar a operação acima: o método parseInt da classe Integer transforma diretamente uma cadeia de caráteres num tipo int. Portanto, o comando
int i = Integer.parseInt( itxt );
pode ser usado em vez da última linha do trecho de código anterior.
Formatação
A transformação de números em caráteres descrita na seção anterior resulta numa representação completa do número, sem arredondamento nem controle do formato pelo programador. Recursos para formatar texto, e em especial números, podem ser encontrados no pacote java.text. Como é de se esperar, formatos são objetos em Java. Como ilustração, o trecho de código abaixo resulta na impressão de a = 3.1416 na saida padrão:
double a = 3.1415927;
DecimaFormat meuFormato = new DecimalFormat( "0.####" );
System.out.println( "a = " + meuFormato.format( a ) );
O formato desejado é especificado pelo argumento do método construtor (veja os detalhes na documentação). O método format transforma o número (neste caso, de precisão dupla) numa cadeia da caráteres, respeitando este formato.
Decomposição em palavras
Se uma cadeia de caráteres representa uma sucessão de palavras chaves, pode ser necessário decompô-la em palavras individuais. Semelhantemente, se dados númericos foram armazenados num arquivo na forma de uma tabela com várias colunas e estamos lendo linha por linha, precisamos decompor cada linha em números individuais. A classe StringTokenizer do pacote java.util permite realizar este tipo de operação. Cria-se um objeto StringTokenizer associado à cadeia de caráteres a ser decomposta. O método countTokens conta o número de palavras (ou outros elementos) da cadeia. As palavras podem ser extraidas sucessivamente com o método nextToken. O método hasMoreTokens testa a presença de palavras ainda não separadas. Por exemplo:
String linhaDeDados = "0.225 4.32 6.17";
StringTokenizer separador = new StringTokenizer( linhaDeDados );
int i = separador.countTokens();
String n1 = separador.nextToken();
String n2 = separador.nextToken();
boolean b = separador.hasMoreTokens();
Após execução, o inteiro i vale 3; as cadeias de caráteres n1 e n2 contém "0.225" e "4.32", respectivamente. O booleano b possui o valor true, pois há ainda um número que não foi separado.
Listas
Construção
Listas ("Arrays") são utilizadas para agrupar variáveis do mesmo tipo. O tipo pode ser qualquer tipo primitivo ou qualquer classe de objetos. Para declarar que uma variável representa uma lista, acrescenta-se colchetes após o nome da variável. A lista é criada utilizando-se a palavra-chave new e indicando o número de elementos entre colchetes:
int c[];
c = new int[ 12 ];
double d[] = new double[ 30 ];
No caso de uma lista de tipos primitivos, a lista pode ser criada e inicializada através de uma lista de valores entre chaves:
int b[] = { 3, 5, 7, 9 };
O índice especificando um elemento de uma lista é contado a partir de 0. Por exemplo, o comando acima é equivalente ao seguinte:
int b[] = new int[ 4 ];
b[ 0 ] = 3;
b[ 1 ] = 5;
b[ 2 ] = 7;
b[ 3 ] = 9;
No caso de uma lista de objetos, deve-se notar que a criação da lista não cria os objetos. Cria simplesmente uma lista de referências que ainda não apontam para nada (null). Os objetos ainda precisam ser criados e as suas referências atribuidas aos elementos da lista. Por exemplo, o código abaixo cria 3 rótulos organizados numa lista:
Label llb[] = new Label[ 3 ];
llb[ 0 ] = new Label( "Vetor a" );
llb[ 1 ] = new Label( "Vetor b" );
llb[ 2 ] = new Label( "Produto" );
Comprimento
Qualquer lista possui uma variável inteira length que fornece o número de elementos da lista. Como exemplo de uso desta variável, podemos acrescentar ao trecho de código acima um laço que faz com que a cor de fundo dos três rótulos seja azul:
for( int i = 0; i < llb.length; i++){
llb[ i ].setBackground( Color.blue );
}
Referência e valor
Uma lista pode ser passada como argumento a um método. Deve-se notar que listas são objetos e, na linguagem Java, objetos são sempre passados aos métodos por referência, ao passo que tipos primitivos são sempre passados por valor. Isto quer dizer que uma modificação à lista realizada no método chamado implicará na mesma modificação à lista definida no programa que chamou o método em questão.
Indices múltiplos
Listas de múltiplos indices podem ser definidas, sendo consideradas como listas de listas. Os seguintes exemplos criam objetos que são essencialmente matrizes:
int b[][];
b = new int[ 2 ][ 3 ];
int c[][] = { { 1, 2, 3 }, { 4, 5, 6} };
Porém, listas de listas podem ser mais gerais de que matrizes, pois as "linhas" podem ter tamanhos diferentes:
int b[][];
b = int[ 2 ][ ];
b[ 0 ] = new int[ 2 ];
b[ 1 ] = new int[ 3 ];
int c[][] = { { 1, 2 }, { 3, 4, 5} };
Objetos e classes / Variáveis / Métodos
Classes
Uma classe é um modelo usado para definir vários objetos com características semelhantes. Um programa é constituído de uma classe ou de um conjunto de classes. Os elementos básicos de uma classe são chamados membros da classe e podem ser divididos em duas categorias:
1) As variáveis, que especificam o estado da classe ou de um objeto instância desta classe.
2) Os métodos, que especificam os mecanismos pelos quais a classe ou um objeto instância desta classe podem operar.
O esqueleto de uma classe apresenta-se da seguinte maneira:
class NomeDaClasse{
...
TipoDaVariahvel1 variahvel1;
TipoDaVariahvel2 variahvel2;
...
TipoDeRetorno1 mehtodo1(){
...
}
TipoDeRetorno2 mehtodo2(){
...
}
...
}
É claro que, além das variáveis mencionadas acima, que estão definidas fora de qualquer método, haverá em geral também variáveis definidas dentro de um determinado método e cujo escopo será limitado a este método, ou possivelmente a um sub-bloco deste método. Estas variáveis são chamadas locais. Em contradistinção, as variáveis das quais tratamos aqui são chamadas globais. Veja adiante nesta aula uma discussão um pouco mais completa desta distinção.
Para que uma instância de uma classe possa ser criada por qualquer outra classe, a classe em questão deve ser declarada pública, o que é feito acrescentando-se a palavra chave public à declaração da classe:
public class NomeDaClasse{
...
}
Classes que constituem aplicativos independentes e applets devem ser públicas. Uma classe que não for declarada pública somente poderá ser acessada por outras classes do mesmo pacote. Deve-se notar que cada arquivo fonte pode conter somente uma classe pública. Caso o arquivo contenha uma classe pública, ele deve possuir o mesmo nome que esta classe, com a terminação .java.
Objetos
Um objeto é uma instância de uma classe, ou seja uma realização concreta e particular da mesma. Um objeto precisa ser criado. Para que seja possível acessar as variáveis e os métodos de um objeto, é preciso atribuir uma referência ao objeto. O tipo de uma referência, ou seja a classe à qual pertence o objeto ao qual ela vai referir-se, precisa ser declarado.
Declaração: a seguinte instrução declara que a variável nomeDoObjeto refere-se a um objeto instância da classe NomeDaClasse:
NomeDaClasse nomeDoObjeto;
Criação: a seguinte instrução cria (em memória) um novo objeto instância da classe NomeDaClasse, que será referenciado pela variável nomeDoObjeto previamente declarada:
nomeDoObjeto = new NomeDaClasse();
As duas instruções acima podem ser combinadas numa só:
NomeDaClasse nomeDoObjeto = new NomeDaClasse();
Vale notar que a atribuição de uma referência a um objeto não é obrigatória. Há casos em qua basta criar o objeto e passá-lo como argumento de um método, com um comando do tipo
algumMehtodo( new AlgumaClasse() );
Variáveis
Variáveis de instância e variáveis de classe
Uma variável de instância é uma variável cujo valor é específico ao objeto e não à classe. Uma variável de instância em geral possui uma valor diferente em cada objeto representante da classe.
Uma variável de classe é uma variável cujo valor é comum a todos os objetos representantes da classe. Mudar o valor de uma variável de classe em um objeto automaticamente muda o valor para todos os objetos instâncias da mesma classe. Um exemplo óbvio de uma variável de classe seria o número de instâncias desta classe que já foram criadas.
Uma variável é considerada como de instância por "default". Para declarar uma variável de classe, acrescenta-se a palavra-chave static. Alias, outra expressão utilizada para indicar uma variável de classe é variável estática. Exemplo:
static int nuhmeroDeInstahnciasDestaClasse;
Restrições de acesso
Algumas palavras-chaves são diponíveis para ampliar ou restringir o acesso a uma variável. Estas palavras-chaves são acrescentadas à declaração da variável.
Uma variável que pode ser acessada por qualquer outra classe é dita pública, e é declarada usando-se a palavra-chave public.
Uma variável que pode ser acessada somente por métodos da própria classe é dita privada, e é declarada usando-se a palavra-chave private.
Uma variável que, além de poder ser acessada por métodos da própria classe, também pode ser acessada pelas subclasses da classe na qual ela é declarada, é dita protegida e é declarada usando-se a palavra-chave protected. [O conceito de subclasse será desenvolvido na próxima aula.]
Uma variável para a qual não foi especificada nenhuma destas palavras-chaves é dita amigável e pode ser acessada por todas as classes que pertencem ao mesmo pacote. Pacotes são agrupamentos de classes. Como já sabemos, as classes de biblioteca da SUN estão organizadas em pacotes. O programador também pode criar os seus próprios pacotes.
Os vários tipos de declaração de acesso estão exemplificados abaixo:
/* a classe definida neste arquivo pertence ao pacote chamado algumPacote */
package algumPacote;
public class NomeDaClasse{
/* a variável w é acessível por qualquer classe *
public int w;
/* a variável x é acessível pelos métodos da classe NomeDaClasse e das suas subclasses */
protected int x;
/* a variável y é acessível pelos métodos da classe NomeDaClasse e das outras classes que pertencem ao pacote chamado algumPacote */
int y;
/* a variável z é acessível somente pelos métodos da classe NomeDaClasse */
private int z;
...
}
As restrições de acesso desempenham um papel fundamental na programação orientada a objeto. Embora possa parecer mais simples e simpático permitir a qualquer objeto o acesso a todas as variáveis de qualquer outro objeto, isto resultaria em código muito pouco robusto. Recomenda-se, pelo contrário, limitar o acesso a qualquer variável o quanto for possível, dada a função da variável em questão. Mesmo no caso de variáveis que precisam ser acessíveis a todos os objetos, há uma alternativa preferível ao acesso irrestrito outorgado pela palavra chave public, como veremos na próxima seção.
Como um objeto acessa as variáveis de outro objeto
Supondo que as restrições de acesso discutidas acima o permitam, uma classe pode utilizar ou modificar uma variável pertencente a um objeto através do operador ponto.
Por exemplo, após declarar e criar o objeto nomeDoObjeto, representante da classe NomeDaclasse, podemos ir buscar a variável variahvel deste objeto e atribui-la à variável var da classe que estamos desenvolvendo:
TipoDaVariahvel var;
var = nomeDoObjeto.variahvel;
Evidentemente, var e variahvel devem ser do mesmo tipo (chamado TipoDaVariahvel no exemplo acima).
Também podemos atribuir à variável variahvel do objeto nomeDoObjeto o valor de uma variável var da nossa classe [entende-se aqui "valor" no sentido geral, podendo ser uma referência a um objeto]:
TipoDaVariahvel var = algumValor;
nomeDoObjeto.variahvel = var;
Se variahvel for uma referência a um outro objeto, pode-se concatenar operadores pontos para alcançar variáveis deste objeto:
TipoDaVariahvel var;
var = nomeDoObjeto.variahvel.outraVariahvel;
Neste caso, é claro, var e outraVariahvel devem ser do mesmo tipo.
Se a variável a ser acessada for uma variável de classe, pode-se usar a classe, em vez de uma instância particular, para acessar a variável:
TipoDaVariahvel var;
var = NomeDaClasse.variahvel;
Embora esta seja a maneira a mais direta de permitir que um objeto tenha acesso às variáveis de outro objeto, não é a mais segura, pois permite que um objeto modifique uma variável do outro sem restrição. Uma maneira mais segura consiste em declarar a variável como privada, mas incluir na classe métodos especiais controlando o acesso à variável:
public class NomeDaClasse{
private TipoDaVariahvel variahvel;
...
public TipoDaVariahvel getVariahvel(){
return variahvel;
}
public void setVariahvel( TipoDaVariahvel valor ){
/* aqui pode-se colocar testes para determinar se o valor é aceitável */
...
variahvel = valor;
}
}
Assim, pode-se colocar no método setVariahvel código para conferir que o valor passado como argumento é adequado. Omitindo o método setVariahvel, impedimos qua a variável seja modificada por qualquer outro objeto, embora ela possa ser lida chamando o método getVariahvel.
O operador ponto também serve para chamar os métodos de outro objeto (veja adiante nesta aula), de maneira que a classe que deseja manipular as variáveis do outro objeto conterá agora comandos do tipo:
TipoDaVariahvel var, varp;
var = nomeDoObjeto.getVariahvel();
...
nomeDoObjeto.setVariahvel( varp );
Esta técnica de programação é chamada encapsulação de variáveis.
Variáveis globais e variáveis locais
As variáveis discutidas acima são declaradas fora de qualquer método (usualmente no cabeçalho da classe) e são acessíveis por qualquer método da classe. Tais variáveis são chamadas globais. Muitas vezes, variáveis auxiliares são declaradas dentro de um determinado método, ou até dentro de um bloco menor. Tais variáveis são chamadas locais. Elas existem somente durante a execução daquele método ou bloco. A parte de código que "enxerga" uma determinada variável é chamada o escopo da variável. Assim, o escopo de uma variável global é a classe inteira, e o escopo de uma variável local é o método, ou um bloco contido dentro do método, ao qual ela pertence.
Exemplo:
class NomeDaClasse{
/* a variável abaixo é global */
TipoDaVariahvel variahvel1;
...
TipoDeRetorno nomeDoMehtodo()
{
/* a variável abaixo é local; está definida somente dentro deste método */
TipoDaVariahvel variahvel2;
for( int i = 0; i < 10; i++ ){
/* a variável i é local, definida só dentro deste bloco */
... //
}
}
}
Embora não pareça uma boa ideia, é possível dar a uma variável local um nome que já foi atribuído a uma variável global. Neste caso, a variável local "encobre" a variável global, mas o acesso à variável global é possível usando a palavra-chave this (que fornece uma referência ao próprio objeto) e o operador ponto:
class NomeDaClasse{
TipoDaVariahvel variahvel; // variável global
...
TipoDeRetorno nomeDoMehtodo()
{
TipoDaVariahvel variahvel; // variável local
...
/* a variável abaixo recebe o valor de variahvel local */
variahvel2 = variahvel;
/* a variável abaixo recebe o valor de variahvel global */
variahvel3 = this.variahvel;
}
}
Alguns programadores fazem uso desta construção em métodos set:
public class NomeDaClasse{
private TipoDaVariahvel variahvel;
public void setVariahvel( TipoDaVariahvel variahvel )
{
this.variahvel = variahvel;
}
}
Variáveis constantes
Esta expressão um tanto paradoxal refere-se a variáveis cujo valor não pode mudar durante a execução do programa. Tais variáveis são caracterizadas pela palavra-chave final, e por convenção recebem usualmente nomes escritos inteiramente em maiúsculas.
Por exemplo, na classe Math do pacote lang, encontramos
public static final double PI;
que contem o valor da famosa constante matemática.
Métodos
Valor de retorno e argumentos
Em geral, um método recebe argumentos cujos valores lhe são passados pelo objeto que o chamou, efetua um conjunto de operações e retorna algum resultado. A declaração do método especifica o nome do método, o tipo de retorno, o nome e o tipo de cada argumento. Os argumentos são variáveis locais do método em questão. O valor de retorno é devolvido utilizando-se a palavra chave return:
TipDeRetorno nomeDoMehtodo( TipoDoArg1 arg1, TipoDoArg2 arg2, ...){
TipoDeRetorno valorDeRetorno;
...
return valorDeRetorno;
}
Se o método não utiliza nenhum argumento, parénteses vazias devem ser incluídas na declaração.
Se o método não retorna nenhum valor, isto deve ser declarado usando-se a palavra-chave void:
void nomeDoMehtodo( TipoDoArg1 arg1, TipoDoArg2 arg2, ... ){
...
}
Métodos estáticos
Por "default", um método efetua uma determinada operação sobre um determinado objeto, ou seja uma instância da classe na qual o método está declarado. Existem métodos que realizam operações genéricas, não relativas a uma instância particular. Tais métodos são chamados estáticos e são declarados acrescentando-se a palavra-chave static à declaração do método.
Por exemplo, os métodos da classe Math do pacote lang, que realizam operações matemáticas sobre números, são estáticos:
public static int min( int a, int b ){
... // retorna o menor dos 2 inteiros a e b
}
Restrições de acesso
Algumas palavras-chaves são diponíveis para ampliar ou restringir o acesso a um método. Estas palavras-chaves são acrescentadas à declaração do método.
Um método que pode ser acessado por qualquer outra classe é dito público, e é declarado usando-se a palavra-chave public.
Um método que pode ser acessado somente por métodos da própria classe é dito privado, e é declarado usando-se a palavra-chave private.
Um método que, além de poder ser acessado por todas as classes do mesmo pacote, também pode ser acessado pelas subclasses da classe na qual ele é declarado, é dito protegido e é declarado usando-se a palavra-chave protected.
Um método para o qual não foi especificada nenhuma destas palavras-chaves é dito amigável e pode ser chamado por todas as classes que pertencem ao mesmo pacote.
Exemplo:
package algumPacote;
public class NomeDaClasse{
public int mehtodo1( ){
... // acessível por qualquer classe
}
protected int mehtodo2( ){
... // acessível por esta classe e suas subclasses
}
int mehtodo3( ){
... // acessível pelas classes deste pacote
}
private int mehtodo4( ){
... // acessível por esta classe
}
}
Como um objeto chama um método de outro objeto
Supondo que as restrições de acesso discutidas acima o permitam, uma classe que possui uma referência a um objeto pode chamar (executar) um método pertencente este objeto através do operador ponto":
/* o método abaixo não requer argumento e não retorna nada */
nomeDoObjeto.mehtodo1( );
/* o método abaixo requer dois argumentos e não retorna nada */
nomeDoObjeto.mehtodo2( var1, var2 );
/* o método abaixo não requer argumento e retorna um valor do tipo Resultado */
Resultado resultado = objeto.mehtodo3( );
Se a variável de retorno for um objeto, pode-se concatenar operadores pontos para chamar métodos deste objeto:
nomeDoObjeto.mehtodo3().mehtodo4();
Se o método for estático, usa-se a classe, em vez de uma instância particular, para chamar o método:
int x = Math.min( a, b );
Como criar um objeto - Método construtor
Para criar uma objeto, usa-se a palavra chave new, seguida de uma chamada ao método construtor, cujo nome é idêntico ao da classe:
NomeDaClasse nomeDoObjeto = new NomeDaClasse( );
O método construtor não precisa ser incluído explicitamente na classe, mas pode ser incluído para realizar tarefas no ato da criação. A sintaxe é:
class NomeDaClasse{
NomeDaClasse( ){
... // comandos executados na criação do objeto
}
}
ou, para uma classe pública:
public class NomeDaClasse{
public NomeDaClasse( ){
... // comandos executados na criação do objeto
}
}
O método construtor pode receber argumentos:
public class NomeDaClasse{
public NomeDaClasse( Tipo1 arg1, Tipo2 arg2 ){
... // comandos executados na criação do objeto
}
}
Valores para estes argumentos devem então ser passados ao construtor no comando de criação:
NomeDaClasse nomeDoObjeto = new NomeDaClasse( valorArg1, valorArg2 );
Programação orientada a objetos:
Aspectos fundamentais
Recursos adicionais
Aspectos fundamentais
Sobrecarga de métodos
É permitido incluir numa classe métodos que possuem o mesmo nome e o mesmo tipo de retorno, mas que diferem pelo número e/ou pelos tipos dos argumentos.
Qual destes métodos é executado depende do número e/ou dos tipos dos argumentos passados pelo programa que chama o método. Esta técnica é chamada sobrecarga ("overloading") de métodos. Ela é frequentemente utilizada, por exemplo para definir vários construtores para uma determinada classe.
Exemplo:
public class Ponto{
protected double x, y;
public Ponto(){
setPonto( 0, 0 );
}
public Ponto( double a, double b ){
setPonto( a, b );
}
public void setPonto( double a, double b ){
x = a;
y = b;
}
public String emPalavras(){
return "[" + x + ", " + y + "]";
}
}
Herança
Pode-se criar uma nova classe, acrescentando recursos a uma classe já construída, ou modificando alguns recursos desta classe. A nova classe herda (possivelmente com modificações) os recursos já disponíveis na classe anterior.
A classe herdeira é chamada subclasse da classe anterior, que é a sua superclasse.
Este processo de herança pode ser repetido em cascata, criando várias gerações de classes.
Vale notar que a linguagem Java permite somente herança simples, i.e. uma classe pode herdar diretamente de uma única classe (possui uma única superclasse direta). Em contrapartida, uma classe pode possuir várias subclasses diretas.
A relação de herança é indicada através da palavra-chave extends. Por "default", uma classe herda da classe Object.
Exemplo: O exemplo abaixo cria uma subclasse da classe definida no exemplo anterior:
public class Circulo extends Ponto{
protected double r;
public Circulo(){
/* aqui ocorre uma chamada implicita ao construtor da superclasse */
setRaio( 0 );
}
public Circulo( double a, double b, double r ){
super( a, b );
setRaio( r );
}
public void setRaio( double r ){
this.r = r;
}
}
No exemplo acima, as variáveis x e y da superclasse são acessíveis à subclasse pois foram declaradas protected.
A palavra-chave super permite referência à superclasse, e em especial, se for utilizada como nome de método, ao construtor da superclasse.
Nota-se que se não houver, no construtor da subclasse, nenhuma chamada explicita ao construtor da superclasse, o construtor sem argumento é chamado por "default". Se for incluída uma chamada ao construtor da superclasse, ela deve ser o primeiro comando executado no construtor da subclasse.
Superação de métodos
Na construção de uma subclasse a partir de uma superclasse, pode-se também substituir um método já presente na superclasse por outro, de mesmo nome, mesmo tipo de retorno, igual número e mesmos tipos de argumentos. Este procedimento é chamado "overriding" em inglés, o que será traduzido aqui por superação.
Exemplo: na classe Circulo acima, podemos substituir o método emPalavras por uma versão incrementada cujo valor de retorno inclui também o raio do círculo:
public class Circulo extends Ponto{
protected double r;
public Circulo(){
setRaio( 0 );
}
public Circulo( double a, double b, double r ){
super( a, b );
setRaio( r );
}
public void setRaio( double r ){
this.r = r;
}
public String emPalavras(){
return "[" + x + ", " + y + ", " + r + "]";
}
}
Vale notar que o método superado ainda pode ser utilizado, caso for necessário, fazendo-se uso da palavra-chave super. Por exemplo, uma possível alternativa à construção acima (produzindo porém um resultado ligeiramente diferente) seria:
public class Circulo extends Ponto{
protected double r;
public Circulo(){
setRaio( 0 );
}
public Circulo( double a, double b, double r ){
super( a, b );
setRaio( r );
}
public void setRaio( double r ){
this.r = r;
}
public String emPalavras(){
return super.emPalavras() + "[" + r + "]";
}
}
Polimorfismo
Já que uma subclasse herda as propriedades da superclasse, uma instância de uma subclasse é também uma instância da sua superclasse (e da superclasse desta, etc... até a classe raiz Object).
Assim, no exemplo acima, um Circulo também é um Ponto e uma instância de Circulo pode ser atribuída a uma referência declarada como Ponto.
Exemplo:
import java.awt.Label;
import java.applet.Applet;
public class Test extends Applet{
private Ponto meusPontos[];
private Label rohtulos[];
public void init(){
meusPontos = new Ponto[ 2 ];
meusPontos[ 0 ] = new Ponto( 3, 5 );
meusPontos[ 1 ] = new Circulo( 15, 5, 1.5 );
rohtulos = new Label[ 2 ];
for( int i = 0; i < meusPontos.length; i++ )
{
rohtulos[ i ] = new Label( meusPontos[ i ].emPalavras() );
add( rohtulos[ i ] );
}
}
}
Neste exemplo, apesar de ambos objetos serem declarados como Ponto, o segundo é de fato uma instância da subclasse Circulo e portanto, quando for chamado o método emPalavras deste objeto, será executado o método da classe Circulo (que retorna informação também sobre o raio). Este recurso da linguagem é chamado polimorfismo, pois ele faz com que objetos declarados como instâncias de uma classe (no caso, a classe Ponto) possam assumir diversas formas (no caso, um deles é um mero Ponto e o outro é um Circulo, que é de verdade um ponto incrementado).
Se uma instância de uma subclasse for atribuída a uma referência do tipo da superclasse, somente os métodos que já estão definidos na superclasse podem ser chamados como descrito acima. Para chamar um método definido na subclasse, mas não na superclasse, é necessário efetuar uma coerção explicita. Por exemplo, se acrescentarmos à classe Circulo um método para calcular a área, ou seja,
public class Circulo extends Ponto{
protected double r;
public Circulo(){
setRaio( 0 );
}
public Circulo( double a, double b, double r ){
super( a, b );
setRaio( r );
}
public void setRaio( double r ){
this.r = r;
}
public double ahrea( ){
return Math.PI * r * r;
}
public String emPalavras(){
return "[" + x + ", " + y + ", " + r + "]";
}
}
devemos usar a coerção para chamar este método numa instância de Circulo que foi atribuída a uma referência de tipo Ponto:
import java.awt.Label;
import java.applet.Applet;
public class Test extends Applet{
private Ponto meusPontos[];
private Label rohtulos[];
public void init(){
meusPontos = new Ponto[ 2 ];
meusPontos[ 0 ] = new Ponto( 3, 5 );
meusPontos[ 1 ] = new Circulo( 15, 5, 1.5 );
rohtulos = new Label[ 2 ];
for( int i = 0; i < meusPontos.length; i++ ){
rohtulos[ i ] = new Label( meusPontos[ i ].emPalavras() );
add( rohtulos[ i ] );
}
double a = ( ( Circulo ) meusPontos[ 1 ] ).ahrea();
rohtulos[ 1 ].setText( rohtulos[ 1 ].getText() + "[" + a + "]" );
}
}
Em linguagens orientadas a objeto, a palavra polimorfismo é também usada num sentido um tanto diferente, qual seja para referir-se às várias "formas" que pode tomar uma classe que herda de várias outras. Embora Java não permite herança múltipla, ele oferece um recurso alternativo, chamado interface, que possibilita este tipo de polimorfismo (veja abaixo).
Recursos adicionais
Interfaces
Como já mencionado, Java não permite que uma classe seja herdeira dos recursos de mais de uma outra classe. Esta limitação é imposta para evitar que o programador possa cometer erros sutís e perigosos, caso ele quiser construir uma classe que herde de duas outras que possuem um método de mesmo nome (e tipos de argumentos), mas com implementações diferentes nas duas classes. Neste caso, haveria ambiguidade no funcionamento do método herdado. Do outro lado, a herança múltipla é um recurso poderoso, cujo total abandono limitaria bastante a linguagem.
A saida encontrada é o conceito de interface, que nada mais é que um conjunto de declarações de métodos (nome, tipo de retorno, tipos dos argumentos) desprovidos de implementação. Cabe ao programador que deseja implementar a interface em questão providenciar uma implementação destes métodos na classe que ele está desenvolvendo.
Desta forma, além de estender alguma superclasse, a classe em desenvolvimento pode implementar várias interfaces.
A palavra chave implements é utilizada para indicar que uma classe implementa uma determinada interface.
Exemplo: definimos a interface:
interface Forma{
public double ahrea();
public String emPalavras();
}
Podemos então declarar que a classe Circulo construida acima implementa esta interface:
public class Circulo extends Ponto implements Forma{
protected double r;
public Circulo(){
setRaio( 0 );
}
public Circulo( double a, double b, double r ){
super( a, b );
setRaio( r );
}
public void setRaio( double r ){
this.r = r;
}
public double ahrea( ){
return Math.PI * r * r;
}
public String emPalavras(){
return "[" + x + ", " + y + ", " + r + "]";
}
}
Poderiamos definir uma outra classe que implementa a mesma interface:
public class Quadrado extends Ponto implements Forma{
protected double l;
public Quadrado(){
setLado( 0 );
}
public Quadrado( double a, double b, double l ){
super( a, b );
setLado( l );
}
public void setLado( l ){
this.l = l;
}
public double ahrea( ){
return l * l;
}
public String emPalavras(){
return "[" + x + ", " + y + ", " + l + "]";
}
}
No exemplo acima, um objeto de tipo Circulo é também uma instância de uma Forma. O mesmo é verdade de um objeto de tipo Quadrado. O uso deste polimorfismo está ilustardo no exemplo abaixo:
import java.awt.Label;
import java.applet.Applet;
public class Test extends Applet{
private Forma minhasFormas[];
private Label rohtulos[];
public void init(){
minhasFormas = new Forma[ 2 ];
minhasFormas[ 0 ] = new Circulo( 3, 5, 1.5 );
minhasFormas[ 1 ] = new Quadrado( 15, 5, 2.5 );
rohtulos = new Label[ 2 ];
for( int i = 0; i < minhasFormas.length; i++ ){
String descr = minhasFormas[ i ].emPalavras();
double ar = minhasFormas[ i ].ahrea();
rohtulos[ i ] = new Label( descr + "; área: " + ar );
add( rohtulos[ i ] );
}
}
}
Classes abstratas
A linguagem Java ainda oferece um meio termo entre uma classe na qual está providenciada uma implementação para todos os métodos declarados, e uma interface na qual nenhum dos métodos declarados está implementado. Trata-se da classe abstrata, na qual alguns dos métodos declarados estão implementados e outros não.
Uma classe abstrata deve ser declarada tal usando-se a palavra chave abstract.
Uma classe abstrata não pode ser instânciada diretamente. Pode-se construir subclasses da classe abstrata, nas quais os métodos declarados mas ainda não implementados devem receber uma implementação.
Exemplo: uma alternativa ao esquema do exemplo anterior seria definir uma classe abstrata Forma:
public abstract class Forma extends Ponto{
abstract double ahrea( );
}
Podemos então definir as classes Circulo e Quadrado como subclasses concretas desta classe abstrata:
public class Circulo extends Forma{
protected double r;
public Circulo(){
setRaio( 0 );
}
public Circulo( double a, double b, double r ){
super( a, b );
setRaio( r );
}
public void setRaio( double r ){
this.r = r;
}
public double ahrea( ){
return Math.PI * r * r;
}
public String emPalavras(){
return "[" + x + ", " + y + ", " + r + "]";
}
}
public class Quadrado extends Forma{
protected double l;
public Quadrado(){
setLado( 0 );
}
public Quadrado( double a, double b, double l ){
super( a, b );
setLado( l );
}
public void setLado( l ){
this.l = l;
}
public double ahrea( ){
return l * l;
}
public String emPalavras(){
return "[" + x + ", " + y + ", " + l + "]";
}
}
Aula 6
Componentes gráficas
Passeio pela documentação sobre componentes gráficas
Tratamento de eventos
Gerenciadores de disposição
Passeio pela documentação
A seguir listamos as componentes gráficas mais usuais encontradas no pacote java.awt. Limitamo-nos alguns poucos comentários, em especial a respeito dos eventos gerados por estas componentes, cujo tratamento será detalhado na seção seguinte.
Component
Uma classe abstrata, superclasse de todas as componentes gráficas.
Container
Uma classe abstrata, definindo propriedades gerais de componentes que podem conter outras componentes.
Panel
O tipo o mais simples de Container. Vale notar que a classe Applet é subclasse de Panel.
Canvas
Uma classe utilizada como fundo para pintar. O método paint deve ser sobre-escrito para realizar a pintura desejada.
Label
Um rôtulo, i.e. uma linha de texto que fornece informação ao usuário. O texto pode ser modificado pelo programa mas não pelo usuário diretamente.
Button
Um botão simples. A ação normalmente realizada pelo usuário é clicar, o que gera um evento do tipo ActionEvent. O objeto encarregado de tratar este evento (i.e. fazer o que o programador deseja que seja feito caso o usuário clicar o botão) deve implementar a interface ActionListener. O método addActionListener do botão deve ser chamado para registrar este objeto como "ouvidor de eventos" do botão. A referência ao ouvidor em questão é passada com argumento do método.
TextComponent
Esta é a superclasse das classes destinadas a tratar texto (veja o dois itens seguintes). Se o texto for editável, a digitação ou alteração do mesmo gera eventos do tipo TextEvent. O objeto encarregado de tratar estes eventos deve implementar a interface TextListener. O método addTextListener da componente de texto deve ser chamado para registrar este objeto como "ouvidor de eventos" da componente. A referência ao ouvidor em questão é passada com argumento do método.
TextField
Subclasse de TextComponent definindo um campo de texto, i.e. uma única linha de texto editável. Além dos eventos do tipo TextEvent discutidos acima, um campo de texto pode também gerar eventos do tipo ActionEvent, por exemplo quando aperta-se a tecla Enter. Tais eventos são tratados seguindo o modelo descrito para o botão.
TextArea
Subclasse de TextComponent definindo uma área de texto, i.e. mais de uma linha de texto editável.
Choice
Uma componente permitindo a escolha entre vários itens de uma lista. Um clique do mouse em cima de um pequeno botão faz aparecer a lista. Um item da lista pode ser selecionado com o mouse. Isto gera um evento do tipo ItemEvent. O objeto encarregado de tratar estes eventos deve implementar a interface ItemListener. A classe Choice implementa a interface ItemSelectable, que define alguns métodos relacionado com escolha, em especial o método addItemListener, que deve ser chamado para registrar o "ouvidor de eventos de escolha de item" da componente Choice. A referência ao ouvidor em questão é passada como argumento do método.
Checkbox
Este tipo de botão permite uma escolha do tipo sim/não e vem acompanhado de alguma marca indicando a opção. Vários botões deste tipo podem ser agrupados num objeto CheckboxGroup. Neste caso, somente um dos botões pode estar selecionado (opção sim), os outros estando então automaticamente deselecionados (opção não). Botões assim agrupos são usualmente chamados botões de rádio. A classe Checkbox implementa a interface ItemSelectable e os seus eventos, do tipo ItemEvent, são tratados como descrito no item anterior.
List
Uma lista de itens entre os quais um, ou vários, podem ser selecionados. Uma barra de rolagem aparece automaticamente se o número de itens for maior que o número de linhas visíveis. Esta classe também implementa a interface ItemSelectable. Um evento do tipo ItemEvent é gerado por um clique simples do mouse. Um evento do tipo ActionEvent é gerado por um clique duplo do mouse. Assim, esta componente pode utilizar dois ouvidores de eventos, um ItemListener e um ActionListener. Evidentemente, estas duas funcionalidades podem ser incorporadas numa única classe que implementa ambos interfaces.
Scrollbar
Uma barra de rolagem que permite "rolar" um cursor sobre um dado intervalo de valores inteiros. A rolagem é realizada arrastando o cursor com o mouse ou clicando. Isto gera eventos do tipo AdjustmentEvent. O objeto encarregado de tratar estes eventos deve implementar a interface AjustmentListener. A classe Scrollbar implementa a interface Adjustable, que define alguns métodos relacionado com rolagem, em especial o método addAdjustmentListener, que deve ser chamado para registrar o "ouvidor de eventos de rolagem" da componente Scrollbar. A referência ao ouvidor em questão é passada como argumento do método.
ScrollPane
Esta classe herda de Container pode conter uma única componente, que pode ser rolada horizontalmente e verticalmente. As barras de rolagem são partes intregrantes da componente.
Frame
Um janela com uma barra de título e uma borda. Esta classe herda da classe Window, que representa o tipo mais rudimentar de janela e herda por sua vez da classe Container. A classe Frame implementa a interface MenuContainer, que permite que ela receba uma barra de menu. Os eventos associados à manipulação de uma janela (abertura, fechamento, iconização, redimensionamento...) são do tipo WindowEvent. O objeto encarregado de tratar estes eventos deve implementar a interface WindowListener. O método addWindowListener, da superclasse Window, deve ser chamado para registrar o "ouvidor de eventos de janela" da componente Frame. A referência ao ouvidor em questão é passada como argumento do método.
Dialog
Uma caixa de diálogo é uma janela com uma barra de título, usualmente utilizada para permitir a entrada de dados ou para fornecer informação ao usuário. Uma caixa de diálogo pode ser modal ou não. Se ela for modal, nenhuma outra janela pode ser acessada enquanto a caixa de diálogo estiver aberta.
MenuComponent
As componentes associadas a menus não herdam da classe Component e sim da classe MenuComponent, uma classe abstrata. As subclasses diretas de MenuComponent são MenuBar e MenuItem.
MenuBar
Uma barra de menu, que pode ser acrescentada a um Frame, passando-a como argumento do método setMenuBar deste frame. Menus podem ser acrescentados à barra chamando-se o método add da mesma.
Menu
Um menu, que pode ser acrescentado a uma barra de menu ou, tratando-se de um submenu, a outro menu. Já que um objeto deste tipo pode ser acrescentado a um menu, a classe Menu herda da classe MenuItem descrita abaixo. Um item pode ser acrescentado a um menu passando-o como argumento do método add do mesmo.
MenuItem
Uma item de menu, que pode ser acrescentada a um Menu, passando-a como argumento do método add deste menu. Um clique em cima de um item de menu gera um evento do tipo ActionEvent, cujo tratamento já foi descrito acima.
CheckboxMenuItem
Subclasse de MenuItem que fornece um item de menu que permite uma escolha do tipo sim/não e mostra uma marca especial se a selação for sim. Um clique em cima de um item de menu deste tipo gera um evento do tipo ItemEvent, cujo tratamento já foi descrito acima.
PopupMenu
Subclasse de Menu que fornece um menu contextual, i.e. um menu que aparece quando o botão direito do mouse é clicado. O clique produz ume evento do tipo MouseEvent. o objeto encarregado de tratar este evento deve implementar a interface MouseListener. Deve comandar a aparição da caixa de diálogo chamando o método show da mesma.
Tratamento de eventos
O tratamento de eventos tipicamente segue o padrão seguinte:
O objeto responsável pelo tratamento dos eventos ("ouvidor") deve implementar uma determinada interface característica do tipo de eventos a serem tratados. Lembramos que uma interface é uma prescrição de um conjunto de métodos (de nome, acesso, valor de retorno e tipos de argumentos definidos) que devem ser todos implementados pela classe que implementa a interface. Métodos que não vão ser utilizados devem receber uma implementação trivial (não fazer nada). Os tipos de eventos e as interfaces de tratamento de eventos são definidos no pacote java.awt.event. Vale notar que existem classes de biblioteca, também definidas neste pacote, que fornecem implementações triviais destas interfaces. Estas classes são chamadas adaptadores (Adapter). Uma alternativa à implementação da interface é a criação de uma subclasse do adaptador na qual os métodos que vão ser utilizados são sobre-escritos. Lembramos porém que uma classe só poder herdar de uma única classe, embora possa implementar várias interfaces.
O objeto responsável pelo tratamento dos eventos deve ser registrado como "ouvidor" junto à componente que vai receber os eventos.
O tipos de eventos e interfaces de tratamento correspondentes, eoncontrados nas componentes listadas acima são:
ActionEvent - ActionListener
public void actionPerformed( ActionEvent e )
{
// ação a ser realizada
}
TextEvent - TextListener
public void textValueChanged( TextEvent e )
{
// ação a ser realizada se o texto foi mudado
}
ItemEvent - ItemListener
public void itemStateChanged( ItemEvent e )
{
// ação a ser realizada se um novo item foi selecionado
}
AdjustmentEvent - AdjustmentListener
public void adjustmentValueChanged( AdjustmentEvent e )
{
// ação a ser realizada se houve rolagem
}
WindowEvent - WindowListener
public void windowActivated( WindowEvent e )
{
// ação a ser realizada se a janela foi ativada
}
public void windowClosed( WindowEvent e )
{
// ação a ser realizada se a janela foi fechada
}
public void windowClosing( WindowEvent e )
{
// ação a ser realizada se a janela está sendo fechada
}
public void windowDeactivated( WindowEvent e )
{
// ação a ser realizada se a janela foi desativada
}
public void windowDeiconified( WindowEvent e )
{
// ação a ser realizada se a janela foi de-iconificada
}
public void windowIconified( WindowEvent e )
{
// ação a ser realizada se a janela foi iconificada
}
public void windowOpened( WindowEvent e )
{
// ação a ser realizada se a janela foi aberta
}
MouseEvent - MouseListener
public void mouseClicked( MouseEvent e )
{
// ação a ser realizada se o mouse foi clicado
}
public void mousePressed( MouseEvent e )
{
// ação a ser realizada se o botão do mouse foi apertado
}
public void mouseReleased( MouseEvent e )
{
// ação a ser realizada se o botão do mouse foi solto
}
public void mouseEntered( MouseEvent e )
{
// ação a ser realizada se o mouse entrou em cima da componente
}
public void mouseExited( MouseEvent e )
{
// ação a ser realizada se o mouse sai de cima da componente
}
Movimentos do mouse também ser tratados por
MouseEvent - MouseMotionListener
public void mouseDragged( MouseEvent e )
{
// ação a ser realizada se o mouse foi arrastado (com botão apertado)
}
public void mouseMoved( MouseEvent e )
{
// ação a ser realizada se o mouse foi movido (com botão solto)
}
Gerenciadores de disposição
O pacote java.awt fornece classes destinadas a organizar as componentes gráficas sobre a tela. Estas classes implementam a interface LayoutManager (ou a interface LayoutManager2), que será traduzido aqui em "gerenciador de disposição". Um gerenciador de disposição é atribuido a uma instância de um classe que pode conter componentes (i.e. uma subclasse da classe Container), chamando-se o método setLayout deste Container, com a referência ao LayoutManager desejado passada como argumento.
Os gerenciadores de disposição mais comuns são listados abaixo.
FlowLayout
Este é o tipo mais básico de gerenciador de disposição. As componentes são colocadas sucessivamente da esquerda para a direita sobre a mesma linha até que não haja mais espaço suficiente. Passa-se então a linha seguinte. É possível especificar o alinhamento das componentes numa linha como centrado, a esquerda ou a direita. É possível também especificar um intervalo horizontal e vertical entre as componentes. Este é o gerenciador de disposição "default" das classes Panel e Applet.
GridLayout
Este gerenciador de disposição divide o Container numa grade que organiza as componentes em linhas e colunas. As componentes são adicionadas linha por linha, da esquerda para a direita. É possível especificar uma intervalo horizontal e vertical entre as componentes.
BorderLayout
Este gerenciador de disposição divide o Container em cinco regiões: central, norte, sul, leste e oeste. As regiões norte e sul extendem-se sobre toda a largura do Container e possuem a altura das componentes que elas contem. As regiões leste e oeste extendem-se em altura sobre toda a região entre as regiões norte e sul. Elas possuem a largura das componentes que elas contém. A região central ocupa o espaço que sobra. É possível definir um intervalo horizontal e vertical entre as componentes. Este é o gerenciador de disposição "default" das classes Window e Frame.
CardLayout
Este gerenciador de disposição arranja as componentes na forma de um baralho, de maneira que uma só das componentes está visível num determinado momento, as outras estando escondidas. Evidentemente, é necessário incluir um dispositivo (por exemplo, um ou vários botões) que permita mudar a componente visível. O ouvidor de eventos deste dispositivo chamará métodos do objeto CardLayout para efetuar esta operação. Os métodos first e last selecionam respectivamente a primeira e a última componente do baralho. Os métodos next e previous selecionam respectivamente a componente seguinte e a componente anterior. A referência ao Container deve ser passada como argumento a estes métodos. Quando uma componente é acrescentada ao Container, pode-se passar ao método add deste, além da referência à componente, um nome para esta (na forma de uma cadeia de caráteres). Isto possibilita ao ouvidor de eventos ir buscar uma componente qualquer dentro do baralho, através de uma chamada ao método show do CardLayout, método este que recebe como argumentos a referência ao Container e o nome da componente.
Aula 7
Animação
Linhas de execução
Animação num applet - Interface "Runnable"
Eliminação da tremulação - Bufferização dupla
Linhas de execução
Para controlar o fluxo de execução de um programa, utiliza-se uma (ou várias) linha de execução que, em Java, é representada por uma instância da classe Thread, encontrada no pacote java.lang. Os métodos desta classe, de maior relevância para o controle de uma animação simples, são:
public void run()
Especifica as operações a serem realizadas pela linha de execução. Este método deve ser sobre-escrito pelo programador, codificando as operações em questão.
start()
Deslancha a execução da linha de execução; não pode ser sobre-escrito pelo programador.
suspend()
Suspende a execução da linha de execução; não pode ser sobre-escrito pelo programador.
resume()
Retoma a execução de uma linha de execução que foi suspensa; não pode ser sobre-escrito pelo programador.
stop()
Encerra a execução da linha de execução; não pode ser sobre-escrito pelo programador.
sleep( long tempo )
Este é um método estático, ou seja cuja chamada afeta todas as instâncias da classe. O resultado da chamada é que todas as linhas de execução "dormem" durante o intervalo especificado pelo argumento, em milisegundos. Este método pode "lançar" uma exceção do tipo InterruptedException que deve ser capturada ou relançada pelo objeto que efetua a chamada. O lançamento e a captura de exceções é assunto para uma próxima aula, mas o código necessário no caso discutido aqui pode ser encontrado no exemplo abaixo.
Outros métodos, de interesse para o desenvolvimento de programas contendo várias linhas de execução, serão discutidos numa outra aula.
Animação num applet - Interface "Runnable"
Como visto acima, o método mais geral de criar uma linha de execução consiste em construir uma subclasse da classe Thread, na qual o método run é sobre-escrito para realizar as operações desejadas. Porém, como em Java uma classe só pode herdar de uma única superclasse, a classe assim construída não poderia simultaneamente ser um applet ou outro tipo de componente gráfica. Esta complicação pode ser contornada com o uso da interface Runnable, que é definida como contendo um único método, exatamente o método public void run(). Pode-se então criar uma instância da classe Thread, passando como argumento do construtor uma referência ao objeto Runnable que implementa a interface. Este objeto passa então a atuar como "alvo" da linha de execução, o que significa que o método run() associado à linha de execução é na verdade aquele implementado no objeto Runnable. Por exemplo, no caso de um applet:
public class MeuApplet extends Applet implements Runnable
{
Thread meuThread;
public void init();
{
// o próprio applet é o objeto Runnable alvo da linha de execução
meuThread = new Thread( this );
...
}
public void start();
{
// iniciar a execução
meuThread.start();
...
}
// este método é executado pela linha de execução
public void run();
{
// aqui ficaria o laço de animação
while( true )
{
... // mexer os elementos da imagem;
repaint(); // repintar
try
{
// paradinha do processador
Thread.sleep( 20 );
}
catch( InterruptedException e )
{
// o que houve?
showStatus( e.toString() ) ;
}
}
}
public void stop();
{
// encerrar a execução
meuThread.stop();
...
}
}
Note que a estrutura acima é só um exemplo muito rudimentar. Em especial, ele produz uma animação que começa tão logo o applet for carregado pelo navegador e só é encerrada quando o applet parar (normalemente quando o usuário navegar para outra página). Um controle mais sofisticado pode ser obtido com chamadas aos métodos start, stop, suspend e resume do Thread geradas por eventos produzidos pelo usuário (por exemplo cliques sobre botões).
Eliminação da tremulação - Bufferização dupla
O laço de animação normalmente contem uma chamada ao método repaint da componente gráfica que apresenta a animação. Lembramos que este método chama o método update que por sua vez chama o método paint.
O método update, por default, limpa a tela da animação. Isto pode ser indesejável, por exemplo no caso de uma animação que pretende mostrar a elaboração progressiva de uma imagem final. Neste caso, o método update deve ser sobre-escrito para evitar a limpeza da tela. A forma mínima de tal método seria
public void update( Graphics g );
{
paint( g );
}
Mesmo nos casos nos quais a imagem precisa ser inteiramente repintada a cada passo da animação, o método update precisa ser sobre-escrito para obter uma animação de boa qualidade. Isto porque a operação de limpar e repintar continuamente a imagem, se feita diretamente sobre a tela do computador, produz uma tremulação no mínimo desagradável. A solução, chamada "bufferização dupla" consiste em utilizar uma imagem auxiliar "fora da tela", para fazer a operação de limpeza e repintagem. Esta imagem é transferida para a tela quando estiver pronta. A imagem auxiliar é uma instância da classe Image do pacote java.awt, criada através de uma chamada ao método createImage da componente gráfica. O método drawImage do contexto gráfico da tela é usado para transferir a imagem para a mesma. Eis o código que deve ser incluído na componente gráfica para tanto:
private Image offScreenImage; // imagem auxiliar declarada na classe
public void update( Graphics g )
{
// Criar a imagem auxiliar e buscar o seu context gráfico
if( offScreenImage == null )
offScreenImage = createImage( getSize().width, getSize().height );
Graphics offScreenGraphics = offScreenImage.getGraphics();
// limpar a imagem auxiliar
offScreenGraphics.setColor( getBackground() );
offScreenGraphics.fillRect( 0, 0, getSize().width, getSize().height );
// pintar a imagem auxiliar
offScreenGraphics.setColor( g.getColor() );
paint( offScreenGraphics );
// transferir a imagem auxiliar para a tela
g.drawImage( offScreenImage, 0, 0, this );
offScreenGraphics.dispose();
}
Aula 8
Tratamento de exceções
Considerações gerais
Classes Throwable e Exception
Lançamento de exceções
Captura de exceções
Considerações gerais
Exceções são situações excepcionais e geralmente indesejáveis que podem ocorrer durante a execução de um programa. Exceções podem ser tratadas incluindo-se código adequado no programa; não são portanto erros fatais.
Exemplos típicos de exceções são:
Índice de uma lista (Array) fora do intervalo permitido.
Problemas em operações aritméticas, tais como "overflows" e divisões por zero.
Argumentos inválidos numa chamada a um método.
Uso de uma referência que não aponta para nenhum objeto.
Falta de memória (relativamente improvável em Java graças ao coletor de lixo).
O modelo de tratamento de exceções Java permite tratar uma exceção num escopo (bloco de código) diferente daquele que gerou a exceção. Isto permite uma melhor organização do código.
Naturalmente, exceções são objetos em Java. A classe Exception é a superclasse de todas as exceções. Existem vários tipos de exceções já definidos nas bibliotecas. O programador pode também construir as suas próprias exceções.
Muitas classes de biblioteca possuem métodos que podem gerar ("lançar") exceções. Estas exceções podem ser tratadas ("capturadas") por código escrito pelo programador. Além disto, métodos escritos pelo programador também podem lançar exceções, tanto de tipos definidos nas bibliotecas como de novos tipos construidos pelo programador.
Classes Throwable e Exception
Objetos que podem ser lançados para indicar que algo anormal aconteceu, e capturados para lidar com esta situação, devem estender a classe Throwable do pacote java.lang. Os métodos mais úteis desta classe são:
public String getMessage()
// retorna uma mensagem de erro
public void printStackTrace()
// imprime uma descrição da pilha no instante em que o problema ocorreu
Como já mencionado, a superclasse de todas as exceções é a classe Exception do pacote java.lang. Conforme indicado acima, esta classe é uma subclasse da classe Throwable. Existem um grande número de subclasses de Exception espalhadas pelos vários pacotes de biblioteca. O programador pode estender a classe Exception ou uma das suas subclasses para construir as suas próprias exceções.
Nem todos os problemas que podem ocorrer são considerados exceções. Problemas mais sérios, que em geral não podem ser tratados pelo programador, são chamados erros e são objetos da classe Error do pacote java.lang (ou das subclasses desta classe, que também estão espalhadas pelos vários pacotes). Erros não serão considerados aqui.
Lançamento de exceções
Uma exceção é lançada usando-se a palavra chave throw seguinda da referência à exceção. Exemplo:
Exception opa = new Exception( "deu zebra" );
...
if( temProblema ) throw opa;
Se o programador desejar que a exceção assim lançada seja tratada fora do método que a gerou, ele deve explicitar isto usando a palavra chave throws seguida do tipo de exceção, na declaração do método. Por exemplo, o código acima estaria inserido no corpo de um método declarado como segue:
TipoDeRetorno nomeDoMetodo( ) throws Exception
{
// aqui vai o código acima
}
Na verdade, existe uma classe de exceções, RuntimeException (do pacote java.lang) que não precisam ser listadas explicitamente após a palavra chave throws. Correspondentemente, não é mandatório para o programador incluir código para manipular tais exceções.
Uma vez que a exceção foi lançada, a execução do método é interrompida e o controle volta ao objeto que chamou este método. Este objeto deve capturar a exceção como descrito a seguir, ou eventualmente relançar a exceção para que ela seja capturada mais alto na hierarquia das chamadas de métodos.
Captura de exceções
Para capturar uma exceção, é necessário montar a seguinte estrutura de código.
O código que pode lançar a exceção deve ser inserido num bloco precedido da palavra chave try. O processador então tentará executar o bloco, até que eventualmente uma exceção seja lançada, seja por um comando contido dentro do bloco, seja por um método chamado dentro do bloco.
O bloco try descrito acima deve ser seguido de um bloco que será executado caso houver de fato lançamento de uma exceção do tipo em questão. Este bloco deve ser anunciado pela palavra chave catch seguida (entre parénteses) do tipo de exceção em questão. Se vários tipos de exceções podem ser lançadas no bloco try, deve-se fornecer um bloco catch para cada tipo de exceção. Se uma exceção do tipo em questão for lançada no bloco try, o bloco try é encerrado e a execução salta para o bloco catch apropriado. Os blocos catch são chamados manipuladores de exceções.
Ainda é possível acrescentar, após o(s) bloco(s) catch, um bloco precedido da palavra chave finally, que será executado em todos se casos, após a execução completa do bloco try ou após a execução de um bloco catch, conforme o caso.
Resumindo, a estrura é:
try
{
// aqui vai código que pode gerar exceções dos tipos
// ExceptionType1 e ExceptionType2
}
catch( ExceptionType1 opa1 )
{
// aqui vai código para lidar com uma exceção do tipo
// ExceptionType1
}
catch( ExceptionType2 opa2 )
{
// aqui vai código para lidar com uma exceção do tipo
// ExceptionType2
}
finally
{
// aqui vai código que deve ser executado em qualquer caso
}
Quando ocorre uma exceção, os blocos catch são examinados sucessivamente até que o argumento corresponda ao tipo da exceção. Note que, no exemplo acima, se ExceptionType2 for uma subclasse de ExceptionType1, o segundo bloco catch nunca será alcançado. Neste caso, deve-se inverter a ordem dos blocos catch.
Um bloco catch pode também lançar novas exceções e relançar a exceção que ele manipulou. Neste caso, todo o conjunto de blocos acima deve estar inserido num bloco try mais externo e as exceções em questão devem ser manipuladas por blocos catch seguindo este bloco try externo.
Em Java,
(A) é possível criar e manipular objetos, mas não removê-los, pois a remoção é manipulada automaticamente pelo sistema. - quem remove é o garbage collector
(B) classes são definidas através do uso da palavra chave class, seguido do nome da classe, que, entre outras restrições, não pode ser iniciado por um número. - a declaração de classe é Class tal
(C) quando uma classe é criada e não há nenhuma referência à sua superclasse, implicitamente a classe criada é derivada diretamente da classe Object. - quando uma classe é definida, a criação já é a instanciação
(D) construtores da superclasse podem ser explicitamente invocados usando a palavra-chave super. - não vi erro algum aqui
(E) todas as determinações de métodos a executar ocorrem através de ligação tardia. - apenas métodos polimorficos é feito tardiamente.
Se tivesse no enunciado "Em java é incorreto afirmar que," eu marcaria a letra E, mesmo colocando essas considerações...
Resposta: Letra E
Comentários