Apostila de Java

Propaganda
UNIVERSIDADE FEDERAL DE VIÇOSA
DEPARTAMENTO DE INFORMÁTICA
JAVA NA PRÁTICA
Volume I
Alcione de Paiva Oliveira
Vinícius Valente Maciel
2002
Java na Prática
1
Sumário
Capítulo I - Introdução ....................................... 5
CONVENÇÕES ............................................................................................................................. 9
Capítulo II - Programação Orientada a Objetos .................. 10
CLASSES E OBJETOS E LINGUAGENS DE PROGRAMAÇÃO ......................................................... 11
Ocultando de Informação................................................................................................... 15
Especialização e Herança .................................................................................................. 16
Sobrescrita, Sobrecarga e Polimorfismo............................................................................ 17
INTRODUÇÃO À DIAGRAMA DE CLASSES ................................................................................. 19
Diagrama de Classes.......................................................................................................... 19
Capítulo III - Introdução à Linguagem Java ..................... 26
PALAVRAS RESERVADAS ......................................................................................................... 29
LITERAIS .................................................................................................................................. 30
SEPARADORES.......................................................................................................................... 33
TIPOS DE DADOS....................................................................................................................... 34
Tipos de dados simples....................................................................................................... 34
Tipos de dados compostos .................................................................................................. 36
CONVERSÃO DE TIPOS.............................................................................................................. 40
OPERADORES ........................................................................................................................... 41
Expressões e Precedência entre Operadores ..................................................................... 49
COMENTÁRIOS ......................................................................................................................... 50
BLOCOS E ESCOPO ................................................................................................................... 51
ESTRUTURAS DE CONTROLE .................................................................................................... 52
Seleção ............................................................................................................................... 52
Repetição............................................................................................................................ 56
break e continue ......................................................................................................... 59
ARGUMENTOS DA LINHA DE COMANDO .................................................................................... 61
ASSERT (ASSERTIVAS) ............................................................................................................. 62
Sintaxe e semântica ............................................................................................................ 63
Habilitando e Desabilitando Assertivas ............................................................................. 63
Capítulo IV Classes, Packages e Interfaces ................... 66
CLASSES .................................................................................................................................. 66
Construtores ....................................................................................................................... 68
Valor de Retorno ................................................................................................................ 68
OBJETOS .................................................................................................................................. 69
MODIFICADORES DE ACESSO .................................................................................................... 72
Outros Modificadores......................................................................................................... 74
REFERÊNCIAS COMPARTILHADAS ............................................................................................ 78
COPIANDO OBJETOS ................................................................................................................. 81
O objeto this ................................................................................................................... 85
PACKAGES ............................................................................................................................... 86
Usando Packages ............................................................................................................... 87
Criando Packages .............................................................................................................. 87
Empacotando packages em arquivos JARs ........................................................................ 89
O Mecanismo de Extensão ................................................................................................. 92
DERIVANDO CLASSES ............................................................................................................... 94
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
2
super ................................................................................................................................... 97
A classe Object ............................................................................................................... 97
Sobrescrita e Polimorfismo ................................................................................................ 98
CLASSES E MÉTODOS ABSTRATOS ......................................................................................... 101
INTERFACES ........................................................................................................................... 102
CLASSES INTERNAS ................................................................................................................ 105
Classes Internas Anônimas............................................................................................... 107
INICIALIZAÇÃO E FINALIZAÇÃO DE OBJETOS ......................................................................... 109
Inicialização automática .................................................................................................. 109
Inicialização de atributos de Instância ............................................................................ 109
Inicialização de atributos de classe.................................................................................. 110
Blocos de inicialização..................................................................................................... 110
Ordem de inicialização .................................................................................................... 111
Finalização de Objetos..................................................................................................... 112
CONVERSÃO DE OBJETOS....................................................................................................... 112
EXCEÇÕES.............................................................................................................................. 113
Continuação após o Tratamento da Exceção................................................................... 115
A hierarquia de Exceções................................................................................................. 115
Capturando mais de uma exceção.................................................................................... 116
Lançando exceções........................................................................................................... 117
Comportamento do Sistema diante das Exceções ............................................................ 119
Criando suas próprias exceções....................................................................................... 120
A cláusula finally ....................................................................................................... 121
DOCUMENTANDO O CÓDIGO................................................................................................... 122
Rótulos.............................................................................................................................. 122
HTML embutida ............................................................................................................... 124
AGENDA ELETRÔNICA VERSÃO CONSOLE 1.0 ........................................................................ 125
Capítulo V – Entrada e Saída (java.io) ...................... 132
ACESSO SEQUENCIAL............................................................................................................. 132
ACESSO DIRETO ..................................................................................................................... 137
Capítulo VI – java.util ..................................... 141
LIDANDO COM COLEÇÕES ...................................................................................................... 141
As Interfaces Iterator e Enumeration ................................................................... 141
Vector ........................................................................................................................... 143
Stack.............................................................................................................................. 146
Hashtable .................................................................................................................... 148
MISCELÂNEA DE CLASSES DO PACOTE JAVA.UTIL................................................................. 151
Arrays ........................................................................................................................... 151
Date ................................................................................................................................ 154
Observable .................................................................................................................. 156
StringTokenizer ...................................................................................................... 160
AGENDA ELETRÔNICA VERSÃO CONSOLE 2.0 ........................................................................ 162
Capítulo VII - Serialização e Persistência .................... 169
AGENDA ELETRÔNICA VERSÃO CONSOLE 2.1 ........................................................................ 171
Capítulo VIII – AWT (Abstract Window Toolkit) ............... 174
A HIERARQUIA DE COMPONENTES ......................................................................................... 174
OLÁ MUNDO AWT ................................................................................................................ 176
TRATAMENTO DE EVENTOS ................................................................................................... 177
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
3
Modelo de Eventos 1.1 ..................................................................................................... 177
Tratamento de Eventos com classes Internas................................................................... 181
EXEMPLO BÁSICO .................................................................................................................. 186
ACRESCENTANDO CORES ....................................................................................................... 190
GERENCIANDO O LAYOUT...................................................................................................... 191
Exemplo com BorderLayout....................................................................................... 192
Exemplo com FlowLayout ........................................................................................... 193
Exemplo com CardLayout ........................................................................................... 194
Exemplo com GridLayout ........................................................................................... 196
Exemplo com GridBagLayout .................................................................................... 197
UTILIZANDO LISTAS............................................................................................................... 199
TRABALHANDO COM MENUS E DIÁLOGOS ............................................................................. 202
CAPTURANDO EVENTOS DO TECLADO ................................................................................... 206
PRINCIPAIS CLASSES .............................................................................................................. 206
Color.............................................................................................................................. 206
Component .................................................................................................................... 208
Button ........................................................................................................................... 210
Label.............................................................................................................................. 211
List ................................................................................................................................ 212
TextField .................................................................................................................... 213
TextArea....................................................................................................................... 214
CONTAINERS .......................................................................................................................... 215
Panel.............................................................................................................................. 217
Frame.............................................................................................................................. 218
AGENDA ELETRÔNICA VERSÃO GRÁFICA 1.0......................................................................... 221
Capítulo IX - Applets ....................................... 227
DESCRIÇÃO DO CÓDIGO HTML ............................................................................................. 231
MÉTODOS DA CLASSE APPLET ............................................................................................... 232
EXIBINDO UMA IMAGEM ........................................................................................................ 236
ÁUDIO .................................................................................................................................... 239
OBTENDO PARÂMETROS ......................................................................................................... 240
EXECUTANDO UM APPLET COMO APLICAÇÃO ........................................................................ 241
PREPARANDO APPLETS PARA PRODUÇÃO E ARQUIVOS JARS ................................................. 242
AGENDA ELETRÔNICA VERSÃO APPLET 1.0 ........................................................................... 244
Capítulo X JavaBean ......................................... 245
O QUE É UM JAVABEAN? ....................................................................................................... 245
JAVABEANS E FERRAMENTAS RAD ....................................................................................... 245
PROPRIEDADES ...................................................................................................................... 246
Simples ............................................................................................................................. 246
Indexada ........................................................................................................................... 247
Ligada (Bound)................................................................................................................. 248
Restringidas(Constrained) ............................................................................................... 249
EVENTOS................................................................................................................................ 251
DESENVOLVIMENTO DO EXEMPLO ......................................................................................... 252
TimerEventListener .......................................................................................................... 252
TimerEvent ....................................................................................................................... 253
TimerBean ........................................................................................................................ 253
INSTALANDO O BEANS DEVELOPMENT KIT (BDK)................................................................ 257
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
4
TESTANDO EXEMPLO NO BDK ............................................................................................... 257
Capítulo XI Perguntas Frequentes ............................ 264
Bibliografia ................................................ 265
Links ....................................................... 266
Índice ...................................................... 268
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
5
Capítulo I - Introdução
Java é uma linguagem de programação desenvolvida pela Sun
Microsystems e lançada em versão beta em 1995. O seu desenvolvimento foi
iniciado em 1991 pela equipe liderada por James Gosling visando o mercado de
bens eletrônicos de consumo. Por isso foi projetada desde o início para ser
independente de hardware, uma vez que as características dos equipamentos
variam amplamente neste nicho de desenvolvimento. Outro objetivo
estabelecido desde sua concepção foi o de ser uma linguagem segura. Segura
tanto no sentido de evitar algumas falhas comuns que os programadores
costumam cometer durante o desenvolvimento, como no sentido de evitar
ataques externos. Isto é importante no mercado de bens eletrônicos de consumo
por que ninguém gostaria de adquirir um produto que necessitasse desligar e
religar para que voltasse a funcionar corretamente. Estas características
despertaram o interesse para utilização de Java em outro ambiente que também
necessitava de uma linguagem com este perfil: a Internet. A Internet também é
um ambiente constituído por equipamentos de diferentes arquiteturas e necessita
muito de uma linguagem que permita a construção de aplicativos seguros.
Muitas pessoas argumentarão que estas características podem ser encontradas
em outras linguagens e portanto isto não explica o súbito sucesso da linguagem.
Podemos arriscar alguns palpites apesar de este ser um terreno um pouco
pantanoso para se aventurar, até por que as linguagens de programação tendem
assumir um caráter quase religioso. Uma das razões que na nossa opinião
favoreceram a rápida adoção da linguagem foi a sintaxe. Java é sintaticamente
muito semelhante à linguagem C/C++, apesar de existirem diferenças
fundamentais na filosofia de implementação entre as duas linguagens. Isto
facilitou a migração de uma legião imensa de programadores C/C++ para a nova
linguagem. Outra razão que não pode ser desprezada é o momento atual onde os
desenvolvedores estão ansiosos para se libertarem de sistemas proprietários.
Portanto, apesar de não serem novas as idéias embutidas na linguagem Java, a
reunião delas em uma só linguagem, juntamente com a facilidade migração dos
programadores e o momento atual, contribuíram para o rápido sucesso da
linguagem.
Hoje, segundo a International Data Corp. (IDC), existem mais de 2
milhões de programadores Java no mundo e a estimativa é que o número de
desenvolvedores ultrapasse os 5 milhões em 2004. O número de programadores
Java deve ultrapassar o de programadores C++ ainda este ano (2002), segundo a
consultoria americana Evans Data. Os profissionais que dominam a linguagem
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
6
estão entre os mais bem pagos da área de Tecnologia da Informação (TI), com
salários variando de 3 a 10 mil reais, podendo em alguns casos chegar à 16 mil
reais, segundo a revista Info Exame (dezembro de 2001).
A lista abaixo apresenta as principais características de Java de modo que
o leitor tenha uma visão geral da linguagem:
•
Orientação a objetos. Java não é uma linguagem totalmente orientada a
objetos como Smalltalk, onde tudo é objeto ou métodos de objetos. Por
questões de eficiência foram mantidos alguns tipos primitivos e suas
operações. No entanto, Java possui um grau de orientação a objetos bem
maior que C/C++, o que a torna bem mais harmoniosa e fácil de assimilar,
uma vez que o programador tenha compreendido esta forma de
desenvolvimento.
•
Compilação do código fonte para código de uma máquina virtual
(Bytecodes). Esta característica visa tornar a linguagem independente de
plataforma de Hardware e Sistema Operacional. Obviamente é necessário
que exista um programa capaz de interpretar o código em Bytecodes para
cada Sistema Operacional, denominado de Máquina Virtual. No entanto,
nada impede que o código fonte seja traduzido diretamente para o código
executável na máquina de destino. Já existem ambientes de desenvolvimento
que apresentam este tipo de opção. Alternativamente, é possível projetar
equipamentos que processem em hardware os Bytecodes. A Sun
desenvolveu um processador que executa operações em Bytecodes,
denominado de JavaChip. O diagrama abaixo ilustra as etapas envolvidas na
execução de um código Java.
Figura I-1. Fases para execução de um programa fonte em Java
•
Ausência de manipulação explícita de ponteiros. Em linguagens como
C/C++ e Pascal existe o tipo ponteiro como tipo primitivo da linguagem. A
especificação original de Pascal é restritiva no uso de ponteiros, permitindo
que sejam usados apenas para referenciar memória obtida na área de
alocação dinâmica (heap) e não permite que o programador examine o valor
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
7
da variável do tipo ponteiro, nem que realize operações aritméticas com
ponteiros. Já a linguagem C/C++ permite que o valor armazenado na
variável do tipo ponteiro faça referência a qualquer área de memória,
inclusive à área estática e automática (pilha), além de permitir aritmética de
ponteiros e o exame direto do valor armazenado. A manipulação do tipo
ponteiro exige uma grande dose de atenção por parte do programador e
mesmo programadores experientes frequentemente cometem erros no seu
uso. Além disso, o uso de ponteiros é uma fonte de insegurança na
linguagem, uma vez que permite que o usuário faça acesso a memória que
pode pertencer a outros processos, abrindo a possibilidade para
desenvolvimento de programas hostis ao sistema. A linguagem Java não
possui o tipo ponteiro. Isto não que dizer que não seja possível realizar
alocação dinâmica de memória. Todo objeto criado é alocado na área de
heap, mas o usuário não pode manipular a referência ao objeto
explicitamente.
•
Recuperação automática de memória não utilizada (Coleta de Lixo –
Garbage Collection). Nas linguagens onde existe alocação dinâmica de
memória, o programador é responsável pela liberação de memória
previamente obtida na área de alocação dinâmica e que não está sendo mais
utilizada. Se houver falhas na execução desta responsabilidade ocorrerá o
problema que é conhecido sob a denominação de “vazamento de memória”.
Este problema faz com que a partir de certo ponto o programa não consiga
obter memória para criação de novos objetos, apesar de existir área que não
está sendo mais usada mas que não foi devolvida ao gerente de memória.
Outro erro comum é a tentativa de acesso á áreas de memória já liberadas.
Todos os programadores que trabalham com linguagens que permitem
alocação dinâmica conhecem bem estes problemas e sabem o quanto é difícil
implementar programas que não possuam estes tipos de erros. A maior parte
dos erros que ocorrem no uso destas linguagens é devido a problemas na
alocação/liberação de memória. Visando o desenvolvimento de aplicações
robustas, livres deste tipo de falha, os projetistas de Java incorporaram um
procedimento de coleta automática de lixo à máquina virtual. Deste modo, os
objetos que não estão sendo mais usados são identificados pelo
procedimento, que libera a memória para ser utilizada na criação de novos
objetos.
•
Segurança. As pessoas costumam dizer que Java é uma linguagem segura.
Mas o que é ser uma linguagem de programação segura? Segurança possui
significados distintos para pessoas diferentes. No caso da linguagem Java na
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
8
versão 1.0 segurança significa impedir que programas hostis que possam
causar danos ao ambiente computacional, ou que busquem informações
sigilosas em computadores remotos para uso não autorizado. Na versão 1.1
foi adicionada a capacidade de permitir a verificação da identidade dos
programas (autenticação) e na versão 1.2 os dados que os programas enviam
e recebem podem ser criptografados por meio do uso de um pacote adicional.
Na versão 1.4 o pacote de criptografia JCE (JavaTM Cryptography Extension)
foi incorporado ao J2SDK.
•
Suporte à Concorrência. A construção de servidores, a criação de
programas com interfaces gráficas, e programas semelhantes que tem em
comum a necessidade de que o atendimento de uma solicitação não
incapacite o sistema de responder a outras solicitações concorrentemente,
demandam o uso de uma linguagem que facilite o desenvolvimento deste
tipo de programa. As linguagens projetadas antes do surgimento destas
necessidades, como C/C++, não previam facilidades para este tipo de
programação, o que obrigou a incorporação destes recursos posteriormente,
por meio de funções adicionais. Como a programação concorrente é uma
forma de programação que difere bastante da programação sequencial
convencional, a simples adição de novas funções para tentar adaptar a
linguagem a esta forma de codificação, não cria um ajuste perfeito com a
linguagem subjacente. Por outro lado, Java foi projetada visando facilitar a
programação concorrente. Isto faz com que a criação linhas de execução
(threads) seja bem mais natural dos que nas linguagens
tradicionais.Programação em rede. Java possui em seu núcleo básico
classes para comunicação em rede por meio dos protocolos pertencentes à
pilha de protocolos TCP/IP. A pilha de protocolos TCP/IP é a utilizada pela
Internet e tornou-se o padrão de fato para comunicação entre computadores
em uma rede heterogênea. Isto torna Java particularmente atrativa para o
desenvolvimento de aplicações na Internet. Além disso Java está incorpora
um amplo de conjunto de soluções para computação distribuída, como
CORBA (Common Object Request Broker Architecture), RMI (Remote
Method Invocation) e Servlets/JSP (aplicações Java que são executadas por
servidores Web).
Após o lançamento da versão beta da linguagem em 1995, a Sun tem
liberado diversas evoluções da linguagem na forma de versões e releases de um
conjunto de ferramentas denominado de Java Development Kit (JDK) até a
versão 1.2, quando se passou a denominar Java 2 SDK (Standard Development
Kit). Isto ocorreu porque outros kits de desenvolvimento com propósitos
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
9
específicos foram lançados, como o J2EE (Java 2 Enterprise Edition), voltado
para aplicações distribuídas escaláveis e o J2ME (Java 2 Micro Edition), voltado
para aplicações embutidas em dispositivos eletrônicos (Celulares, handheld,
etc.). Durante a elaboração deste livro, a última versão estável do SDK era a de
número 1.4 que pode ser obtida gratuitamente no site http://java.sun.com/.
Convenções
As seguintes convenções são usadas neste livro.
1. Fontes com larguras constantes são usadas em:
•
exemplos de código
public class Ponto {
private int x,y;
}
•
nomes de métodos, classes e variáveis mencionadas no texto.
2. Fontes com larguras constantes em negrito são usadas dentro de exemplos de
códigos para destacar palavras chave.
3. Fontes em itálico são usadas:
•
•
em termos estrangeiros;
na primeira vez que for usado um termo cujo significado não for
conhecimento generalizado.
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
10
Capítulo II - Programação Orientada
a Objetos
O mundo pode ser visto como um conjunto de objetos que se relacionam.
Por exemplo, uma pessoa, uma casa, uma cadeira da casa, etc. Os objetos não
são necessariamente físicos. Podem possuir uma natureza abstrata, como um
evento (uma partida de futebol) ou algo inexistente no mundo real (elefante corde-rosa). Na verdade, o conceito de objeto atua no nível lógico e não no real. Se
iremos representar algo como objeto ou não depende apenas de uma decisão a
nível lógico que pode facilitar a simulação de determinado aspecto da realidade.
Os objetos se agrupam em classes, segundo propriedades ou atributos
comuns. Por exemplo, a classe dos retângulos agrupa todas as formas
geométricas com a propriedade de possuir quatro lados formando ângulos de
90o. A relação entre um objeto e uma classe é de pertinência. Dizemos que um
objeto pertence a uma classe ou, mais comumente, que é uma instância de uma
classe. O Figura abaixo ilustra exemplos de classes.
Figura II-1. Classe dos (a) retângulos e dos (b) dos triângulos.
As classes podem ser relacionar com outra classe no sentido que uma
classe pode conter outra. Por exemplo, a classe de retângulos está inserida em
uma classe mais genérica, a classe dos polígonos. A classe mais genérica é
denominada de Superclasse e as classes mais específicas são denominadas de
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
11
Subclasses. As subclasses herdam as propriedades das superclasses. No nosso
exemplo, os polígonos possuem as propriedades de ter uma área, uma posição no
plano, um número n de vértices e n-1 ângulos. Todas essas propriedades são
herdadas tanto pela classe dos retângulos como pela classe do triângulos.
Podemos desta forma organizar os objetos em uma hierarquia onde as classes
mais específicas herdam as propriedades das classes mais genéricas.
Figura II-2. Classe dos polígonos.
Os objetos de uma classe possuem comportamentos que podem alterar o
valor de suas propriedades. Por exemplo, um carro pode sofrer uma aceleração
ou ser freado e com isso alterar a sua velocidade. Um objeto qualquer pode ser
deslocado, alterando assim as suas coordenadas no espaço.
Classes e Objetos e Linguagens de Programação
As linguagens de programação são utilizadas para construir simulações
de aspectos da realidade no computador. Quanto mais facilmente pudermos
expressar os conceitos capturados da realidade, mais facilmente construiremos a
simulação. Seguindo este raciocínio podemos concluir que as linguagens que
possuem facilidades para representação de objetos permitem uma modelagem
mais fácil dos conceitos do mundo real. No entanto, podemos utilizar uma
linguagem de programação convencional para modelar as classes e objetos
abstraídos da realidade. Por exemplo, podemos modelar a classe dos retângulos
por meio de um registro (record) em Pascal.
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
12
type Classe_Retangulo = record
X1,Y1,X2,Y2: integer;
end;
Exemplo II-1. Representação da Classe Retângulo em Pascal.
No Exemplo II-1 o retângulo é definido por dois pontos, sendo X1 e Y1 o
ponto superior esquerdo e X2 e Y2 o ponto inferior direito. Os objetos podem
ser representados por meio de variáveis do tipo definido:
type Classe_Retangulo = record
X1,Y1,X2,Y2: integer;
end;
var Retangulo1 : Classe_Retangulo;
Exemplo II-2. Criação de objetos em Pascal.
procedure intRetangulo(XA,YA,XB,YB: integer; var R:
Classe_Retangulo);
begin
R.X1 := XA;
R.Y1 := YA;
R.X2 := XB;
R.Y2 := YB;
end;
procedure MudaPos(X,Y: integer; var R: Classe_Retangulo);
begin
R.X2 := X+(R.X2-R.X1);
R.Y2 = Y+(R.Y2-R.Y1);
R.X1 = X;
R.Y1 = Y;
end;
Exemplo II-3. Definição das operações em Pascal.
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
13
As propriedades dos objetos podem ser manipuladas através de funções e
procedimentos. Por exemplo, podemos ter uma operação para inicializar os
valores da estrutura e outra para alterar a posição do retângulo. O Exemplo II-3
mostra essas duas operações.
Contudo, existem algumas limitações das linguagens convencionais que as
tornam inadequadas para a modelagem de objetos:
•
Não existem recursos para ocultar a estrutura de dados de procedimentos
que não foram projetados para a sua manipulação. É muito importante
que a linguagem forneça recursos para se implementar este tipo de
isolamento. Se acessarmos uma estrutura apenas por meio dos
procedimentos projetados para este fim, quando a estrutura for alterada
apenas os procedimentos que manipulam a estrutura sofreriam
modificações. No entanto, se não agirmos desta forma será necessário
procurar em todo o programa os acessos diretos à estrutura. Claro que
este comportamento pode ser adotado em qualquer linguagem, mas é
mais seguro se a linguagem fornece meios para o programador forçar
este tipo de comportamento. A capacidade de “esconder” a estrutura de
dados de acessos diretos é chamada de ocultação de informação.
•
Não existem recursos para herança de propriedades entre classes e
subclasses. Se precisarmos implementar uma estrutura que é uma
especialização de outra já implementada, será preciso codificar
novamente todas as propriedades, mesmo as comuns, e todos os
procedimentos de acesso. Isto dificulta o reaproveitamento de código, o
que, consequentemente aumenta o tempo de desenvolvimento e a
possibilidade de erros.
•
Não existe uma forma de relacionar explicitamente as estruturas de dados
com os procedimentos que as manipulam. O relacionamento entre os
procedimentos que manipulam uma estrutura de dados e a estrutura é
estabelecido implicitamente, por meio de alguma convenção definida
pelo programador. É importante que a linguagem obrigue o programador
relacionar explicitamente os procedimentos com a estrutura de dados, de
modo que fique claro qual é a interface de acesso ao objeto.
A adoção de uma linguagem programação orientada a objetos resolve todos
esses problemas. Existem várias linguagens comerciais com esta característica:
Smalltalk, Eiffel, C++, etc. Algumas com elementos não orientados a objetos
como C++, outras puramente orientadas a objetos como Smalltalk, onde tudo é
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
14
objeto. Java é uma linguagem orientada a objetos mais “pura” do que C++,
fugindo desta orientação apenas em alguns pontos bem definidos, em nome da
eficiência de execução. Em Java podemos representar diretamente as classes e
objetos. Por exemplo, a classe retângulo seria declarada da seguinte forma:
class Retangulo
{
int X1,Y1,X2,Y2;
public Retangulo(int XA, int YA, int XB, int YB)
{
X1 = XA;
Y1 = YA;
X2 = XB;
Y2 = YB;
}
void MudaPos(int X, int Y)
{
X2 = X+(X2-X1);
Y2 = Y+(Y2-Y1);
X1 = X;
Y1 = Y;
};
}
Exemplo II-4.Representação da Classe Retângulo em Java.
No Exemplo II-4 mudamos o nome do procedimento iniRetangulo para
que é o mesmo nome da classe. No momento não é importante
entendermos a razão desta mudança, que será esclarecida no próximo capítulo.
Note que os procedimentos são declarados dentro do corpo da classe, tornando
explícito relacionamento entre a classe e os procedimento. As funções
declaradas nas classes são chamadas de métodos e a partir de agora nos
referenciaremos a eles como tal. Note também que diferentemente do exemplo
em Pascal, não é preciso passar o objeto como parâmetro, uma vez que as
variáveis que estão sendo modificadas pertencem ao objeto corrente, ao qual está
associado o método. É como se para cada objeto de uma classe fossem criadas
versões de todos os métodos da classe, de modo que cada método só opera sobre
as variáveis do objeto a quem pertencem. Para declarar uma variável do tipo da
classe basta preceder a variável com o nome da classe.
Retangulo,
Retangulo ret;
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
15
Até esse momento nenhum objeto foi criado. Para criar um objeto
(instância) é usado o operador new.
ret = new Retangulo(10,10,20,30);
Note que o operador new é seguido de uma chamada ao método com o
mesmo nome da classe. O métodos com esta característica são chamados de
construtores e só podem ser invocados durante a criação de um objeto. Como
veremos mais tarde, uma classe pode ter mais de um construtor. Após a criação
do objeto é possível acessar os outros métodos do objeto através do operador “.”.
Por exemplo, podemos mudar a posição do objeto por meio do método MudaPos.
ret.MudaPos(40,40);
Como já dissemos, não é preciso passar o objeto como argumento, já que
é criada uma cópia do método para cada objeto. A grosso modo podemos dizer
que cada instância da classe recebe uma cópia da variáveis e dos métodos da
classe.
Ocultando de Informação
O projetista cuidadoso deve ocultar a representação interna da classe,
permitindo o acesso aos atributos da classe via métodos predefinidos. Desta
forma a representação interna fica isolada do restante do programa e fica mais
fácil alterá-la sem que seja preciso alterar outras partes do código. A ocultação
de informação é obtida por meio de qualificadores, como o private, que
impede o acesso à variáveis via métodos definidos em outras classes. O nível de
ocultação depende do qualificador utilizado. Todos os qualificadores serão
abordados com detalhes no Capítulo IV. O exemplo II-5 mostra como impedir
que as variáveis declaradas na classe Retangulo sejam acessadas diretamente.
class Retangulo
{
private int X1,Y1,X2,Y2;
public Retangulo(int XA, int YA, int XB, int YB)
{
X1 = XA;
Y1 = YA;
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
16
X2 = XB;
Y2 = YB;
}
void MudaPos(int X, int Y)
{
X2 = X+(X2-X1);
Y2 = Y+(Y2-Y1);
X1 = X;
Y1 = Y;
};
}
Exemplo II-5. Ocultando informação em Java.
Especialização e Herança
Para criar uma subclasse de uma classe pré-existente utilizamos o
operador extends. Por exemplo, podemos definir uma subclasse da classe
Retangulo, chamada de RetanguloColorido, que possui, além das variáveis e
métodos herdados da superclasse, uma variável para armazenar a cor do
retângulo, juntamente com um método para alterar o valor.
class RetanguloColorido extends Retangulo
{
private Color Cor;
void AtribuiCor(Color C)
{
Cor = C;
};
}
Exemplo II-6. Declarando subclasses em Java.
A princípio, subclasse pode acessar todos os métodos e variáveis da
superclasse. No entanto, isto também pode ser alterado via qualificadores.
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
17
Sobrescrita, Sobrecarga e Polimorfismo
Podemos definir mais de um método com o mesmo nome na mesma
classe ou subclasses. Caso o método possua a mesma assinatura (número e tipos
de argumentos e tipo de retorno) que outro método, então o método não pode
pertencer à mesma classe do anterior. Se ambos os métodos estiverem na mesma
linha hierárquica (classe/subclasse), dizemos que o método da subclasse
sobrescreve o método da superclasse. O método que será executado dependerá
da classe do objeto.
class Empregado
{
protected float salario;
public float getSalario() {return salario;}
}
class Vendedor extends Empregado
{
protected float comissao;
public float getSalario() {return salario+comissao;}
}
Exemplo II-7. Sobrescrita do método getSalario().
No exemplo II-7 o método getSalario() da classe Vendedor
sobrescreve o método do mesmo nome da classe Empregado.
Se a assinatura do método for diferente de outro método com o mesmo
nome definido anteriormente na mesma classe ou em outra classe da mesma
linha hierárquica, então estamos realizando uma sobrecarga sobre o
identificador do método. Quando for usado o identificador dentro do código de
um programa o método invocado será determinado pela classe a que pertence o
objeto do método e pelo número e tipos dos argumentos passados para o método.
O termo sobrecarga advém do fato de um mesmo identificador denotar mais de
método.
class Empregado
{
protected float salario;
public void aumento() {salario= salario*10.0;}
public void aumento(float porcent)
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
18
{
salario= salario*porcent;
}
}
Exemplo II-8. Sobrecarga do método aumento().
No exemplo II-8 o identificador aumento pode referenciar dois métodos
distintos. Um aumenta o salário em 10% e no outro o aumento depende do valor
da porcentagem passado como parâmetro. Note que as assinaturas do métodos
diferem entre si.
Alguns autores chamam sobrecarga de polimorfismo, que é a habilidade
de um determinado objeto se comportar ou ser visto de diferentes formas,
quando na verdade a sobrecarga é um tipo particular de polimorfismo, chamado
de polimorfismo ad hoc . Na sobrecarga um identificador representa vários
métodos com computações distintas. Existe também o polimorfismo
paramétrico, onde um método pode realizar a mesma computação sobre objetos
de tipos distintos. Isso pode ser implementado em Java definindo um método
que recebe e retorna objetos da classe Object. Como a classe Object é a “mãe
de todas as classes” o método pode operar da mesma forma independente da
classe a qual o objeto realmente pertence, desde que a computação seja
independente da classe.
class Poli
{
public Object identidade(Object objeto)
{
return Object;
}
}
Exemplo II-9. Polimorfismo paramétrico.
No Exemplo II-9 o método identidade() retorna o objeto passado
como parâmetro. Este método realiza a mesma computação, independentemente
da classe do objeto. Obviamente é um exemplo muito simples e sem utilidade
prática mas serve para ilustrar o conceito de polimorfismo paramétrico.
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
19
Introdução à Diagrama de Classes
É possível registrar diretamente em uma linguagem de programação os
objetos percebidos em uma determinada realidade. No entanto, é melhor
utilizarmos uma notação gráfica intermediária para melhor visualizarmos os
objetos e as relações entre objetos, e ir alterando esta representação até estarmos
seguros que possuímos um entendimento razoável do problema, e ai sim,
partirmos para a codificação da solução. Muitas vezes é preciso recorrer a mais
de uma notação gráfica, de modo a expressar várias facetas da realidade que está
sendo modelada. Neste livro, recorreremos ao uso de notação gráfica em alguns
exemplos para ilustrarmos a arquitetura dos programas antes de apresentarmos o
código. Acreditamos que desta forma o leitor compreenderá melhor os
exemplos.
A notação gráfica que adotamos mostra as relações estáticas entre classes
de objetos. Ela faz parte do conjunto de notações da UML (Unified Modeling
Language ou Linguagem de Modelagem Unificada) proposta por Grady Booch,
James Rumbaugh e Ivar Jacobson em 1995. A UML é um conjunto de notações
que tem por objetivo modelar diversos aspectos de um sistema em diferentes
níveis de abstração. Ou seja, pode ser utilizado para a captura de requisitos de
um sistema assim como em projeto de programas. É voltada para análise e
projeto de sistemas orientados a objetos. A área de análise e projeto orientados a
objetos ainda não possui uma notação “vencedora” como existe para a análise e
projeto estruturado. Contudo, a UML vem se popularizando rapidamente, e é
encontrada com facilidade em textos de programação, sobretudo em se tratando
de Java. Portanto, um conhecimento sobre as principais notações que constituem
a UML é importante para qualquer um que deseja ingressar na área de
programação orientada a objetos. Este livro utiliza uma das linguagens, ou
diagramas, que compõem a UML: o diagrama de classes.
Diagrama de Classes
O Diagrama de Classes representa graficamente as classes do sistema e o
relacionamento estático entre as classes, isto é, o relacionamento que não muda
com o tempo. Por exemplo, em um sistema acadêmico, um aluno cursa várias
disciplinas. O número de disciplinas e a disciplina que efetivamente está sendo
cursada pode alterar, mas o vínculo aluno-cursa-displinas permanece. Para
ilustrar o nosso estudo dos diagramas da UML utilizaremos exemplos sobre
modelagem de aspectos realidade acadêmica.
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
20
Uma classe é representada no diagrama de classes por meio de um
retângulo, que pode ser dividido em até três seções horizontais, como mostrado
na figura abaixo:
Figura II-3. Forma geral para representação de uma classe.
A seção superior é usada para registrar o nome da classe. A seção
intermediária é reservada para registro das propriedades da classe, caso existam,
e na seção inferior é registrado a assinatura dos métodos que pertencem à classe,
caso existam. Por assinatura do método queremos dizer o nome do método,
juntamente com seus argumentos e valor de retorno. A figura abaixo mostra uma
representação da classe das disciplinas.
Figura II-4. Representação da classe das disciplinas
De modo geral, por razões de simplicidade, não se representam os
métodos que tratam da alteração dos atributos e construtores. Se Assumi que
toda classe possui estes métodos. Portanto, podemos simplificar a representação
acima, omitindo a seção do métodos.
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
21
Figura II-5. Representação simplificada da classe das disciplinas.
Podemos indicar tanto nos atributos quanto nas classes a visibilidade
deles em relação a outras classes. As visibilidades possíveis, juntamente com os
símbolos adotados estão listados na tabela abaixo:
Visibilidade
Símbolo
Pública
+
Protegida
#
Privada
-
Descrição
Sem restrição de acesso.
Pode ser acessado apenas na própria classe e
por subclasses.
Pode ser acessado apenas na própria classe.
Tabela II-1. Visibilidades possíveis para atributos e métodos.
A visibilidade é atribuída a um atributo ou método precedendo a
declaração do método com o símbolo adequado, como na figura abaixo:
Figura II-6. Representação com visibilidade.
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
22
As classes podem se relacionar de diversas formas: por associação
comum, por agregação e generalização. Abaixo é apresentada cada forma de
relacionamento, juntamente com suas as notações.
Associação comum
A notação utilizada para associar duas classes é simplesmente uma linha
unindo as classes. A figura II.7 mostra a associação entre a classe dos alunos e a
classe das disciplinas.
Figura II-7. Associação entre Aluno e Disciplina.
A figura acima expressa que alunos se associam com disciplinas mas não
indica se um aluno se relaciona com várias ou apenas uma disciplina. Esta
informação é chamada de cardinalidade da relação e é expressa anotando-se o
valor da cardinalidade na associação junto à classe que está sendo relacionada.
Assim, a figura II.8 expressa que um aluno se relaciona com várias disciplinas.
Figura II-8. Associação de um Aluno com várias Disciplinas.
Como uma disciplina se relaciona com vários alunos, o diagrama
completo é o representado na figura II-9.
Figura II-9. Associação de vários Aluno com várias Disciplinas.
A tabela II-2 mostra algumas representações de cardinalidade:
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
Notação
1
* ou 0..*
0..1
n..m
23
Descrição
Exatamente um
Zero ou mais
Opcional (zero ou um)
Máximo e mínimo
Tabela II-2. Representações de cardinalidade.
Até agora apresentamos apenas associações entre duas classes, mas nada
impede que mais de duas classes participem de uma associação. Por exemplo, a
figura II-10 ilustra uma associação ternária que representa o fato de um aluno
cursar uma disciplina em um período.
Figura II-10. Associação entre aluno, disciplina e período.
Uma associação pode ter atributos próprios. Ou seja, atributos que não
pertençam a nenhuma das classes envolvidas na associação mas sim à própria
associação. Na associação entre alunos e disciplina o atributo nota, não pertence
a aluno, tampouco à disciplina, uma vez que para saber uma nota é preciso saber
quem é o aluno e qual é a disciplina. A representação de atributos da associação
é representada por meio de um retângulo ligado à associação por meio de uma
linha tracejada.
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
24
Figura II-11. Representação de atributos de associação.
Agregação
Alguns objetos são compostos por outros objetos. Por exemplo, um carro
é composto por chassi, lataria, pneus e motor, que por sua vez é composto pelo
carburador, pistões, bloco, etc. Este tipo de associação é representada por uma
linha com um losango na ponta.
Figura II-12. Agregação entre Curso e Disciplina.
Generalização
O último tipo de associação entre classes é o que o ocorre entre
superclasses e subclasses. Uma superclasse é uma generalização das suas
subclasses, que herdam os atributos e métodos da primeira. A notação utilizada
para representar a generalização é uma linha com um triângulo na extremidade
da associação no lado da classe mais genérica.
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
25
Figura II-13. Representação da generalização.
A figura II-14 procura representar todas associações discutidas em um
único diagrama.
Figura II-14. Associação entre as classes do domínio acadêmico.
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
26
Capítulo III - Introdução à
Linguagem Java
Existe uma tradição entre os programadores que estabelece que ao se
começar a aprender uma nova linguagem de programação, o primeiro programa
a ser escrito deve ser um que imprima a frase “Olá mundo” em um dispositivo
de saída. Dizem que isto atrai a sorte e espanta os bugs. Independente da crença
geral, existem algumas razões bastante justificáveis para se começar o
aprendizado de uma linguagem executando logo um programa, mesmo sem ter
muita idéia do que se está fazendo. Primeiramente, existe o fator psicológico.
Iniciar o aprendizado executando um programa sem erros, aumenta confiança do
aluno e elimina temores de se estar aprendendo algo muito complexo. Existe
também o fato de que apesar de um programa muito simples dar uma visão um
pouco limitada da linguagem, já é possível observar alguns elementos
importantes. Afinal trata-se de um programa completo. Portanto, para não fugir a
tradição, eis o programa OlaMundo em Java:
public class OlaMundo
{
public void exibeOla()
{
System.out.println(“Ola, Mundo!”);
}
public static void main(String args[])
{
OlaMundo obj = new OlaMundo();
Obj.exibeOla();
}
}
Exemplo III-1. Programa OlaMundo.
O programa acima é composto por uma única classe que possui apenas
dois métodos. Isto é importante, porque não é possível fazer um programa Java
sem recorrer às classes, uma vez que os procedimentos são definidos como
métodos de classes. Isto não é verdade em linguagens como C++, o que diminui
o seu “grau” de orientação a objetos.
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
27
Os métodos com o nome main são métodos especiais e servem como
ponto inicial para execução do programa. Ou seja, a execução do programa será
iniciada a partir da execução de um método main(). A assinatura do método
main é sempre a mesma e a sua descrição em detalhes será vista na seção
Argumentos da linha de comando. Podemos adiantar apenas que o qualificador
public estabelece que este método pode ser chamado por métodos ou
procedimentos externos à classe. O qualificador static significa que o método
pertence à classe e não às instâncias da classe, e deste modo pode ser invocado
mesmo antes de ser criado algum objeto para a classe. void indica que o método
não retornará valor algum. Já argumento String args[] é um array de
Strings contendo os parâmetros passados na linha de comando.
O corpo do método main() possui duas linhas. A primeira instrução cria
um objeto da classe OlaMundo e o atribui à variável obj. A segunda linha invoca
o método exibeOla() do objeto recém criado. O método exibeOla() invoca o
método println() do objeto out da classe System, que faz parte do pacote de
classes fornecido com a linguagem. Este método exibe no dispositivo de saída
padrão a String que é passada como argumento.
Se você olhar os programas “OlaMundo” mostrados em outros livros
sobre Java irá notar que o programa apresentado aqui é um pouco mais
complicado que o exibido nesses livros. Geralmente este programa inicial é
apresentado apenas com um método: o método main(). A razão de termos usado
uma abordagem diferente é que desejamos desenvolver um hábito saudável na
programação em Java: procure usar o método main() apenas para criar os
objetos e deixe que os objetos executem a lógica do problema.
Este programa deve ser salvo em um arquivo contendo O MESMO
NOME DA CLASSE e com as mesmas letras maiúsculas e minúsculas (é
importante frisar, uma vez que esquecer este detalhe é um erro muito comum), e
com a extensão “.java”. Portanto o arquivo contendo o programa acima deverá
se chamar OlaMundo.java. Se você instalou o Java 2 development kit (SDK),
que pode ser obtido gratuitamente no site http://java.sun.com/products/, então
para compilar o programa basta digitar o comando:
javac OlaMundo.java
O código Java é traduzido (o termo mais comum é compilado) para
instruções de uma máquina virtual (bytecodes) para que possa ser executado de
forma independente da plataforma (sistema operacional e hardware). O código
em bytecodes é armazenado em um arquivo com o mesmo nome do original e
com a extensão “.class”. Assim após a execução do comando acima será
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
28
gerado o arquivo OlaMundo.class. A invocação da máquina virtual para
executar o código em bytecodes é efetuada por meio do seguinte comando:
java OlaMundo
Neste ponto nota-se uma desagradável idiossincrasia do SDK. Para
compilar o programa para bytecode foi necessário explicitar a extensão do
arquivo, porém, para executar o código é preciso omitir a extensão “.class”.
Apesar de ser um detalhe que aparentemente não prejudica o uso do ambiente,
confunde o usuário, principalmente o iniciante.
Quando recebe um arquivo de bytecodes para a executar, a máquina
virtual procura o método main() em uma classe com o mesmo nome do arquivo.
Uma vez encontrado o método a execução é iniciada com a execução do método.
Isto significa que em nosso exemplo será procurado o método main() da classe
OlaMundo para servir de ponto de entrada. Podem existir outros métodos main()
em outras classes codificadas no mesmo arquivo de bytecodes, mas apenas o
método main() da classe com o mesmo nome do arquivo servirá de ponto inicial
de execução. Portanto, em nosso caso, o programa será executado produzindo a
saída:
Ola, Mundo!
Agora que já cumprimos o ritual de iniciação na linguagem, podemos passar a
descrever os detalhes da linguagem. Grande parte das descrições que seguem
abaixo foram baseadas na especificação da linguagem registrada no livro The
Java Language Specification, segunda edição, por James Gosling e outros.
Identificadores
Todos os identificadores da linguagem devem iniciar com uma letra, ou o
caractere ´_`, ou o caractere ´$`. Deve-se evitar o uso do caractere ´$`, de modo
que fique reservado para geração de código automático. Tratamos por letra todo
caractere reconhecido pelo método Character.isJavaLetter. Isto inclui uma
ampla gama de caracteres do conjunto Unicode1, de modo que os programadores
podem usar identificadores adaptados a uma ampla gama de idiomas. Após o
1
O conjunto de caracteres Unicode foi criado para substituir o conjunto ASCII. Ele usa 16 bits
para representar os caracteres, o que resulta em 65536 caracteres possíveis, em oposição aos 7
bits do código ASCII (8 para o ASCII estendido), o que resulta em 128 caracteres possíveis (256
para 8 bits).
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
29
primeiro caractere, também podem ser usados os caracteres que vão de ´0` até
´9`. A linguagem Java distingue as letras maiúsculas das minúsculas, portanto o
identificador Aluno é distinto do identificador aluno. A tabela III-1 mostra
alguns identificadores válidos e inválidos.
Válido
Aluno10
Num_Alunos
_disciplina$
Professor_10
αβγ
Não
Inválido
Aluno#10
Num Alunos
!disciplina
10Professor
&uuu
Não?
Tabela III-1. Identificadores válidos e inválidos em Java.
Palavras Reservadas
As seguintes sequências de caracteres são reservadas para o uso como
palavras chave e, portanto, não podem ser usadas como identificadores:
abstract
assert2
boolean
break
byte
case
catch
char
class
const
continue
default
do
double
else
extends
final
finally
float
for
goto
if
implements
import
instanceof
int
interface
long
native
new
package
private
protected
public
return
short
static
strictfp3
super
switch
synchronized
this
throw
throws
transient
try
void
volatile
while
Tabela III-2. Palavras reservadas da linguagem Java.
As palavras chave const e goto, apesar de serem reservadas, não estão
sendo usadas correntemente na linguagem.
2
3
Introduzida a partir da versão 1.4 do SDK.
Introduzida a partir da versão 1.2 do SDK
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
30
Literais
Literais são elementos do código que representam um valor de tipo
primitivo, tipo String ou null. Os literais podem ser numéricos, booleanos,
caracteres ou cadeias de caracteres (Strings). Literais numéricos incluem
inteiros, ponto flutuante.
Literais Inteiros
Um literal inteiro é do tipo primitivo long (longo) se possui o sufixo ´L`
ou ´l`, caso contrário é do tipo primitivo int (inteiro). Um numeral hexadecimal
é prefixado pelos caracteres ´0x` ou ´0X` seguidos de um ou mais dígitos
hexadecimais. Os dígitos hexadecimais com valores entre 10 e 15 são
representados pela letras ´a` até ´f` ou ´A` até ´F`, nessa ordem. Um numeral
octal consiste de um dígito 0 seguido de um ou mais dígitos de 0 até 7.
O maior literal decimal do tipo int é 2147483648. Os literais decimais
de 0 até 2147483647 podem aparecer em qualquer lugar que um literal inteiro
pode aparecer, mas o literal 2147483648 pode aparecer somente como operando
de uma negação unária.
Os maiores literais hexadecimal e octal positivos inteiros são 0x7fffffff e
017777777777, respectivamente, que correspondem ao valor decimal
2147483647. Os maiores literais hexadecimal e octal negativos inteiros são
0x80000000 e 020000000000, respectivamente, que representam o valor
decimal -2147483648 (). Os literais hexadecimal e octal 0xffffffff e
037777777777, representam o valor decimal -1. Abaixo estão listados alguns
exemplos de literais inteiros:
0
-12
0372
0xCafe
1999
0x00FF00FF
O maior literal decimal do tipo longo é o 9223372036854775808L. Os
literais decimais de 0L até 9223372036854775807L podem aparecer em
qualquer lugar que um literal inteiro longo pode aparecer, porém o literal
9223372036854775808L pode aparecer somente como operando de uma
negação unária.
Os maiores literais hexadecimal e octal positivos inteiros longos são
0x7fffffffffffffffL e 0777777777777777777777L, respectivamente, que
correspondem ao valor decimal 9223372036854775807L.Os maiores literais
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
31
hexadecimal e octal negativos inteiros longos são 0x8000000000000000L e
01000000000000000000000L, respectivamente, que representam o valor
decimal -9223372036854775808L. Os literais hexadecimal e octal
0xffffffffffffffffL e 01777777777777777777777L, representam o valor decimal 1. Abaixo estão listados alguns exemplos de literais inteiros longos:
0L
0777L
0xC0B0L
0xCafe
1999
0x100000000L
Literais de Ponto Flutuante
Um literal de ponto flutuante é composto por uma parte inteira seguida
de um ponto decimal, uma parte fracionária, um expoente e um sufixo
determinando o tipo. O expoente, se presente, é indicado pela letra ‘E’ ou ‘e’,
seguido por um inteiro com sinal um opcional. Pelo menos um dígito na parte
inteira ou fracionária e o ponto decimal ou o expoente ou o sufixo indicando o
tipo são exigidos. Todos os outros componentes são opcionais.
Um tipo ponto flutuante é do tipo float se possuir o sufixo ‘F’ ou ‘f’,
caso contrário é do tipo double. O tipo double possuir opcionalmente o sufixo
‘D’ ou ‘d’.
O tipo float e double de Java obedecem a especificação IEEE 754 para
binários de ponto flutuante de precisão simples (32-bit) e dupla (64-bit).
O maior literal positivo do tipo float é 3.40282347e+38f. O menor
literal positivo do tipo float diferente de zero é 1.40239846e-45f. O maior
literal positivo do tipo double é 1.79769313486231570e+308. O menor literal
positivo do tipo double diferente de zero é 4.94065645841246544e-324.
Um programa em Java pode representar quantidades infinitas sem
produzir erros de compilação por meio da utilização de expressões constantes
tais como 1f/0f e -1d/0d ou pela utilização das constantes predefinidas
POSITIVE_INFINITY e NEGATIVE_INFINITY das classes Float e Double.
Exemplos de literais do tipo float:
1e1f
2.f
.3f
0f
3.14f
6.022137e+23f
Exemplos de literais do tipo double:
1e1
2.
.3
0.0
3.14
1e-9d
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
1e137
Java na Prática
32
Literais Booleanos
O tipo boolean possui dois valores, representados pelos literais true e
false.
Literais de Caracteres
Um literal do tipo char é expresso como um caractere ou uma sequência
de escape, envolvida por aspas simples. Os caracteres CR e LF nunca são
caracteres de entrada, uma vez que são reconhecidos como terminadores de
linha. Exemplos de literais do tipo char:
Caractere
'a'
'%'
'\n'
'\t'
'\\'
'\''
'\u03a9'
'\uFFFF'
'\177'
Descrição
o caractere
o caractere
line feed
Tab
o caractere
o caractere
a.
%
\
'.
Tabela III-3. Exemplo de literais do tipo char.
Literais de Cadeia de Caracteres (Strings)
Um literal do tipo String consiste de zero ou mais caracteres envolvidos
por aspas duplas. Cada caractere pode ser representado por uma sequência de
escapes. O tipo String não é um tipo primitivo e sim uma classe denominada
String. Portanto, um literal é na verdade uma instância da classe String.
Uma String longa pode ser particionada em cadeias menores unidas
pelo operador de concatenação ´+’. Exemplos de literais do tipo String:
Literal
""
Descrição
Cadeia vazia.
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
"\""
"esta é uma cadeia"
"esta é uma "
+"cadeia "
33
Uma cadeia com apenas o caractere ".
Uma cadeia contendo 17 caracteres.
Uma cadeia formada por dois literais do
tipo String.
Tabela III-4. Exemplo de literais do tipo String.
Sequências de escape
Sequências de escape permitem a representação de alguns caracteres não
gráficos, assim como as aspas simples e duplas e a barra invertida. Exemplos de
sequências de escape:
Sequência
\b
\t
\n
\f
\r
\"
\'
\\
Descrição
\u0008: backspace BS
\u0009: tab horizontal HT
\u000a: linefeed LF
\u000c: form feed FF
\u000d: carriage return CR
\u0022: aspas duplas "
\u0027: aspas simples '
\u005c: barra invertida \
Tabela III-5. Exemplo sequências de escape.
O Literal null
O tipo null possui apenas um valor, representado pelo literal null.
Separadores
Java possui nove separadores (caracteres de pontuação), listados abaixo:
(
)
{
}
[
]
;
,
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
.
Java na Prática
34
Tipos de dados
Os tipos de dados definem como devem ser interpretados os dados
armazenados. São essenciais a toda linguagem de programação e devem ser
cuidadosamente selecionados para compor a linguagem, de modo a não limitar a
sua área de atuação. A maioria da linguagens modernas definem um conjunto de
tipos simples, um conjunto de tipos compostos, formados por tipos mais
primitivos e alguma forma de definição de novos tipos, de modo que possa
modelar mais apropriadamente a realidade. A linguagem Java obedece esta
regra, fornecendo um conjunto de tipos simples e um conjunto de tipos
compostos. O mecanismo para definição de novos tipos utilizado por Java é
simplesmente a definição de classes.
Java é uma linguagem fortemente tipada. Isto significa que toda variável
e expressão possui um tipo determinado em tempo de compilação. Os tipos
primitivos disponíveis em Java não são classes. Esta é uma das razões porque
Java não é uma linguagem 100% orientada a objetos. No entanto, para cada tipo
primitivo existe uma classe correspondente onde são declarados um conjunto de
métodos para a manipulação dos valores primitivos, focalizando principalmente
na conversão de tipos. Estas classes estão agrupadas no pacote java.lang. Por
exemplo, o tipo primitivo int é usado para expressar valores inteiros. A classe
correspondente ao tipo int é a Integer. Nela estão declaradas variáveis
públicas contendo o valor máximo e mínimo que uma variável do tipo inteiro
pode armazenar e métodos para conversão para outros tipos.
Tipos de dados simples
Tipos de dados simples são aqueles que não podem ser divididos em
tipos mais primitivos. Os tipos de dados simples de Java podem ser divididos em
inteiros, ponto flutuante, booleano e caractere. Para se definir uma variável de
um determinado tipo basta preceder o nome da variável com o nome do tipo
desejado, como na forma abaixo:
<nome do tipo> <nome da variável>;
Assim, para se declarar uma variável var1 do tipo inteiro basta a linha de
código abaixo:
int var1;
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
35
Podemos também declarar mais de uma variável em com o mesmo tipo,
usando o caractere ‘,’ como separador. Portanto, a declaração
int var1, var2;
é equivalente às declarações
int var1;
int var2;
Java também permite que as variáveis sejam inicializadas durante a
declaração. Assim, para inicializarmos a variável var1 com o valor 1 na
declaração basta a linha de código abaixo:
int var1=1;
Segue abaixo as categorias de tipos de dados simples.
Inteiros
Nome
Tamanho
byte
short
int
long
8 bits
16 bits
32 bits
64 bits
Tabela III-6. Tipos inteiros.
Ponto Flutuante
Nome
Tamanho
float
double
32 bits
64 bits
Tabela III-7. Tipos de ponto flutuante.
booleanos
boolean
{true,false}
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
36
Caractere
char
16 bits
Tipos de dados compostos
Os tipos de dados compostos são constituídos por tipos mais simples.
Todos os tipos compostos em Java são Classes. Nesta seção trataremos duas
classes de objetos muito usadas em Java para criação de tipos compostos: os
arrays (arranjos) e strings. Posteriormente abordaremos a declaração de objetos
no caso geral e no Capítulo VI será apresentado o pacote de classes java.util
que contém várias classes para agrupamento de objetos.
Arrays
Um array é um objeto, e como tal herda todos os métodos da classe
Object. Um array contém um certo número de variáveis chamadas de
componentes. Em Java todo objeto é acessado indiretamente, via uma referência.
Ou seja, não se cria uma variável do tipo de um objeto e sim uma variável que
pode referenciar um objeto. Estamos falando de ponteiros, o que pode parecer
contraditório, uma vez que tínhamos mencionado que Java não possui ponteiros.
Na verdade Java acessa as instâncias de objeto por meio de ponteiros mas eles
não estão disponíveis como tipo da linguagem e nem é possível que o
programador os manipule diretamente.
Pode parecer pouco importante para o programador saber que a variável
não armazena o objeto diretamente e sim uma referência a um objeto, uma vez
que ele não pode manipular ponteiros. No entanto, acreditamos que esta
informação é importante para que o leitor possa entender as etapas para a criação
de objetos. A primeira etapa é a declaração da variável para referenciar o objeto
e a segunda etapa é a criação do objeto propriamente dito. Para se declarar uma
variável para referenciar objetos do tipo array é usada a seguinte sintaxe:
tipo identificador[];
tipo[] identificador;
Exemplos
int numeros[];
char[] letras;
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
37
long grade[][];
Note que não é definido o número de elementos do array. Isto faz parte
do objeto. Note também que existem duas formas de declarações de referências a
arrays. O número de “[]” indica o número de dimensões do array. A criação do
objeto é realizada por meio do operador new, seguido pelo tipo do array e pelo
número de componentes de cada dimensão, como nos exemplos abaixo:
numeros = new int[10];
char alfabeto[] = new char[26];
grade = new long[10][10];
Alternativamente podemos realizar as duas etapas acima e ainda definir
os elementos do array em uma única declaração como no exemplo abaixo, onde
é criado um array de três inteiros, referenciados pela variável primos e onde o
primeiro elemento é 7, o segundo é 11 e o terceiro é 13.
int primos = {7, 11, 13};
De agora em diante não faremos distinção entre referência a objeto do
tipo array e o objeto array, a não ser que seja necessário explicitar esta distinção.
O acesso aos elementos do array é realizado por meio do nome da
variável seguida por um expressão inteira não negativa envolvida pelos
caracteres ‘[’ e ‘]’. A expressão inteira é chamada de índice e os valores
admissíveis para a expressão vai de 0 a n-1, onde n é número de elementos do
array. O índice do primeiro elemento é 0. Abaixo seguem alguns exemplos:
alfabeto[0] = ‘a’;
grade[0][5] = 10L;
for(int i=0; i<10; i++) numeros[i] = i*2;
É possível descobrir o tamanho de um array em tempo de execução
acessando a variável pública length do objeto, onde está armazenada a
capacidade do array. Por exemplo:
for(int i=0; i< numeros.length; i++) numeros[i] = i*2;
Strings
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
38
O manipulação de cadeias de caracteres (strings) em Java é realizada por
uma classe do pacote java.lang denominada String. Ou seja, não existe um
tipo primitivo para tratar cadeias de caracteres. Para se declarar uma variável que
faz referencia a um objeto String usa-se a seguinte linha de comando:
String nome;
Para que a variável faça referência a um objeto basta criar um objeto por
meio do operador new ou atribuir a referência de um objeto preexistente.
nome = new String(“Pedro”);
String x, y;
x= nome;
y = “Pedro”;
No primeiro caso a variável nome faz referencia a um objeto recém criado
contendo o valor “Pedro”. Já a variável x faz referencia ao mesmo objeto
referenciado pela variável nome. Ou seja, nenhum novo objeto String é criado,
ocorrendo um compartilhamento de objetos. Já a variável y faz referência a um
objeto String contendo o valor “Pedro”, distinto do objeto referenciado por x e
nome. Podemos também inicializar a variável durante a declaração.
String nome = “Pedro”;
Os objetos do tipo String possuem um conjunto extenso de métodos e
construtores para a manipulação e criação de Strings. A tabela abaixo mostra
alguns dos mais utilizados.
Construtor
Descrição
String(byte[] bytes, int offset,
int num)
Constrói uma nova String convertendo o subarray de
bytes especificado
Constrói uma nova String usando a sequência de
caracteres contida no StringBuffer.
Constrói uma nova String convertendo o array de
bytes especificado
Constrói uma nova String com o mesmo conteúdo
da String passada como argumento.
Constrói uma nova String contendo zero caracteres.
Constrói uma nova String convertendo o array de
caracteres especificado.
Constrói uma nova String convertendo o subarray de
caracteres especificado.
String(StringBuffer buffer)
String(byte[] bytes)
String(String valor)
String()
String(char[] valor)
String(char[] valor, int offset,
int num)
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
Tabela III-8. Principais construtores da classe String.
Método
Descrição
charAt(int indice)
compareTo(String
outraString)
equals(Object anObject)
getChars(int srcBegin,
int srcEnd, char[] dst,
int dstBegin)
indexOf(String str)
Retorna o caractere localizado no índice especificado.
Compara duas Strings lexicograficamente.
Verifica se dois objetos são iguais.
Copia os caracteres da String em um array de
caracteres.
Retorna o índice da primeira ocorrência do substring
no string.
indexOf(String str, int
Retorna o índice da primeira ocorrência do substring
fromIndex)
no string a partir do índice especificado.
indexOf(int ch, int
Retorna o índice da primeira ocorrência do caractere
fromIndex)
no string a partir do índice especificado.
indexOf(int ch)
Retorna o índice da primeira ocorrência do caractere
no string .
length()
Retorna o comprimento do string.
replace(char oldChar,
Retorna uma nova String onde todos os caracteres
char newChar)
oldChar foram substituídos pelos caracteres
newChar.
substring(int beginIndex) Retorna uma nova string que é substring da atual.
substring(int beginIndex, Retorna uma nova string que é substring da atual.
int endIndex)
toLowerCase()
Converte para minúsculas.
toUpperCase()
Converte para Maiúsculas.
trim()
Remove os espaços em branco do inicio e do fim do
String.
valueOf(Object obj)
Retorna a representação em String do argumento
Object.
valueOf(char c)
Retorna a representação em String do argumento
char.
valueOf(boolean b)
Retorna a representação em String do argumento
booleano.
valueOf(long l)
Retorna a representação em String do argumento
long.
valueOf(int i)
Retorna a representação em String do argumento int..
valueOf(char[] data)
Retorna a representação em String do argumento
array de caracteres
valueOf(float f)
Retorna a representação em String do argumento
float.
valueOf(double d)
Retorna a representação em String do argumento
double.
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
39
Java na Prática
40
Tabela III-9. Principais métodos da classe String.
Os literais de Strings são tratados como instâncias da classe String e
como tal possuem os métodos de um objeto da classe String. Por exemplo,
após a execução da expressão abaixo a variável x conterá o valor 5.
int x = “Pedro”.length();
Conversão de Tipos
De forma geral as conversões entre tipos em Java devem ser
especificadas explicitamente. A forma mais comum de se especificar uma
conversão é por meio da notação abaixo:
(<tipo destino>) <expressão>
Por exemplo:
int i = 10;
char c = (char) i;
Este tipo de conversão é chamada de casting. O programador deve ficar
bastante atento no que diz respeito a conversões, uma vez que pode acontecer
perda de informação quando convertemos um tipo para outro que ocupa um
espaço menor na memória. A tabela abaixo mostra as conversões entre tipos
primitivos que podem causar perda de informação:
do tipo
byte
short
char
int
long
float
double
para o tipo
char
byte,
byte,
byte,
byte,
byte,
byte,
char
short
short,
short,
short,
short,
char
char, int
char, int, long
char, int, long, float
Tabela III-10. Conversão de tipos que podem causar perda de informação.
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
41
Outra forma de converter tipos é usando os métodos fornecidos pelas
classes associadas aos tipos. A tabela abaixo mostra a classe associada a cada
tipo primitivo.
Tipo
int
float
double
boolean
byte
short
long
Classe
Integer
Float
Double
Boolean
Byte
Short
Long
Tabela III-11. Classes associadas a cada tipo primitivo.
As classes fornecem métodos para conversão mais sofisticados como do
tipo primitivo para String e vice-versa. Por exemplo, a classe Integer
fornece um método para converter String para int:
int i = Integer.parseInt(“12”);
Para se converter um inteiro para String podemos utilizar o método
toString():
String s = Integer.toString(12);
Existem métodos semelhantes nas outras classes. Existe também um tipo
de conversão que somente se aplica aos operandos do operador binário ‘+’
quando um dos operandos é um objeto da classe String. Caso o outro operando
não seja um objeto da classe String, então ele será convertido para String e o
resultado da operação será a concatenação das duas cadeias. As Conversões
entre objetos de classes distintas serão tratadas no próximo Capítulo.
Operadores
Os operadores atuam sobre valores e variáveis de modo a gerar novos
valores ou modificar os valores das variáveis. Os símbolos abaixo representam
os 37 operadores da linguagem Java:
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
=
==
+
+=
>
<=
-=
<
>=
*
*=
!
!=
/
/=
~
&&
&
&=
?
||
|
|=
:
++
^
^=
42
-%
%=
<<
<<=
>>
>>=
>>>
>>>=
Os operadores podem ser divididos nas seguintes classes.
Unários
Descrição
Incremento
Decremento
Negativo
Complemento de bit
Símbolo
++
-~
Tabela III-12. Operadores unários.
Os operadores de incremento e decremento (++ e --) aumentam e
decrementam variáveis de tipo inteiro de uma unidade. Estes operadores podem
ser usados na forma prefixa ou pósfixa. Na forma prefixa o operador modifica o
valor da variável antes que o valor seja usado na expressão onde está a variável.
Na forma pósfixa o operador modifica o valor da variável depois que o valor é
usado na expressão onde está a variável. Por exemplo, o valor das variáveis x,
y e w após a execução do trecho de código abaixo
int x = 1, y, w;
y = x++;
w = --x;
é 1. Isto ocorre porque y recebe o valor de x antes de ser incrementado e w
recebe o valor de x depois de ser decrementado.
O operador de negação unário (-) é usado para mudar o sinal de um valor
inteiro. O operador de Complemento de bit (~) inverte cada bit da representação
binária de um inteiro. O exemplo abaixo mostra efeito da aplicação deste
operador.
byte x = 10; // valor em binário 00001010
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
x = ~x;
43
// valor em binário 11110101; valor em decimal 245.
O programa abaixo mostra o uso de cada operador unário.
public class Unarios {
public static void main (String args[]) {
int x = 10, y = 0;
System.out.println("x = " + x);
System.out.println("y = " + y);
System.out.println("++x = " + ++x);
System.out.println("y++ = " + y++);
System.out.println("x = " + x);
System.out.println("y = " + y);
System.out.println("-x = " + -x);
System.out.println("~y = " + ~y);
}
}
Saída:
x = 10
y = 0
++x = 11
y++ = 0
x = 11
y = 1
-x = -11
~y = -2
Exemplo III-2. Uso dos operadores unários.
Binários
Descrição
Adição e concatenação de strings
Subtração
Multiplicação
Divisão
Módulo
E de bit
OU de bit
OU exclusivo de bit
deslocamento de bits a esquerda
Símbolo
+
*
/
%
&
|
^
<<
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
44
deslocamento de bits a direita
des. a direita com preenchimento zero
>>
>>>
Tabela III-13. Operadores binários.
Acreditamos que os operadores binários para adição, subtração,
multiplicação, divisão e concatenação de Strings não necessitam maiores
explicações. O operador de módulo (%) retorna o resto da divisão entre dois
operandos inteiros. Por exemplo, o valor da variável x após a execução do trecho
de código
int x = 7 % 4;
é 3.
Os operadores &, | e ^ implementam operações orientada para bits, ou
seja, operações que atuam sobre os bits individuais dos operandos. São úteis
quando se usa valores como campos de bits. O operador & realiza a operação
lógica E entre cada bit individual dos operandos. Por exemplo, o valor da
variável x após a execução do trecho de código
y
4
1
byte y = 3;
byte x = Y & 5;
00000011
00000101
00000001
&
é 1.
O operador | realiza a operação lógica OU entre cada bit individual dos
operandos. Por exemplo, o valor da variável x após a execução do trecho de
código
byte y = 3;
byte x = Y | 5;
y
4
7
é 7.
00000011
00000101
00000111
|
O operador ^ realiza a operação lógica XOU (ou exclusivo) entre cada bit
individual dos operandos. Por exemplo, o valor da variável x após a execução do
trecho de código
byte y = 3;
byte x = Y & 5;
y
4
1
00000011
00000101
00000110
é 6.
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
^
Java na Prática
45
Os operadores <<, >> e >>> implementam operações de deslocamento de
bits. Os bits do operando à esquerda serão deslocados o número de posições
especificadas pelo operando à direita. Os bits que saem dos limites do campo são
perdidos e a variável é preenchida com zeros no lado oposto ao deslocamento. O
operador << realiza a operação de deslocamento de bits a esquerda. Por
exemplo, o valor da variável x após a execução do trecho de código
byte x = 7 << 1;
é 14. Note que o deslocamento de uma unidade para a esquerda tem o mesmo
efeito da multiplicação por 2, desde que nenhum bit seja. Os operadores >> e
>>> realizam a operação de deslocamento de bits a direita. A diferença entre o
operador >> e o >>> é que o primeiro não desloca o bit de mais alta ordem
usado para indicar números negativos. O programa abaixo ilustra o uso desses
operadores:
public class Deslocamento {
public static void main (String args[]) {
int x = 7, y =–7;
System.out.println("x
System.out.println("y
System.out.println("x
System.out.println("x
System.out.println("y
System.out.println("y
= " + x);
= " + y);
<< 1= " +
>> 2= " +
>> 2= " +
>>> 31= "
(x<<1));
(x>>2));
(y>>2));
+ (y>>>31));
}
}
Saída:
x = 7
y = -7
x <<= 14
x >> 2= 1
y >> 2= -2
y >>> 30= 3
Exemplo III-3. Uso dos operadores de deslocamento de bits.
Note que no caso do número positivo o deslocamento à direita teve o
efeito de uma divisão inteira por dois. Já quando o número é negativo este efeito
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
46
não ocorre, devido ao fato da representação de números inteiros ser feita em
complemento a dois.
Relacionais
Descrição
Menor que
Maior que
Menor igual
Maior igual
Igual
Diferente
Símbolo
<
>
<=
>=
==
!=
Tabela III-14. Operadores relacionais.
Os operadores relacionais são usados para comparar valores. O resultado
da aplicação desses operadores é um valore boolano, ou seja :true ou false. O
programa abaixo ilustra o uso desses operadores:
public class Relacional {
public static void main
int x = 7, y = 8;
System.out.println("x
System.out.println("y
System.out.println("x
System.out.println("x
System.out.println("y
System.out.println("y
System.out.println("x
}
}
(String args[]) {
= " + x);
= " + y);
< y = " + (x < y));
> 10 = " + (x > 10));
<= 8 = " + (y <= 8));
== 5 = " + (y == 5));
!= y = " + (x != y));
Saída:
x
y
x
x
y
y
x
= 7
= 8
< y = true
> 10 = false
<= 8 = true
== 5 = false
!= y = true
Exemplo III-4. Uso dos operadores relacionais.
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
47
Booleanos
Descrição
E
OU
OU Exclusivo
E Curto circuito
OU Curto circuito
Negação
Igualdade
Condicional
Símbolo
&
|
^
&&
||
!
==
?:
Tabela III-15. Operadores booleanos.
Os operadores booleanos atuam sobre valores booleanos e retornam um
valor booleano. A tabela abaixo resume o resultado da aplicação dos operadores
booleanos, exceto o operador condicional (?:) e o de negação (!):
Operando 1
true
true
false
false
Operando 2
true
false
true
false
&
true
false
false
false
|
true
true
true
false
^
false
true
true
false
&&
true
false
false
false
||
true
true
true
false
==
true
false
false
true
Tabela III-16. Resumo dos operadores booleanos.
O operador de negação muda o valor booleano como mostrado na tabela
abaixo:
Operando
true
false
!
false
true
Tabela III-17. Operador de negação.
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
48
O operador condicional atua sobre três operandos: uma expressão
condicional e duas outras expressões quaisquer. A forma geral do operador é
<condição> ? <expressão 1> : <expressão 2>.
Se <condição> for avaliada como true, então o resultado da aplicação
do operador condicional será o retorno da avaliação da <expressão 1>. Caso
contrário o resultado da avaliação da <expressão 2> será retornado. O
programa abaixo ilustra o uso deste operador:
public class Condicional
{
public static void main (String args[])
{
int x = 5;
boolean par = (x % 2 == 0) ? true : false;
System.out.println("x = " + x);
System.out.println("É par = " + par);
}
}
Saída:
x = 5
É par = false
Exemplo III-5. Uso do operador condicional.
Atribuição
Descrição
Simples
Adição
Subtração
Multiplicação
Divisão
Modulo
AND
OR
XOR
Símbolo
=
+=
-=
*=
/=
%=
&=
|=
^=
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
49
Tabela III-18. Operadores de atribuição.
Os operadores de atribuição simplesmente atribuem um valor a uma
variável. Exceto o operador de atribuição simples (=), todos os outros operadores
na forma
<variável> <op>= <expressão>
funcionam como uma maneira abreviada de se escrever
<variável> = <variável> <op>(<expressão>)
Por exemplo, a atribuição
x += 2;
tem o mesmo significado da atribuição
x = x +2;
Esta forma de escrever facilita a trabalho de digitação, porém torna o
código fonte menos legível.
Expressões e Precedência entre Operadores
Expressões são trechos de códigos que geram valores. Portanto, o trecho
de código 1+2 é uma expressão pois gera o valor 3. Literais também são
expressões pois geram o próprio valor que representam. Portanto, o literal 3.14 é
também uma expressão. As variáveis que ocorrem em uma expressão são
também expressões, pois representam o valor que armazenam.
As expressões são combinadas por meio dos operadores para formar
expressões maiores. Assim, podemos combinar as expressões 3 e 5*6 por meio
de um operador tipo + para formar a expressão
3+5*6
Uma pergunta que pode surgir é: como a expressão é avaliada? Ou seja,
que operador é aplicado primeiro: o + ou o *? Dependendo da ordem de
aplicação dos operadores o resultado da avaliação da expressão pode ser
diferente, como no caso acima. A ordem de aplicação obedece uma prioridade
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
50
definida pela precedência relativa entre os operadores. A tabela abaixo define a
precedência entre os operadores. A precedência decresce de cima para baixo. Em
se tratando de operadores com a mesma precedência a avaliação em uma
expressão é feita da esquerda para a direita.
.
++
*
+
<<
<
==
&
^
&&
||
?:
=
[]
-/
>>
>
!=
()
!
%
>>>
<=
~
>=
Tabela III-19. Precedência de operadores.
Comentários
Comentar o código fonte faz parte das regras que definem um bom estilo
de programação. Java possui três diferentes formas de se comentar o código,
ilustradas abaixo:
// comentário de linha
/* comentário de bloco */
/** comentário de bloco c/ propósito de documentação
*/
As duas primeiras formas de comentário são familiares para os
programadores de C++. Na primeira forma, todos os caracteres posicionados
após “//” e antes do final da linha são ignorados pelo compilador. Na segunda
forma todos os caracteres que ocorrem após “/*” são ignorados pelo
compilador, até que seja encontrado a sequência “*/”. A terceira forma de
comentário é semelhante a segunda, com a diferença de que é usada pela
ferramenta de documentação javadoc para gerar uma documentação no formato
HTML (HyperText Markup Language). O uso dessa ferramenta será tratado no
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
51
final do próximo capítulo. O exemplo III-6 repete o código do exemplo III-5
com a adição de alguns comentários:
public class Condicional
{ // classe para teste do operador ?:
public static void main (String args[])
{
int x = 5; // variável usada para o teste
/*
Será impresso true se x for par
*/
boolean par = (x % 2 == 0) ? true : false;
System.out.println("x = " + x);
System.out.println("É par = " + par);
}
}
Exemplo III-6. Uso de comentários.
Blocos e Escopo
Na linguagem Java, assim como em C/C++ um bloco de comandos é
delimitado pelos caracteres ‘{’ e ‘}’. Os blocos são utilizados para agrupar
comandos que são relacionados. Um bloco também pode conter blocos,
chamados de blocos internos. Todas as variáveis declaradas em um bloco podem
ser referenciadas apenas dentro do bloco e nos blocos internos ao bloco onde foi
definida, desde que não exista nenhuma variável no bloco mais interno com o
mesmo nome. As seções onde uma determinada variável pode ser acessada
definem a escopo da variável. A figura III-1 ilustra a relação entre blocos e
escopo.
public class Visivel {
public static void main (String args[]){
int x = 5;
escopo do x externo
int y = 2;
{
...
{
escopo de y
int x =2;
...
}
...
}
}
}
}
escopo do x interno
}
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
52
Figura III-1. Visibilidade de variáveis.
Estruturas de Controle
As estruturas de controle definem a sequência de execução das
instruções. Não é possível implementar um algoritmo que não seja trivial em
uma linguagem de programação que não tenha um conjunto mínimo de
estruturas de controle. As estruturas de controle podem ser divididas em seleção,
repetição e sequência. A sequência é simplesmente definida pela execução
sequencial dos comandos, de cima para baixo. Falta abordarmos as estruturas
pertencentes às outras duas classes.
Seleção
Na seleção o fluxo sequencial de execução é desviado segundo uma
condição ou valor. Java apresenta duas formas seleção: o if e o switch.
if
O comando de seleção de if possui duas formas básicas:
if (condição)
comando1
(a)
if (condição)
comando1
else
comando2
(b)
Na forma (a) o comando1 é executado se condição for avaliada como
true. Na forma (b) o comando1 é executado se condição for avaliada como
true senão o comando2 é executado.
if (x==0)
y = 5;
else
y+=6;
Exemplo III-7. Comando if.
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
53
Se o comando a ser executado for na verdade um conjunto de comandos
devemos então usar os delimitadores de bloco ‘{’ e ‘}’ para agrupar os
comandos em um único bloco.
if (x==0)
y = 5;
else
{
y+=6;
x++;
}
Exemplo III-8. Comando if executando um bloco.
Os comandos if podem combinar em forma aninhada como mostrado no
exemplo III-9.
if (x==0)
if (y == 1)
x= y;
else
y+=6;
Exemplo III-9. ifs aninhados.
O aninhamento como no exemplo III-9 deixa uma dúvida com que if o
else está relacionado? Com o mais interno ou o mais externo? A regra para
esses casos é: o else se relaciona com o if mais interno. Essa regra pode ser
alterada, utilizando os delimitadores ‘{’ e ‘}’ para definir os blocos de
comandos, como mostrado no exemplo III-10.
if (x==0)
{
if (y == 1)
x= y;
}
else
y+=6;
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
54
Exemplo III-10. Uso de blocos para definir o par if-else.
switch
O comando switch é útil quando existem várias ações distintas que
precisam ser realizadas em função do resultado da avaliação de uma expressão.
Não é nada que não poderia ser feito com um conjunto de if-else, no entanto o
uso do switch facilita codificação e a legibilidade do programa. A forma geral
do comando está definida abaixo:
switch(Expr)
{
case const1: com1;
...
case constN: comN;
default: comDef
}
O comando switch avalia a expressão Expr e compara o valor resultante
com todas as constantes colocadas após a palavra a chave case. A comparação é
feita na ordem de cima para baixo e a primeira constante que igualar com o valor
resultante da expressão faz com que os comandos após o ‘:’ sejam executados
até o fim ou até que seja encontrado o comando break. A palavra chave
default é opcional e ela serve como ponto de entrada para os comandos que
serão executados caso o valor da expressão não seja igual a nenhuma das
constantes. O exemplo III-11 ilustra o uso do comando switch.
switch(letra)
{
case ‘i’:
case ‘a’:
case ‘e’:
System.out.println(“inserir”);
break;
System.out.println(“alterar”);
break;
System.out.println(“excluir”);
break;
default:
System.out.println(
“Ação ignorada: ”+letra);
}
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
55
Exemplo III-11. Uso do comando switch.
O comando break é muito importante na composição do switch. Ele faz
com que a execução sequencial dos comandos seja interrompida para ser
retomada no primeiro comando após o comando switch. O exemplo III-12
mostra as saídas de um programa com um comando switch que não faz uso do
comando break. Note que todos os comandos são executados, uma vez que não
existe um comando break para interromper a execução.
public class Switch {
public static void main (String args[]) {
char letra = ‘i’;
switch(letra)
{
System.out.println(“inserir”);
case ‘i’:
System.out.println(“alterar”);
case ‘a’:
System.out.println(“excluir”);
case ‘e’:
System.out.println(
default:
“Ação ignorada: ”+letra);
}
}
}
Saída:
inserir
alterar
excluir
Ação ignorada:i
Exemplo III-12. Uso do comando switch sem break.
O leitor pode se estar perguntando porque a sequência de comandos não
é interrompida automaticamente em vez de deixar para o programador a tarefa
de sinalizar isso por meio do comando break. A verdade é que a omissão do
comando break pode ser útil para poupar digitação naqueles casos onde mais de
uma opção precisa executar a mesma sequência de comandos. O exemplo III-13
mostra um desses casos.
switch (mês) {
case 1:
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
case
case
case
case
case
case
case
case
case
case
case
56
3:
5:
7:
8:
10:
12: dias = 31; break;
4:
6:
9:
11: dias = 30; break;
2: if (((ano % 4==0) && !(ano % 100 == 0))
|| (ano % 400 == 0) )
dias = 29;
else
dias = 28;
break;
}
Exemplo III-13. Uso útil da omissão do comando break.
Repetição
Os comandos de repetição, ou de iteração, executam um comando ou um
bloco de comandos várias vezes. É uma das formas para executar repetidamente
um comando. A outra é a recursão que é a chamada direta ou indireta de um
método durante a execução do próprio método. Em Java existem três formas de
comandos de repetição: o while; o do-while; e o for.
while
A forma geral do comando while é:
while(condição)
Comando;
O comando while executa Comando enquanto condição for avaliada
como true. Portanto, para encerrar a iteração é necessário que exista a
possibilidade da execução de Comando alterar a avaliação de condição. A
condição é testada antes da execução do comando. O exemplo III-14 mostra a
soma dos 100 primeiros números inteiros.
int i = 1;
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
57
int x = 0;
while(i<=100)
x +=i++;
Exemplo III-14. Soma dos 100 primeiros números inteiros.
Se for necessário executar mais de um comando deve-se agrupá-los por
meio de delimitadores de bloco, como mostrado no exemplo III-15.
while(i<=100)
{
x *=y;
i++;
}
Exemplo III-15. - Uso de while com bloco.
do-while
A forma geral do comando do-while é:
do
Comando;
while(condição)
O comando do-while executa Comando enquanto condição for
avaliada como true. A diferença em relação ao comando while é que a
condição é testada após a execução do comando. O exemplo III-16 mostra a
soma dos 100 primeiros números inteiros. Os comandos também podem ser
agrupados em um bloco.
int i = 1;
int x = 0;
do
x +=i++;
while(i<100);
Exemplo III-16. Soma dos 100 primeiros números inteiros com do-while.
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
58
for
A forma geral do comando for é:
for(Cod.Inicialização;condição; Cod.passo)
Comando;
No comando for o Cod.Inicialização (código de inicialização) é
executado uma vez, a condição é avaliada antes de cada iteração e o
Comando e o Cod.passo é executado a cada iteração. O Cod.passo é
executado após o Comando. Todas as seções do comando for podem ser
omitidas. Enquanto condição for avaliada como true é executada mais uma
iteração. Em geral, o Cod.Inicialização é utilizado para inicializar a
variável que será testada na condição e o Cod.passo é utilizado para alterar
o valor da mesma. O exemplo III-17 mostra a soma dos 100 primeiros números
inteiros.
x = 0;
for(int i=1; i<=100; i++)
x +=i;
Exemplo III-17. Soma dos 100 primeiros números inteiros com for.
Tanto o Cod.Inicialização como o Cod.passo podem ser
compostos de vários comandos. Cada comando é separado por ‘,’. O exemplo
III-18 ilustra esta forma de uso do comando for.
for(int i=1, x = 0; i<=100; i++)
x +=i;
Exemplo III-18. Soma dos 100 primeiros números inteiros com for.
Como já foi dito, todas as seções do comando for podem ser omitidas. O
exemplo III-19 mostra uma iteração infinita, definida pela omissão das seções do
comando for.
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
59
for(;;)
System.out.println(“ola mundo”);
Exemplo III-19. Iteração infinita.
break e continue
Já vimos o uso do comando break quando discutimos o comando
switch. Ele servia para interromper a sequência de execução dos comando e sair
do comando switch. Isto não acontece apenas no comando switch. Toda vez
que um break é encontrado, o fluxo de execução “salta” para fora do bloco onde
está contido o break, sendo retomado no comando após o bloco. A figura III-2
ilustra este comportamento.
for (;;)
{
...
break;
...
}
...
fluxo retomado aqui
Figura III-2. Uso do comando break.
O uso mais comum do comando break ocorre em uma condição de teste
dentro de uma iteração. O exemplo III-20 é equivalente ao exemplo III-18.
for(int i=1, x = 0;; i++)
if (i==101) break;
else x +=i;
Exemplo III-20. Soma dos 100 primeiros números inteiros com for e break.
Já o comando continue é usado dentro de uma iteração e faz com que
ocorra um desvio para a condição de teste da iteração. Ou seja, o comando
continue força um salto para próxima iteração. A figura III-3 ilustra este
comportamento.
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
while (Condição)
{
...
continue;
...
}
...
60
fluxo retomado aqui
Figura III-3. Uso do comando continue.
Como no comando break, o uso mais comum do comando continue é
em uma condição de teste dentro de uma iteração.
break e continue com rótulos
Diferentemente C/C++ Java permite o uso de rótulos após os comandos
break e continue para permitir que a saída para mais de um nível de iteração
quando em laços aninhados. Rótulos correspondentes aos usados nos comandos
break/continue devem ser posicionados imediatamente antes dos comandos de
laços, seguidos do caractere ‘:’. O exemplo III-21 mostra o uso de rótulos para
desviar para um laço mais externo ao corrente. No caso do exemplo uma nova
iteração do laço mais externo é iniciada quando o continue é executado.
public static boolean contemstr(String sub, String str)
{
prox:
for (int i=0; i<=str.length()-sub.length(); i++)
{
for (int j=0; j<sub.length(); j++)
if (str.charAt(i+j) != sub.charAt(j))
continue prox;
return true;
}
return false;
}
Exemplo III-21. Uso de comandos com rótulos.
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
61
Argumentos da linha de comando
Os argumentos digitados na linha de comando são passados para o
método main() da classe invocada por meio de um vetor de Strings. Por
exemplo, se executarmos a linha de comando abaixo
java teste um dois três
o método main() da classe teste receberá o seguinte vetor de Strings:
[0]
um
[1]
dois
[2]
três
Figura III-4. Vetor com os argumentos da linha de comando.
Para quem está acostumado a programar na linguagem C note o primeiro
elemento do vetor não é o nome do programa e sim o primeiro argumento da
linha de comando. O espaço serve como separador de argumentos. Se
desejarmos tratar uma cadeia de caracteres com espaço como um único
argumento é necessário delimitá-la com aspas duplas. Por exemplo, o comando
abaixo
java teste um “dois três”
resulta no seguinte vetor:
[0]
um
[1]
dois três
Figura III-5. Vetor com os argumentos da linha de comando com aspas.
O classe do exemplo III-22 mostra um programa que imprime os
argumentos recebidos, um em cada linha do dispositivo de saída.
public class ImpLinha
{
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
62
public static void main(String args[])
{
int i;
for (i=0;i<args.length;i++)
System.out.println(args[i]);
}
}
Exemplo III-22. Imprimindo a linha de comando.
Todos os argumentos são tratados como Strings. Se houver necessidade
de tratar os argumentos como pertencendo a um tipo diferente será preciso
realizar as conversões necessárias. No exemplo III-23 os argumentos são
convertidos para números inteiros para serem somados.
public class Soma
{
public static void main(String a[]){
int i,soma=0;
for (i=0;i<a.length;i++)
soma += Integer.parseInt(a[i]);
System.out.println(“A soma é:”+soma);
}
}
Exemplo III-23. Imprimindo a soma dos números.
Assert (Assertivas)
Com o surgimento da versão 1.4 do SDK uma nova alteração na
linguagem Java foi introduzida, envolvendo a inclusão de uma nova palavra
reservada: a palavra assert. O objetivo desta alteração é incluir na linguagem
recursos que permitam que o programador verifique se uma dada condição,
chamada de assertiva, é verdadeira em um local específico do código. Isto
possibilita o desenvolvimento de códigos mais confiáveis, sendo um recurso
utilizado durante a etapa de desenvolvimento e desabilitado em tempo de
execução.
Devido a inclusão de uma nova palavra chave, códigos fontes
preexistentes que incluam a palavra assert como identificador gerarão erro de
compilação ao serem compilados com a versão 1.4 do SDK. Este problema de
compatibilidade é solucionado por meio de flags de compilação.
A linguagem C++ possui recurso semelhante, com a diferença de ser
implementado por meio de biblioteca e não a nível de linguagem. No caso de
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
63
Java, a decisão de não utilizar uma biblioteca para implementação deste recurso
deveu-se ao objetivo de se buscar uma implementação mais transparente, apesar
do inconveniente da perda de compatibilidade.
A inclusão de recursos de para verificação de condições estava presente
na especificação original da linguagem, quando ainda era denominada oak, mais
foi retirada da primeira versão por se acreditar que não haveria tempo para se
desenvolver uma implementação adequada.
Sintaxe e semântica
A formato geral de uma assertiva é:
assert<expressão booleana1>;
ou
assert<expressão booleana1>:<expressão booleana2>;
A avaliação de uma assertiva funciona da seguinte forma: a primeira
expressão é avaliada. Caso o resultado seja false e não exista uma segunda
expressão então a exceção AssertionError é lançada. Caso o resultado seja
false e exista uma segunda expressão então ela é avaliada e o resultado é
passado para o construtor da exceção AssertionError antes de ser lançada.
Caso o resultado seja da primeira expressão seja true então a segunda expressão,
caso exista, não será avaliada.
O exemplo III-24 mostra um exemplo do uso de assertivas para garantir
que uma determinada condição seja verdadeira.
...
assert b > 0;
int x = a/b;
...
Exemplo III-24. Uso de assertivas.
Habilitando e Desabilitando Assertivas
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
64
As assertivas podem ser habilitadas/desabilitadas por flags na linha de
comando e chamadas a métodos durante a execução. A habilitação/desabilitação
pode ter uma atuação que varia desde o envolvimento de uma única classe até
um pacote ou todo o programa. Por default as assertivas estão desabilitadas.
Habilitação por flags
A forma geral para a habilitação de assertivas é:
java [-enableassertions
:<nome classe>]
|
-ea]
[:<nome
package>“...”
|
O flag sem argumento habilita as assertivas para todas as classes. Se for
seguindo por o nome de um pacote seguido por “...” as assertivas serão
habilitadas para o pacote especificado e todos os subpacotes. Se for seguido
apenas de “...”as assertivas serão habilitadas para o pacote do diretório corrente e
todos os subpacotes. Se o argumento não terminar com “...”, então as assertivas
serão habilitadas apenas para a classe especificada. Abaixo segue um exemplo
de habilitação de assertivas:
java –ea:br.ufv.dpi.apo... Teste
A desabilitação de assertivas segue a mesma lógica, mudando apenas o
flag que passa ser –disableassertions ou –da.
Habilitação por métodos
Os seguintes métodos da classe ClassLoader podem ser invocados para
habilitar/desabilitar as assertivas em tempo de execução:
void setDefaultAssertionStatus(boolean status)–
habilita/desabilita por
default as assertivas.
void setPackageAssertionStatus(String nomepack, boolean status)–
habilita/desabilita as assertivas no pacote e subpacotes.
void setClassAssertionStatus(String nomeclasse, boolean status)–
habilita/desabilita as assertivas na classe.
void clearAssertionStatus()– retorna ao default.
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
65
Java na Prática
66
Capítulo IV Classes, Pac k ages e
Interfaces
A unidade fundamental de programação em Java é a Classe. Não é
possível fazer um programa em Java que não tenha pelo menos uma classe. Isso
não é verdade para todas as linguagens Orientadas a Objeto. Em C++, por
exemplo, é possível fazer um programa que não use classe. Em Java todas as
variáveis e métodos de um programa Java devem ser definidos dentro de uma
classe. Um programa que não contenha classes não é um programa orientado a
objetos. No entanto, o fato de um programa conter classes também não o torna
merecedor do título de “orientado a objetos”. A programação orientada a objetos
é mais um estilo de programação do que um conjunto de palavras-chave
colocadas em um programa. Porém, é obviamente melhor implementar este
estilo usando uma linguagem que oferece suporte a ele. Neste capítulo
estudaremos o suporte oferecido por Java a este estilo de programação. Não só a
implementação de classes será vista, como também conceitos relacionados,
como pacotes e interfaces.
Classes
No Capítulo II foram discutidas Classes e Objetos do ponto de vista de
modelagem e de implementação. Nesta seção será detalhada a implementação de
classes em Java. A definição de uma classe em Java obedece a seguinte forma
geral:
[public] class <Identificador>
{
<corpo da classe>
}
A palavra reservada public é opcional e indica que a classe pode ser
referenciada por qualquer outra classe além do próprio pacote (conceito que será
visto mais adiante). Apenas uma classe por arquivo pode ser precedida pela
palavra reservada public e o nome do arquivo tem que ser idêntico ao nome
desta classe. Se a declaração da classe não for precedida de public, então ela só
poderá ser referenciada por outras classes do mesmo pacote. Os modificadores
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
67
de acesso private e protected não podem ser aplicados às classes. O
<Identificador> é usado para referenciar a classe. No <corpo da classe>
são definidos os atributos e métodos da classe. Na comunidade de
programadores Java existe a convenção de associar às classes identificadores
que iniciem com letra maiúscula, enquanto que os identificadores de variáveis e
métodos devem iniciar com letra minúscula. No exemplo IV-1 é mostrada a
definição da classe de uma classe com o identificador Pessoa. Nele foi definido
um construtor para atribuir valores aos atributos e definido os métodos para
acesso aos atributos. Note que os métodos que atribuem valores às variáveis da
classe iniciam com a palavra set e os métodos que retornam os valores
armazenados nas variáveis da classe iniciam com a palavra get. Este padrão de
nomeação de método foi adotado a partir da versão 1.1 da linguagem e é muito
importante seguí-lo, principalmente se pretendemos implementar JavaBeans,
como será visto no Capítulo X.
public class Pessoa
{
String nome;
String telefone;
String endereço;
public Pessoa(String n, String t, String e)
{
nome = n; telefone = t; endereço = e;
}
public void setNome(String n) { nome=n; }
public void setTelefone(String t) { telefone = t; }
public void setEndereço(String e) { endereço = e; }
public String getNome() { return nome; }
public String getTelefone() { return telefone; }
public String getEndereço() { return endereço; }
}
Exemplo IV-1. Definição da classe Pessoa.
Unidade de Compilação
Cada arquivo fonte Java é uma unidade de compilação. Ao compilar o arquivo fonte o
compilador irá gerar um arquivo .class para cada classe constante no arquivo. O nome de
cada arquivo será o nome da cada classe.
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
68
Os atributos ou variáveis declarados na classe são criados somente
quando os objetos são criados. Ou seja, cada objeto da classe Pessoa terá a sua
variável nome, telefone e endereço. Portanto, a variável pertence à instância
da classe e não à classe. Variáveis que pertencem ao objeto são chamadas
variáveis de instância. Com os métodos o raciocínio é o mesmo, ou seja, para
podermos usar o método getNome() é preciso criar um objeto da classe Pessoa
e usar o método getNome() deste objeto. Métodos que pertencem ao objeto são
chamados de métodos de instância. Como veremos mais adiante, podemos usar a
palavra reservada static para declarar variáveis e métodos que pertencem à
classe (métodos e variáveis de classe).
Os métodos definem operações sobre os atributos. Eles possuem a forma
geral:
<modificador> <tipo> <identificador>(<parâmetros>)
{
<corpo do método>
}
onde <modificador> é um modificador de acesso, <tipo> é o tipo do valor de
retorno do método, <identificador> é o identificador do método e
<parâmetros> é uma lista de parâmetros. O corpo do método é composto de
comandos, expressões e declarações de variáveis locais ao método. Os métodos
do exemplo IV.1 são muito simples. Eles apenas retornam um valor ou atribuem
um valor aos atributos do objeto.
Construtores
Podemos observar no exemplo IV-1 que existe um método público com o
mesmo nome da classe e que não define um valor de retorno. Métodos como
esse são denominados de construtores e são chamados pelo operador new. Uma
classe pode não ter construtor ou ter vários, desde que tenham diferentes tipos de
argumentos. O interpretador decidirá qual chamar a partir dos argumentos
passados para o construtor. Se o programador não declarar nenhum construtor
então o compilador irá criar automaticamente um construtor default para a
classe.
Valor de Retorno
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
69
Com exceção dos construtores todos os outros métodos precisam retornar
algum valor. Se programador não precisa que o método retorne algum valor,
então ele deve especificar que o método irá retornar o valor void que é o valor
vazio. Se o método não foi definido com valor de retorno void e não é um
construtor então é preciso que no corpo do método seja indicado o valor de
retorno. Esta indicação deve ser feita em toda ramificação de código que leve ao
fim da execução do método e é feita com a palavra chave return seguida de
uma expressão:
return <expressão>
A expressão pode ser uma variável, um literal ou qualquer outra
expressão que gere um valor do tipo especificado para o retorno da função. O
exemplo IV-2 mostra um método que retorna um inteiro. Note que toda
ramificação de código que leve ao fim da execução do método necessita de um
comando return.
public class ExemploIV2
{
...
public int calc(int a)
{
if (a==0) return 1;
else return a*a;
}
}
Exemplo IV-2. Método que retorna um inteiro.
Objetos
Para se criar um objeto de uma classe (ou uma instância da classe) é
preciso primeiro declarar uma variável que irá referenciar o objeto, para depois
criar o objeto. A declaração de um referência a um objeto é feita obedecendo a
seguinte forma geral:
<nome da classe> <identificador>;
Por exemplo, para declarar uma referência à um objeto da classe Pessoa
devemos adicionar a seguinte linha ao código.
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
70
Pessoa p1;
Pode-se declarar mais de uma referência a objetos de uma classe em uma
única declaração, separando os identificadores por vírgula.
Pessoa p1, p2;
Para criar um objeto usa-se o operador new conforme a forma geral
abaixo:
<identificador> = <nome da classe>(<argumentos>);
onde <argumentos> é uma lista de argumentos que serão passados para um
método especial da classe, denominado de construtor. Por exemplo, para criar
um objeto do tipo Pessoa podemos usar a declaração abaixo:
P1 = new Pessoa(“Ana”,”432-6969”,”Rua 17 134”);
Podemos também combinar a declaração da referência com a criação do
objeto em uma única linha de código.
Pessoa P1 = new Pessoa(“Ana”,”432-6969”,”Rua 17 134”);
Uma vez criado o objeto podemos acessar os elementos do objeto por
meio da referência ao objeto e do operador ‘.’. Por exemplo para acessar o
método getNome() do objeto referenciado pela variável P1 devemos usar o
seguinte código:
P1.getNome();
Para acessar um atributo diretamente a sintaxe é mesma. Por exemplo,
para acessar o atributo nome diretamente basta usar a seguinte linha de código.
P1.nome;
O exemplo IV-3 mostra a criação de um objeto do tipo Pessoa e o acesso
a seus membros. Para que a classe seja executável diretamente pela máquina
virtual é preciso que ela possua um método main() que servirá como ponto de
entrada para a execução. Podemos então, inserir código no método main() para
criar uma instância da classe para que, a partir de então, possamos a acessar os
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
71
membros do objeto. O método main() existe antes da criação da instância, e
portanto pode ser acessado pela máquina virtual, porque foi declarado com o
modificador static. Isto significa que o método pertence à classe (método de
classe) e não ao objeto (método de instância).
public class Pessoa
{
...
// igual ao exemplo IV.1
...
public static void main(String args[])
{
Pessoa p;
p = new Pessoa(“Ana”,”432-6969”,”Rua 17 134”);
// Acessa os dados via métodos
System.out.println(“Nome:”+getNome());
System.out.println(“Telefone:”+ getTelefone());
System.out.println(“Endereço:”+ getEndereço());
// Altera o endereço
setEndereço(”Rua 17 138”)
// Acessa os atributos diretamente
System.out.println(“Endereço:”+ endereço);
}
}
Exemplo IV-3. Definição da classe Pessoa.
As implementações da classe Pessoa mostradas nos exemplos IV-1 e
IV-3 possuem alguns problemas que iremos abordar gradativamente. O primeiro
problema está relacionado com o acesso direto aos atributos. Em um programa
bem projetado o acesso aos atributos da classe a partir de métodos definidos em
outras classe é restringido de modo a diminuir a dependência em relação à
representação interna de uma classe. Quanto maior for esta independência maior
é a facilidade para manutenção do programa. Para se restringir o acesso aos
membros da classe deve-se preceder a declaração dos membros com palavras
reservadas denominadas de modificadores de acesso que serão descritos a seguir.
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
72
Modificadores de acesso
Os modificadores de acesso tem por objetivo definir a visibilidade de um
membro de uma classe (atributo ou método) em relação à outras classes. A
tabela IV-1 mostra os modificadores de acesso disponíveis em Java.
Modificador
default
public
protected
private
Descrição
Somente classes do mesmo package possuem acesso
Todos possuem acesso
Apenas os membros da classe e subclasse
Apenas os membros da classe
Tabela IV-1. Modificadores de acesso.
Quando não é especificado nenhum modificador de acesso é assumido o
modificador default, também chamado de friend ou escopo de pacote. Ou seja,
não existe uma palavra reservada default. O acesso default determina que todas
as classes dentro do mesmo package (pacote) podem acessar os membros da
classe corrente. O conceito de package será detalhado mais adiante, no entanto
podemos adiantar que um package é um agrupamento de classes e interface
(também será vista mais adiante). Toda classe e interface pertence a um
package, mesmo que o programador não indique o package explicitamente. Se o
programador não indicar o package então será assumido que a classe ou
interface pertence ao package default.
O modificador public determina que todas as classes podem acessar o
membro. Já o modificador protected limita o acesso ao membro apenas aos
membros da própria classe e aos membros das subclasses da classe. Finalmente,
o modificador private limita o acesso ao membro apenas aos membros da
própria classe.
O exemplo IV-4 é uma redefinição da classe Pessoa, explicitando os
modificadores de acesso dos atributos com objetivo de limitar a visibilidade dos
mesmos.
public class Pessoa
{
protected String nome;
protected String telefone;
protected String endereço;
...
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
73
// igual ao exemplo IV.1
...
}
Exemplo IV-4. Definição da classe Pessoa com os atributos protegidos.
O modificador de acesso usado para os atributos no exemplo IV-4 foi o
Portanto, se for criada uma subclasse da classe Pessoa, esta poderá
acessar diretamente os atributos da classe Pessoa. O exemplo IV-5 mostra a
definição da classe Funcionario como subclasse de Pessoa. Note que o
atributo de instância salario da classe Funcionario é definido com o
modificador de acesso private, impedindo, dessa forma, acessos diretos ao
atributo a não ser por membros da própria classe. Note também que o construtor
da classe Funcionario possui uma chamada a um método super. Esta chamada
representa, na verdade, uma chamada ao construtor da superclasse. Isto será
visto com maiores detalhes mais adiante.
protected.
public class Funcionario extends Pessoa
{
private double salario;
public Funcionario(String n, String t, String e, double s)
{
super(n,t,e);
salario = s;
}
public void setSalario(double s) { salario = s; }
public double getSalario() { return salario; }
}
Exemplo IV-5. Definição da classe Funcionario.
Para utilizar a classe Funcionario pode-se criar uma terceira classe,
como mostra o exemplo IV-6, com o um método main() que serve de ponto de
entrada para o início da execução. Foi colocado propositadamente uma tentativa
de acesso ao atributo privado por um método externo à classe do atributo, de
modo a ilustrar um acesso não permitido.
public class TesteFun
{
public static void main(String a[])
{
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
74
Funcionario f;
f = new Funcionario(“Carlos”,”555-7777”,
“Av Nova 32”,3000.0);
System.out.println(“Nome:”+f.getNome());
// Acesso Ilegal. Use o método.
System.out.println(“Salario:”+f.salario);
}
}
Exemplo IV-6. Utilizando a classe Funcionario.
Outros Modificadores
Além dos modificadores de acesso existem outros modificadores que
estabelecem significados distintos da visibilidade. A tabela IV-2 exibe os
modificadores adicionais.
Modificador
static
final
synchronized
native
Descrição
A variável ou método é comum a todas as instâncias da
classe.
O valor da variável não pode ser modificado.
atribui o monitor do objeto ao thread corrente.
indica que se trata de um método nativos.
Tabela IV.2– Modificadores de adicionais.
O modificador static
Um atributo declarado com o modificador static significa que é um
atributo da classe e não de instância. Em outras palavras, existirá apenas um
atributo, ocupando uma única posição de memória, em vez de um atributo para
cada instância, cada um com sua própria posição de memória. Como resultado, o
atributo existirá antes mesmo que qualquer objeto seja criado e, se for público,
poderá ser acessado prefixando-o com o nome da classe e o operador “.”. O
exemplo V-7 mostra uma classe com um atributo static. Note que o não é
preciso declarar uma instância da classe X para que o atributo exista e para
acessá-lo basta prefixá-lo como nome da classe.
class X
{
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
75
static public int tot =0;
}
public class Main
{
public static void main(String a[])
{
X.tot = 2;
System.out.println(“Valor de tot:”+X.tot);
}
}
Exemplo IV-7. Classe com atributo static.
Outro efeito é que o atributo será compartilhado por todas as instâncias
da classe. Desta forma, se um objeto alterar o conteúdo do atributo, a alteração
afetará todos os outros objetos da classe. O exemplo V-8 mostra a declaração de
dois objetos da classe X. Um dos objetos altera o valor do atributo static e o
outro imprime o valor do atributo. Isto mostra que as alterações feitas por um
objeto no atributo é percebida por todos os outros objetos da classe.
class X
{
static public int tot =0;
public void inc() {tot++;}
}
public class Main
{
public static void main(String a[])
{
X x1, x2;
x1 = new X();
x2 = new X();
// x1 modifica o valor
x1.inc();
System.out.println(“Valor de tot:”+x2.tot);
}
}
Saída:
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
76
Valor de tot:1
Exemplo IV-8. Objetos com atributo static.
O modificador static também pode ser usado na declaração de
métodos. Neste caso o método fica ligado à classe e não à instância, sendo
denominado de método de classe. Como consequência o método pode ser
acessado antes de existir um objeto da classe. Vários exemplos mostrados neste
livro tem pelo menos um método static: o método main(). Ele serve como
ponto de entrada para a execução da aplicação Java, uma vez que inicialmente
não existem objetos. Os métodos static não podem acessar atributos e outros
métodos não estáticos da classe, a não ser que exista uma declaração de um
objeto da classe no corpo do método. Os métodos estáticos são muito usados em
classes utilitárias. Classes utilitárias são classes que servem para agrupar
métodos que prestam algum tipo de serviço a outras classes. Por exemplo, o
método exit() é um método estático da classe System. Ele serve para encerrar
a execução da máquina virtual corrente. Sendo um método estático não é preciso
criar um objeto da classe System para acessá-lo. A classe System é uma classe
utilitária, contendo vários métodos e atributos estáticos úteis para outras classes.
O Modificador final
O modificador final pode ser usado em atributos, métodos e classes. O
modificador final impede se modifique o que está sendo prefixado. Um atributo
prefixado com final indica que uma vez definido seu valor ele não será
alterado. Ou seja, é uma constante. A definição do valor do atributo pode ser
feito tanto na declaração como durante a execução do programa, sendo que esta
última possibilidade não era permitida na versão 1.0 da linguagem Java. O
exemplo IV-9 mostra o uso do modificador final em atributos.
class X
{
public int k =0;
}
public class Main1
{
public final int i = 1;
public final int j;
public final X x = new X();
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
77
public Main1(){j = 2;}
public static void main(String a[])
{
Main1 m = new Main1();
m.x.k= 3;
// Erro : o atriuto não pode ser modificado
// m.j = 4;
System.out.println("Valor de k:"+ m.x.k);
System.out.println("Valor de j:"+ m.j);
System.out.println("Valor de i:"+ m.i);
}
}
Exemplo IV-9. Uso do modificador final em atributos.
Note que no exemplo IV-9 o atributo j é inicializado no construtor e não
na declaração. No exemplo o atributo k do objeto referenciado pelo atributo x é
modificado, mesmo sendo x um atributo final. Isto ocorre porque x é uma
referencia a um objeto e não o próprio objeto, portanto, no caso de referencias a
objeto, o modificador final implica que o atributo não pode referenciar outro
objeto, mas não significa que o objeto referenciado não possa ser modificado.
Um método declarado com o modificador com final não pode ser
sobrescrito em uma subclasse. Alguns compiladores podem aproveitar isso e
verificar se é possível definir as chamadas a esses métodos como inline. Nas
chamadas inline o compilador substitui a chamada ao método pelo o código do
método, tornando mais eficiente a execução do programa. O exemplo IV-10
mostra o uso do modificador final em um método.
class X
{
public int k =0;
public final void mostra()
{
System.out.println("Valor de k:"+ k);
}
}
class SX extends X
{
// Erro: Sobrescrita ilegal
public void mostra()
{
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
78
System.out.println("k:"+ k);
}
}
Exemplo IV-10. Uso do modificador final em métodos.
Uma classe declarada com o modificador com final impede que sejam
criadas subclasses desta classe. O programador pode decidir fazer isso por
razões de projeto ou de segurança, já que neste caso ele terá certeza que a
funcionalidade da classe não será alterada. Outra razão pode ser eficiência, uma
vez que os métodos de uma classe final também são final, e portanto podem
ser otimizados para serem tratados como inline.
Os Modificadores synchronized e native
O modificador synchronized é para controlar o acesso a áreas críticas
no processamento concorrente em Java. É usado em métodos e blocos de
comandos, e seu uso será discutido em detalhes no Erro! A origem da
referência não foi encontrada..
O modificador native é usado em métodos para indicar que o método
foi implementado em outra linguagem de programação. O programador deve
apenas indicar a assinatura do método, como mostrado abaixo:
native int meuMetodo(int a);
O uso de chamadas nativas restringe a portabilidade do programa. No
momento a linguagem Java só suporta chamadas nativas implementadas na
linguagem C/C++.
Referências Compartilhadas
Um outro problema das implementações da classe Pessoa mostradas nos
exemplos IV-1 e IV-3 está relacionado com o compartilhamento de referências.
No construtor, assim como nos métodos setXXX, o argumento é atribuído
diretamente às variáveis de instância. Como toda variável de objeto em Java é na
verdade uma referência para o objeto então as variáveis de instância da classe
Pessoa e os argumentos do construtor e dos métodos setXXX são referências a
objetos do tipo String. Portanto, ao atribuir diretamente o argumento às
variáveis de instância geramos um compartilhamento de referências. Essa
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
79
situação é melhor ilustrada graficamente, como pode ser visto na figura IV-1,
onde é mostrado o compartilhamento entre o argumento n e a variável de
instância nome após a atribuição.
O problema do compartilhamento de referências, é que ele permite
acesso aos objetos referenciados internamente pelos objetos. Com isso é possível
modificar, com métodos externos ao objeto, o objeto referenciado. No entanto,
no caso particular dos objetos da classe String, isso não chega a ser um
problema. Strings são constantes, ou seja, seus valores não podem ser alterados
após a sua criação e, portanto, objetos Strings podem ser compartilhados sem
riscos. Toda alteração em um objeto do tipo String retorna um novo objeto
String, deixando a original inalterada.
Antes da atribuição
n
nome
Objeto
null
Após a atribuição
n
Objeto
nome
Figura IV-1.Compartilhamento de referencias.
Se o programador desejar alterar o valor de uma cadeia de caracteres sem
criar um novo objeto, então ele deve optar por utilizar a classe StringBuffer
que implementa uma sequência mutável de caracteres. No entanto, é preciso
estar alerta para o problema de compartilhamento de referências. O trecho de
código do exemplo IV-11 mostra uma ocorrência de compartilhamento de
referência. No exemplo a classe Pessoa é definida usando a classe
StringBuffer para armazenar as sequências de caracteres. Os objetos da classe
X possuem uma referência a um objeto do tipo Pessoa. Na construção do objeto
Pessoa são passados como parâmetros as referências dos objetos StringBuffer
do objeto da classe X, gerando um compartilhamento de referencias. Quando é
invocado o método m1() do objeto da classe X, este concatena a sequência “fim”
ao StringBuffer referenciado por n. Como n compartilha o mesmo objeto com
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
80
a variável de instância nome do objeto Pessoa, então a alteração também afeta o
objeto Pessoa, ocasionando um efeito colateral, provavelmente indesejável.
Para se evitar esta situação é necessário que o objeto crie uma cópia do
objeto que está recebendo e retorne uma cópia do objeto que referencia. No caso
de objetos do tipo StringBuffer basta construir um novo objeto, passando
como parâmetro a String correspondente ao StringBuffer anterior, como
mostrado abaixo:
nome = new StringBuffer(n.toString());
No entanto, no caso de objetos mais complexos é mais complicado criar
uma cópia por meio do construtor. Neste caso é necessário criar uma cópia
usando o método clone(). Este método será visto na próxima seção.
class Pessoa
{
StringBuffer nome;
StringBuffer telefone;
StringBuffer endereço;
public Pessoa(StringBuffer n, StringBuffer t,
StringBuffer e)
{
nome = n; telefone = t; endereço = e;
}
// O restante da da classe é semelhante ao exemplo VI.1
// ...
}
public class X
{
StringBuffer n = new StringBuffer("Pedro");
StringBuffer t = new StringBuffer("324-6789");
StringBuffer e = new StringBuffer("Rua 17/148 Natal");
Pessoa p;
public X() {p = new Pessoa(n,t,e);}
public void m1() { n.append("fim");}
public static void main(String a[])
{
X x = new X();
x.m1();
System.out.println(x.p.nome);
}
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
81
}
Exemplo IV-11. Exemplo de compartilhamento de referência.
Copiando Objetos
Como visto na seção anterior, a atribuição envolvendo duas referencias a
objetos acarreta um compartilhamento do objeto e não uma cópia do objeto
original. No entanto, em algumas situações não queremos compartilhar um
objeto e sim criar uma cópia de um objeto que já existe, ou seja um clone.
Existem dois tipos de modos de se clonar um objeto:
1- Cópia rasa (shallow copy) - neste caso os atributos do objeto resultante
recebem os valores dos atributos correspondentes no objeto original. Os
objetos referenciados pelo objeto original serão compartilhados pelo
objeto resultante. A figura IV-2 ilustra este tipo de cópia. Note o
compartilhamento das referencias.
Figura IV-2.Cópia rasa entre os objetos A e D.
2- Cópia profunda (deep copy) - neste caso não só o objeto original é
copiado como também todos os objetos por ele referenciados. Este tipo
de cópia pode ser bastante ineficiente. A figura IV-3 ilustra este tipo de
cópia.
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
82
Figura IV-3.Cópia profunda entre os objetos A e D.
Java oferece suporte para a cópia rasa. Para que a classe esteja apta a ser
clonada é preciso que ela implemente a interface Cloneable (interfaces serão
discutidas mais adiante). A interface Cloneable não declara nenhum método,
portanto, para implementar a interface basta apenas escrever Cloneable após a
palavra reservada implements na declaração da classe, como mostrado no
exemplo IV-12.
class Clonavel implements Cloneable
{
int a[];
public Clonavel(int n)
{
a = new int[n];
}
public void setElemento(int i, int el)
{
if (i < a.length) a[i] = el;
}
public int getElemento(int i)
{
if (i < a.length) return a[i];
else return 0;
}
public Object clone()
{
try {
return super.clone();
} catch (CloneNotSupportedException e) {return null;}
}
}
public class CloneTeste
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
83
{
public static void main(String a[])
{
Clonavel x1 = new Clonavel(2);
x1.setElemento(0,1);
x1.setElemento(1,2);
Clonavel x2 = (Clonavel) x1.clone();
x1.setElemento(0,3);
System.out.println("x1:"+x1.getElemento(0)+", "+
x1.getElemento(1));
System.out.println("x2:"+x2.getElemento(0)+", "+
x2.getElemento(1));
}
}
Saída
x1:3, 2
x2:3, 2
Exemplo IV-12. Cópia rasa.
Além de implementar a interface é preciso sobrescrever o método
clone() da classe Object, uma vez que este método é declarado com o
modificador protected e portanto não é visível fora da linha hierárquica. Se o
objetivo for uma cópia rasa então basta invocar o método clone() da classe
Object de dentro método que o está subscrevendo, como feito no exemplo
IV-12, por meio da palavra chave super. Note que é preciso capturar uma
exceção que é lançada (a CloneNotSupportedException) caso a classe não
suporte a clonagem (se não implementar a interface Cloneable). Note também
que o método clone() da classe Object retorna um Object e, portanto, precisa
ser feita uma conversão explicita para o tipo apropriado.
Para demonstrar que o exemplo IV-12 implementa uma cópia rasa é feito
um teste onde são criados dois objetos, sendo um deles o clone do outro. A saída
do programa mostra que ao alterarmos um objeto referenciado por um dos objeto
que foram duplicados a alteração afeta o outro, uma fez que as referências são
compartilhadas.
Se o usuário desejar realizar uma cópia profunda, então ele deve fornecer
o código necessário ao sobrescrever o método clone(). Neste caso ele deve
invocar os métodos clone() dos objetos referenciados. Esta pode ser uma tarefa
bastante complexa uma vez que um objeto pode referenciar vários objetos e
estes podem referenciar outros objetos e assim sucessivamente, em uma longa
cadeia de referências. Além disso é preciso contar com o fato dos objetos
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
84
referenciados terem sobrescrito o método clone(). O exemplo IV-13 mostra
como implementar uma cópia profunda para o exemplo anterior.
class Clonavel implements Cloneable
{
int a[];
public Clonavel(int n)
{
a = new int[n];
}
public void setElemento(int i, int el)
{
if (i < a.length) a[i] = el;
}
public int getElemento(int i)
{
if (i < a.length) return a[i];
else return 0;
}
public Object clone()
{
Clonavel o = null;
try {
o = (Clonavel) super.clone();
} catch (CloneNotSupportedException e) {return null;}
o.a = (int []) a.clone();
return o;
}
}
public class CloneTeste
{
public static void main(String a[])
{
Clonavel x1 = new Clonavel(2);
x1.setElemento(0,1);
x1.setElemento(1,2);
Clonavel x2 = (Clonavel) x1.clone();
x1.setElemento(0,3);
System.out.println("x1:"+x1.getElemento(0)+", "+
x1.getElemento(1));
System.out.println("x2:"+x2.getElemento(0)+", "+
x2.getElemento(1));
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
85
}
}
Saída
x1:3, 2
x2:1, 2
Exemplo IV-13. Cópia profunda.
Note que no exemplo IV-13 a saída do programa mostra que ao
alterarmos um objeto referenciado por um dos objeto que foram duplicados a
alteração não afeta o outro, uma fez que as referências foram clonadas. Isso foi
possível porque os arrays sobrescrevem o método clone(). E quanto às classes
da biblioteca padrão do SDK? Elas sobrescrevem o método clone()? A resposta
é: poucas sobrescrevem. Portanto, o programador deve estar atento e verificar se
é possível invocar o método clone() de uma classe da biblioteca padrão ou se
terá que proceder de outra forma.
O objeto this
Em algumas situações é necessário referenciar o próprio objeto corrente.
Por essa razão todo objeto possui um atributo especial identificado por this
que é uma referência para o próprio objeto. O exemplo IV-14 mostra um uso
típico do atributo this. Ele mostra um método cujo parâmetro formal possui o
mesmo nome de um atributo de instância. Para distinguí-los é necessário
qualificar o atributo da instância com o atributo this.
public class objetoGeo
{
protected Color cor;
protected int x, y;
public objetoGeo(Color cor, int x, int y)
{
this.cor = cor; this.x=x; this.y = y;
}
public Color retCor() {return cor};
}
Exemplo IV-14. Exemplo de uso do atributo this.
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
86
Outro uso típico e quando queremos passar a instância corrente como
argumento de um método. Como mostra o exemplo IV-15.
class X
{
...
public void mx(Y y)
{
...;
}
}
class Y
{
X x;
public void my()
{
x.mx(this);
...
}
}
Exemplo IV-15. Exemplo de uso do atributo this como argumento.
No exemplo IV-15 o objeto da classe Y passa ele próprio como
argumento do método mx() do objeto da classe X.
Packages
Pacotes (Packages) é a solução proposta por Java para agrupar Classes e
Interfaces relacionadas para compor bibliotecas. As Interfaces serão vistas mais
adiante. Organizando as classes em pacotes evita-se a colisão entre os nomes das
classes. No caso dos métodos isso não é necessário, pois métodos com o mesmo
nome em classes diferentes são facilmente distinguidos uma vez que são
qualificados pelos objetos das classes. No entanto como distinguir classes com o
mesmo nome? Esse problema é muito comum, principalmente quando se
trabalha em equipe. Como impedir que um programador trabalhando no mesmo
projeto crie uma classe com o mesmo nome que outra criada por outro
programador? Na hora de unir as classes o sistema não irá compilar devido ao
problema de redeclaração de classes. Sendo Java uma linguagem para atuar na
Internet o problema é ainda mais grave: como impedir que uma classe que foi
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
87
baixada de outra máquina não possua o mesmo nome de uma classe local? Para
contornar todos estes problemas foram criados os Pacotes.
Usando Packages
Toda classe pertence a um pacote. No caso do programador não indicar a
que pacote pertence a classe, o compilador irá assumir que a classe pertence ao
pacote default que é o diretório corrente. Para se usar uma classe definida em
outro package é preciso usar a palavra chave import seguida do nome da
classe qualificada pelo nome do pacote como mostra a forma geral abaixo:
import nome_Package.nomeClasse;
Por exemplo, para importar a classe Color do pacote java.awt o
programador deverá usar a diretiva abaixo:
import java.awt.Color;
Se o programador quiser importar todas as classes do pacote java.awt
basta usar o caractere ´*` no lugar do nome da classe:
import java.awt.*;
É possível usar uma classe declarada em outro pacote sem usar a palavra
chave import. Nesse caso é necessário qualificar o nome da classe com o nome
do pacote toda vez que a classe for referenciada. Por exemplo, no caso da classe
Color seria necessário qualificá-la da seguinte forma:
java.awt.Color
Existem dois pacotes que não precisam ser importados para que suas
classes possam ser usadas sem a qualificação: o pacote default e o pacote
java.lang. O pacote default agrupa todas a classes que estão no diretório
corrente e o pacote java.lang agrupa as classes do núcleo básico da
linguagem.
Criando Packages
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
88
Para incluir uma unidade compilação em um pacote basta que o
programador inclua a seguinte declaração no arquivo.
package nomepacote;
Importante: a declaração acima precisa ser a primeira declaração no
arquivo.
O nome do pacote pode ser composto por vários nomes separados pelo
caractere ‘.’, como no caso do pacote java.awt.
As classes de um determinado pacote devem ser colocadas em um
diretório obedecendo a mesma estrutura do nome do pacote, a partir de algum
diretório constando na variável de ambiente classpath. A variável de ambiente
classpath indica os diretórios que servem como pontos de entrada para procura
de classes pela máquina virtual.
Por exemplo, suponha que eu resolva colocar as classes sobre objetos
geométricos em um determinado pacote, digamos br.com.alcione.geo. Para
indicar que unidade de compilação pertence a esse pacote devemos colocar a
diretiva adequada no início do arquivo fonte, como mostrado no exemplo IV-16.
package br.com.alcione.geo;
public class objetoGeo
{
protected Color cor;
protected int x, y;
public objetoGeo(Color cor, int x, int y)
{
this.cor = cor; this.x=x; this.y = y;
};
public Color retCor() {return cor};
}
Exemplo IV-16. Indicando que uma classe pertence ao pacote
br.com.alcione.geo.
Para que a máquina virtual consiga achar o pacote devemos colocá-lo em
diretório com a mesma estrutura do nome do pacote e que tenha como raiz
algum diretório constante na variável de ambiente classpath. Por exemplo,
suponha que a variável classpath contenha os seguintes diretórios:
CLASSPATH=.;C:\JAVA\LIB;C:\meujavalib
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
89
então o arquivo objetoGeo.class resultante da compilação do arquivo
objetoGeo.java pode ser colocado no diretório:
C:\meujavalib\br\com\alcione\geo
Note que a estrutura do diretório combina com a estrutura do nome do
pacote. Para usar a classe em outra unidade de compilação basta incluir a
seguinte diretiva no arquivo fonte:
import br.com.alcione.geo.*;
O formato do nome do pacote não foi escolhido por acaso. Se existe a
pretensão de usá-lo na Internet então deve-se adotar um nome que seja único na
rede. Para garantir esta unicidade utiliza-se o nome do domínio do criador do
pacote com a ordem dos campos invertida, evitando assim a colisão de nome na
Internet. Por exemplo, nome do pacote br.com.alcione.geo origina-se da
inversão do nome do domínio (alcione.com.br) concatenado com o nome geo.
Por simplicidade, na maioria dos exemplos adotados neste livro,
adotaremos o pacote default.
Empacotando packages em arquivos JARs
Um sistema pode possuir um grande número de classes organizadas em
pacotes. Essas classes espalhadas pela estrutura de diretório de um computador
pode se tornar um problema no momento da distribuição de uma aplicação. O
melhor seria organizar as classes em um único arquivo para poder ser
distribuída. Para solucionar esses e outros problemas foi introduzido a partir da
versão 1.1 da linguagem Java o formato de arquivo JAR (Java Archive). O
formato de arquivo JAR permite o agrupamento de vários arquivos em um único
arquivo. Além disso, o formato JAR permite a compressão de dados de modo
otimizar o armazenamento e a transmissão de dados. O principais benefícios do
uso do formato JAR são os seguintes:
•
•
Menor tempo de carga de Applets: Se todos os arquivos relacionados com
um Applet estão agrupados em um único arquivo JAR, apenas uma transação
HTTP será necessária para carregar o Applet. Este tempo será ainda menor
se os arquivos estiverem compactados.
Segurança: o conteúdo do arquivos JAR podem ser assinados digitalmente.
Deste modo, os usuários que reconhecerem a sua assinatura podem conceder
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
90
privilégios para acessar recursos que não estariam disponíveis, caso
contrário.
Portabilidade: a API para manipulação dos arquivos JAR é parte integrante
do núcleo da biblioteca de classes de Java, o que a torna independente de
plataforma.
•
Outros benefícios foram adicionados com o lançamento da versão 1.2 do
SDK, como por exemplo, informações sobre versão.
Os arquivos contidos em um arquivo JAR podem ser de tipos variados como,
como bytecodes, imagens e sons, podendo pertencer a um Applet, aplicação ou
simplesmente a uma biblioteca de classes. Além disso, um arquivo JAR pode
conter uma descrição dos arquivos armazenados, chamada de manifest. O SDK
vem com uma ferramenta para criação e manutenção de arquivos JAR. O
formato mais comum de invocação do programa é:
jar [opções] destino arquivo(s)-de-entrada
onde destino é o nome do arquivo, arquivo(s)-de-entrada representa o nome
dos arquivos que serão incluídos no arquivo JAR e opções é uma coleção de
letras com o seguinte significado:
Opções
c
t
x
x file
f
m
v
O
M
Descrição
Cria um arquivo novo.
Lista o conteúdo.
Extrai todos os arquivos.
Extrai o arquivo especificado.
Indica que o nome do arquivo de saída, caso seja a criação de um arquivo, ou
de entrada caso contrário, será especificado. Sem essa opção o programa jar
assume que saída ou a entrada será a padrão.
Indica que o primeiro argumento será o nome de um arquivo manifest criado
pelo usuário.
Gera saída com os detalhes da execução.
Não comprime os arquivos. Usada para criar arquivos JAR que podem ser
colocados no classpath.
Não cria o arquivo manifest.
Tabela IV-3. Opções para uso do comando jar.
Se um subdiretório for incluído nos arquivos de entrada então o
subdiretório será automaticamente inserido no arquivo JAR, incluindo os seus
subdiretórios. As informações sobre o caminho até o arquivo é também
armazenada.
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
91
Para invocar uma aplicação armazenada em um arquivo JAR é necessário
passar o nome do arquivo JAR como parâmetro. Na versão JDK1.1 é preciso
usar o comando jre no lugar do comando java. Por exemplo, suponha que
armazenamos uma aplicação, cuja classe principal é Mainclass, em um arquivo
arq.jar. Para executá-la basta digitar o comando:
jre -cp arq.jar Mainclass
onde a opção cp indica que o arquivo arq.jar deve ser anexado ao classpath.
Já na versão SDK1.2 usa-se o comando java, sendo que o arquivo JAR deve
conter um arquivo manifest indicando a classe principal:
java -jar arq.jar
A tabela VII-4 mostra alguns exemplos do uso do utilitário que gera
arquivos JAR.
jar cf meuarq.jar *.class
jar cmf meuarq.jar
meuManifest.mf *.class
jar tf meuarq.jar
Cria um arquivo JAR com o nome meuarq.jar
contendo todos os arquivos de classes do diretório
corrente. Um arquivo manifest é gerado
automaticamente.
Como o exemplo anterior, porém o arquivo manifest
meuManifest.mf é adicionado.
Lista o conteúdo do arquivo meuarq.jar.
jar xf meuarq.jar
Extrai todos os arquivos contidos em meuarq.jar.
Tabela IV-4. Exemplos do uso do comando jar.
O arquivo manifest é um arquivo de texto onde cada linha possui par
diretiva-valor no formato:
<diretiva>:<valor>
As diretivas dependem da versão do SDK. Por exemplo, na versão 1.2 a
classe principal é indicada por meio da diretiva Main-Class, como mostrado
abaixo:
Main-Class: nome da classe
Criando os próprios arquivos manifest
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
92
Os arquivos manifest são criados automaticamente. O conteúdo original
de um arquivo manifest criado pelo SDK 1.3 é:
Manifest-Version: 1.0
Created-By: 1.3.0 (Sun Microsystems Inc.)
A opção m da ferramenta para criação de arquivos JAR permite adicionar
informações ao arquivo manifest default. É necessário que o usuário crie um
arquivo contendo as adições que devem ser feitas. O formato básico do comando
é:
jar cmf arq_adições arq_jar arquivo(s)
O arquivo de adições é simplesmente um arquivo texto onde cada linha é
par diretiva-valor. Por exemplo se o usuário quiser especificar que a classe que
deve ser usada como ponto de entrada é a classe Mainclass, então basta inserir a
seguinte linha no arquivo de adições:
Main-Class: Mainclass
O Mecanismo de Extensão
A partir da versão 1.2 a linguagem Java introduziu um mecanismo para
adicionar classes ao núcleo básico da linguagem, sendo uma alternativa ao uso
da variável de ambiente classpath. Este mecanismo é chamado de Mecanismo
de Extensão. O mecanismo de extensão também oferece suporte para envio de
classes pela rede para uso em Applets. As extensões são agrupadas em arquivos
JAR. Uma vez feito isso, pode-se transformar as classes em extensões
utilizando-se uma das duas formas:
1. Extensão Instalada – colocando o arquivo JAR em um local
predeterminado na estrutura de diretórios do ambiente de tempo de
execução de Java (Java Runtime Environment ou JRE).
2. Extensão de download –Extensão referenciando o arquivo JAR de uma
forma específica, a partir do manifest de outro arquivo JAR.
Extensão Instalada
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
93
Para incluir uma nova classe ou pacote no núcleo básico da linguagem
basta colocar o arquivo JAR que contém a classe no diretório /lib/ext do
ambiente de tempo de execução. O diagrama mostrado na figura IV-4 mostra a
localização deste diretório dentro da estrutura de diretórios do ambiente Java.
Figura IV-4. localização do diretório ext dentro da estrutura de diretórios
Java.
Por exemplo, suponha que desejamos acrescentar o pacote do exemplo
IV-16 como uma extensão da linguagem Java no ambiente local. Para isso
devemos criar primeiro o arquivo JAR. No ambiente DOS o comando seria o
abaixo, executado no diretório acima do diretório /br:
jar cvf geo.jar br\com\alcione\geo\objetoGeo.class
Após isso colocaríamos o arquivo geo.jar dentro do diretório /ext.
Para se usar a classe o procedimento é idêntico ao uso no caso do classpath, ou
seja basta colocar a seguinte diretiva no arquivo fonte:
import br.com.alcione.geo.*;
Extensão de Download
As extensões de download ocorrem quando arquivos JAR ou classes são
indicados como extensões de outros arquivos JAR. Por exemplo, suponha que
existem dois arquivos JAR, x.jar e y.jar, localizados no mesmo diretório.
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
94
Para tornar o arquivo y.jar uma extensão do arquivo x.jar basta colocar a
seguinte informação no manifesto do arquivo x.jar:
Class-Path: y.jar
Deste modo toda classe no arquivo x.jar pode referenciar as classes do
pacote y.jar. Se os arquivos não estiverem no mesmo diretório será necessário
indicar o caminho relativo até o arquivo da extensão. É possível indicar várias
extensões, separando-as por espaço ou usando várias diretivas Class-Path. Para
indicar um diretório como extensão basta adicionar o sufixo ‘/’ ao nome do
diretório. Por exemplo:
Class-Path: meudir/
Derivando classes
O reuso de componentes de programas é uma das principais metas dos
projetistas de linguagens de programação. A razão deste objetivo é que a
reutilização de componentes economiza esforços e acelera o processo de
desenvolvimento de sistemas de computadores. Essa meta pode ser atingida em
parte por meio da criação de classes que são extensões de classes predefinidas.
As extensões herdam todos os atributos e métodos da classe base ou superclasse.
Por exemplo, suponha que alguém tenha definido uma classe para representar os
dados de uma conta bancária:
Figura IV-5. Classe Conta.
Suponha também que além da conta corrente básica, mais dois tipos de
conta devam ser criadas: poupança e conta especial. A poupança possui todos os
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
95
atributos de uma corrente e mais um atributo contendo a data de abertura da
conta que será usada para cálculo do valor dos juros e correção. A conta especial
possui todos os atributos de uma corrente e mais um atributo contendo o valor de
limite de crédito.
No exemplo descrito acima seria um desperdício de tempo e código se
precisássemos codificar todos os atributos e métodos relacionados à classe
Conta nessas duas novas classes. Para evitar esta duplicação de esforço basta
declaramos essas duas novas classes como subclasses da classe Conta. Desta
forma apenas os atributos que não estão declarados na superclasse e métodos
associados precisam ser codificados, uma vez que os membros da superclasse
são herdados pelas subclasses. A figura IV-3 mostra o diagrama de classe
contendo o relacionamento entre as classes e o exemplo IV-17 mostra o código
Java correspondente.
Figura IV-3. Relação entre as classes.
public class Conta
{
protected long numero;
protected long cpf;
protected double saldo;
public Conta(long n, long c, double s)
{
numero = n; cpf = c; saldo = s;
}
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
public
public
public
public
public
96
long getNumero(){return numero;}
long getCpf(){return cpf;}
double getSaldo(){return saldo;}
void setSaldo(double s){saldo = s;}
void somaSaldo(double v){saldo += v;}
}
import java.util.*;
public class CPoupanca extends Conta
{
private Date dataAber;
public CPoupanca(long n, long c, double s, Date d)
{
super(n,c,s);
dataAber = d;
}
public Date getData(){return dataAber;}
}
public class CEspecial extends Conta
{
private double limite;
public CEspecial(long n, long c, double s, double l)
{
super(n,c,s);
limite = l;
}
public double getLimite(){return limite;}
public double getSaldo(){return saldo+limite;}
public void setLimite(double l){ limite = l;}
}
Exemplo IV-17. Implementação das Classes com herança.
Podemos notar que para declarar uma classe como subclasse de outra
basta colocarmos a palavra-chave extends após o nome da classe e o nome da
superclasse após a palavra-chave. Apenas o nome de uma superclasse é
permitido, portanto Java não permite que uma classe seja subclasse de mais de
uma classe (herança múltipla).
Herança Múltipla
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
97
A capacidade de possuir mais de uma superclasse é chamada de herança múltipla. A
linguagem C++ suporta a herança múltipla. No entanto, a implementação desta facilidade é
complexa e sua utilização tende a gerar erro. Além disso poucos são os casos que demandam o
uso desta solução, e mesmo nestes casos é possível utilizar outras soluções. Por essas razões a
linguagem Java não implementou a herança múltipla. Nos casos onde é necessário que um objeto
adote o comportamento de mais de uma classe devemos utilizar as interfaces, como será visto
mais adiante.
Todos os membros declarados com os modificadores public e
podem ser acessados diretamente na subclasse. O membros
declarados com o modificador private não são visíveis dentro da subclasse.
protected
super
Podemos notar também que nos construtores das subclasses do exemplo
IV-17 existem chamadas a um método super(). Este método representa a
chamada ao construtor da superclasse. Se não for chamado explicitamente o
compilador irá gerar código para chamar o construtor default da superclasse. No
entanto, se desejamos chamar explicitamente um construtor com argumentos
devemos usar o método super().
A classe Object
É importante salientar que toda classe em Java, com exceção da classe
Object é subclasse de alguma classe. Se o programador não declarar a classe
base então a classe base será a classe Object. Desta forma a classe Object é a
raiz da hierarquia de classes da linguagem Java. Sendo assim, todo objeto na
linguagem Java herda os seguintes métodos públicos e protegidos da classe
Object.
Métodos Públicos
public boolean equals(Object
obj)
public final native Class
getClass()
public native int hashCode()
public final native void
notify()
public final void notifyAll()
public String toString()
Descrição
Verifica se dois objetos são iguais. A
verificação é feita sobre o endereço do objeto .
Retorna a classe do objeto em tempo de
execução.
Retorna o valor hash do objeto.
Notifica um thread que está esperando sobre o
monitor do objeto.
Notifica todos os threads que está esperando
sobre o monitor do objeto.
Retorna uma representação em String do
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
98
public final void wait(long
timeout)
public final void wait(long
timeout, int nanos)
objeto.
Espera no monitor do objeto para ser
notificado por outro thread.
Espera no monitor do objeto para ser
notificado por outro thread.
Espera no monitor do objeto para ser
notificado por outro thread.
Métodos Protegidos
Descrição
protected native Object clone()
protected void finalize()
Cria uma cópia rasa do objeto corrente.
Chamado pelo coletor de lixo antes de liberar a
área de memória.
public final void wait()
Tabela IV-5. Métodos públicos e protegidos da classe Object.
Sobrescrita e Polimorfismo
Outro ponto interessante que podemos observar no exemplo IV-17 é que
a classe CEspecial possui um método com a mesma assinatura que um método
da superclasse. É o método getSaldo() que no caso da classe CEspecial deve
incluir o limite de crédito como parte do saldo.
Neste caso, o método da subclasse está sobrescrevendo ou ocultando o
método da superclasse. Toda vez que o método getSaldo() for invocado dentro
de algum método da classe CEspecial ou for chamado por meio de um objeto
da classe CEspecial o método chamado será o declarado na classe e não o
declarado na superclasse. No entanto, é possível invocar o método declarado na
superclasse de dentro de algum método da subclasse, bastando qualificar o
método com a palavra chave super como mostrado abaixo:
super.getSaldo();
Por exemplo, o método getSaldo() da classe CEspecial poderia ser
reescrito da seguinte forma:
public double getSaldo()
{
return super.getSaldo()+limite;
}
Suponha agora uma classe como mostrada no exemplo IV-18. O método
imprimeSaldo() espera um objeto da classe Conta mas recebe um objeto da
classe CEspecial. Isto não causa nenhum problema porque todo objeto da classe
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
99
CEspecial é também um objeto da classe Conta. No entanto fica a pergunta:
que método getSaldo() é invocado dentro o método imprimeSaldo()? O da
classe Conta ou o da classe CEspecial?
public class TestaConta
{
public void imprimeSaldo(Conta c)
{
System.out.println(“O Saldo e’:”+c.getSaldo());
}
public static void main(String a[])
{
TestaConta testa = new TestaConta();
CEspecial c= new CEspecial(1, 123456, 500.0, 100.0);
testa.imprimeSaldo(c);
}
}
Exemplo IV-18. Teste de polimorfismo.
A resposta é: o método chamado pertence a instância da classe
Cespecial. Portanto a regra é a seguinte: sempre é chamado o método da
declarado na classe a que o objeto pertence e não da classe superclasse. Para
que essa regra seja implementada é necessário que a associação da chamada do
método com o código do método em casos como no exemplo IV-18 seja feita em
tempo de execução. Isso ocorre porque somente em tempo de execução será
possível saber a classe do objeto que está sendo recebido como parâmetro.
O momento em que ocorre a associação de um identificador com a objeto
que identifica é chamado tempo de amarração (ou vinculação). Se este
momento ocorre em tempo de compilação a amarração é dita estática (static
binding). Se associação ocorre em tempo de execução a amarração é dita
dinâmica (dynamic binding). Assim, na linguagem Java, em se tratando da
associação da chamada do método ao código do método, a amarração é
dinâmica. A única exceção é se o método for declarado como final. Neste caso
a amarração é estática porque não é possível sobrescrever o método.
A implementação da amarração dinâmica é claramente mais ineficiente
que a estática, uma vez que o compilador precisa gerar código para determinar a
classe do objeto em tempo de execução. No entanto, existem vantagens
consideráveis na adoção da amarração dinâmica. Ela facilita o reuso e a
extensão de programas. Por exemplo, se criarmos uma nova subclasse da classe
Conta que também sobrescrevesse o método imprimeSaldo(), nenhuma
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
100
modificação precisaria ser feita em métodos como o imprimeSaldo() da classe
TestaConta, uma vez que o método correto sempre é chamado. Portanto,
podemos ampliar a hierarquia de um conjunto de classes sem precisar alterar, na
maioria das vezes, os métodos que fazem uso dessa hierarquia.
A amarração dinâmica de métodos também acarreta um comportamento
polimórfico. Por exemplo, o objeto que é passado para o método
imprimeSaldo() pode exibir comportamento diversos, dependendo da classe a
qual o objeto realmente pertence.
Amarração Dinâmica em C++
Apesar da amarração dinâmica entre os identificadores dos métodos e o corpo dos
métodos ser considerada uma característica importante para linguagens orientadas a objetos, nem
todas a linguagens deste estilo de programação adotam este comportamento como padrão. Por
exemplo, a linguagem C++ adota a amarração estática como padrão. Caso o programador deseje
que os métodos possuam amarração dinâmica então deve instruir o compilador explicitamente
prefixando os métodos com a palavra-chave virtual. A razão para esta decisão dos projetistas
da linguagem está na eficiência. A amarração dinâmica é bem mais ineficiente que a amarração
estática e a linguagem C++ se propõe a ser uma linguagem que é capaz de gerar código que
executa eficientemente.
O exemplo IV-19 procura esclarecer melhor a amarração dinâmica dos
métodos na linguagem Java.
public class Animal
{
public void fala(){}
}
public class Cachorro extends Animal
{
public void fala(){System.out.println(“Au au!”);}
}
public class Gato extends Animal
{
public void fala(){System.out.println(“Miau!”);}
}
public class UsaAnimal
{
public void falaAnimal(Animal a){a.fala();}
}
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
101
Exemplo IV-19. Classes de vozes dos animais.
No exemplo IV-19 o método falaAnimal() da classe UsaAnimal recebe
qualquer subclasse da classe Animal é invoca o método fala() da subclasse.
Para incluir mais um animal na hierarquia basta declarar mais uma subclasse:
public class Pato extends Animal
{
public void fala(){System.out.println(“Quá Quá!”);}
}
Exemplo IV-20. Classe com a voz do pato.
Nada precisa ser alterado nas outras classes. A nova classe pode ser
inclusive subclasse de outra subclasse que não haverá problema:
public class GatoRonronando extends Gato
{
public void fala(){System.out.println(“Purr!”);}
}
Exemplo IV-21. Classe com a voz do pato.
Podemos observar que a classe Animal propriamente dita não faz nada a
não ser servir de “cabide” para as outras classes. É pouco provável que ela seja
instanciada, já que o seu único método não faz nada. Classes como essa
funcionam como um esquema para as subclasses e deveriam ser tratadas
adequadamente pelo compilador. Este tópico será tratado na próxima seção.
Classes e Métodos Abstratos
Classes abstratas são esquemas ou esqueletos de classes. Uma classe
abstrata não pode ser instanciada e possui um ou mais métodos abstratos. Um
método abstrato é um método sem corpo cuja assinatura é precedida da palavrachave abstract. As subclasses de uma classe abstrata precisam sobrescrever os
métodos abstratos, caso contrário também serão abstratas e não poderão ser
instanciadas.
A classe Animal do exemplo IV-19 é uma ótima candidata à classe
abstrata. Para torná-la uma classe abstrata basta preceder a declaração da classe
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
102
com a palavra chave abstract e declarar como abstratos os métodos que se
deseje tornar abstratos, como mostra o exemplo IV-22.
abstract public class Animal
{
abstract public void fala();
}
Exemplo IV-22. Classe abstrata Animal.
Além de não poder ser instanciada a classe abstrata possui outras
limitações. Uma classe abstrata não pode possuir métodos estáticos e
construtores. Outra limitação é que os métodos abstratos não podem ser
privados, uma vez que as subclasses precisam sobrescrever os métodos
abstratos.
Interfaces
As interfaces podem ser encaradas como classes abstratas completamente
não implementadas. Ou seja, todos os métodos são abstratos e os atributos só
podem ser do tipo static final (constantes). A forma geral de uma interface
obedece o seguinte esquema:
interface identificador
{
corpo da interface
}
A interface não é usada como superclasse de outras classes. Uma
interface é implementada por outras classes. A classe que implementa uma
interface precisa fornecer uma implementação para todas os métodos definidos
na Interface. Para que uma determinada classe seja vista como uma
implementação de uma interface é preciso indicar isto colocando a palavra chave
implements na declaração da classe e após a palavra chave a lista com o nome
de todas as interfaces que se deseja implementar, separados por vírgula. O
esquema abaixo mostra a forma geral do uso de interfaces.
class Identificador implements Interface1, Interface2,...
{
corpo da classe
}
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
103
O leitor deve estar se perguntando: se a interface é apenas uma classe
abstrata completamente não implementada então porque não criar simplesmente
uma classe abstrata? A resposta está relacionada com o tipo de herança que
ocorre entre as classes de Java. Como já mencionamos, a linguagem Java não
permite a herança múltipla, ou seja, uma classe só pode ser subclasse de apenas
uma classe. No entanto, existem situações em que é necessário definir uma
classe que possa ser vista de modos distintos. É ai que entram as interfaces. A
interface define uma forma de ver uma classe, restando para a classe a
implementação desta visão e a linguagem Java permite que uma classe
implemente tantas interfaces quantas se desejar. Desta forma Java evita os
principais problemas da herança múltipla (herdando múltiplas declarações e não
múltiplas implementações), sem abrir mão de criar classes que podem ser vistas
de formas diferentes.
Por exemplo, suponha que um método receba um objeto que representa a
interface de saída de um determinado programa. O objetivo deste método é usar
um método do objeto de interface para escrever uma mensagem na tela. O
exemplo IV-23 mostra o código da classe que contém o método.
public class EscreveMem
{
public static void escreve(InterUsuario inter)
{
inter.exibeMensagem(“ola mundo!”);
}
}
Exemplo IV-23. Classe que recebe um objeto de interface.
Suponha também que deseja-se que o programa tenha tanto uma interface
com o usuário em modo gráfico como em modo texto. Usaremos de agora em
diante as iniciais IU para significar interface com o usuário para evitar a
confusão com o conceito de interfaces que estamos introduzindo. Se deseja-se
que o método escreve() possa receber instâncias dos dois tipos de IU é
necessário que ambas sejam do tipo InterUsuario. No entanto, como veremos
mais adiante, as classes que implementam IU gráficas devem ser subclasses de
classes predefinidas como por exemplo da classe java.awt.Frame. Portanto,
temos agora um problema de herança múltipla. A classe que implementa a IU
gráfica deve herdar de uma classe como a Frame e da classe InterUsuario. A
solução é simples. Basta definir InterUsuario como uma interface como
mostra o exemplo IV-24.
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
104
interface InterUsuario
{
abstract public void exibeMensagem(String men);
}
Exemplo IV-24. Definição de como InterUsuario interface.
Agora basta que cada classe que representa um tipo de IU implemente a
interface InterUsuario. O exemplo IV-25 mostra a implementação de uma IU
gráfica simples e o exemplo IV-26 mostra a implementação de uma IU em modo
texto simples.
import java.awt.*;
import java.awt.event.*;
public class Janela extends Frame implements InterUsuario
{
private Label label1;
public Janela() {
addWindowListener (new WindowAdapter() {
public void windowClosing(WindowEvent evt) {
System.exit(0);
}
}
);
setLayout (new BorderLayout());
label1 = new Label();
label1.setText ("Mensagem");
add (label1, "South");
setSize(200,100);
}
public void exibeMensagem(String men)
{
label1.setText(men);
}
}
Exemplo IV-25. IU gráfica.
public class Console implements InterUsuario
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
105
{
public void exibeMensagem(String men)
{
System.out.println(men);
}
}
Exemplo IV-26. IU em modo texto.
O exemplo IV-27 mostra uma forma de uso das classes definidas nos
exemplos de IV-24 a IV-26.
public class UsaIUs
{
public static void main(String args[])
{
Console c = new Console();
Janela j = new Janela();
j.setVisible(true);
EscreveMem. escreve(c);
EscreveMem. escreve(j);
}
}
Exemplo IV-27. Uso das IUs.
As interfaces também são úteis sob o aspecto de projeto. Em C++ os
programadores estão acostumados separar a interface de uma classe de sua
implementação. Geralmente dentro das classes são declaradas apenas as
assinaturas dos métodos sendo que a implementação dos mesmos pode ser feita
posteriormente. Isto permite separar a especificação do uso de uma classe (o que
ela faz) de sua implementação (como ela faz), reduzindo a complexidade da
tarefa de implementação e permitindo uma melhor divisão de tarefas. No
entanto, na linguagem Java os métodos de uma classe são geralmente
implementados dentro da classe. As Interfaces e Classes podem ser utilizadas
para permitir a separação entre a interface e a implementação a exemplo de
como e feito em C++.
Classes Internas
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
106
A partir da versão 1.1 da linguagem Java foi incluída a permissão para
definir uma classe dentro da definição de outra. As classes declaradas dentro de
outras são denominadas de Classes Internas. O exemplo IV-28 mostra um
esquema contendo o posicionamento relativo de uma classe interna.
public class Externa
{
...
class Interna
{
...
}
}
Exemplo IV-28. Posicionamento relativo das classes Interna e Externa.
Os atributos e métodos declarados na classe externa são visíveis pela classe
interna, mesmo os declarados como protected ou private. No entanto, o
contrário não é verdadeiro, ou seja, os atributos e métodos da instância da classe
interna só são visíveis pela classe externa se forem declarados como públicos.
Como já foi dito as classes mais externas (nível topo) só podem ser
declaradas como públicas ou com escopo de pacote (default). No entanto, as
classes internas também podem receber os qualificadores private e protected.
O efeito destes qualificadores sobre a visibilidade da instância da classe é o
mesmo obtido sobre um atributo qualquer. O exemplo IV-29 mostra o caso de
uma classe contendo uma classe interna privada.
public class Calc
{
Incrementa inc1, inc5;
private int num;
public Calc ()
{
inc1=new Incrementa(1);
inc5=new Incrementa(5);
}
private class Incrementa
{
int i;
public Incrementa(int ai){i=ai;}
public int inc(){return i+num;}
}
public int calcula(){return inc1.inc()+inc5.inc();}
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
107
}
Exemplo IV-29. Uso de classes internas.
A inclusão de classes internas foi uma modificação a nível de linguagem
motivada pelo modelo de eventos proposto a partir da versão 1.1. Neste modelo,
como poderá ser constatado no capítulo que trata sobre a AWT, as classes
internas se ajustam perfeitamente. O exemplo IV-30 mostra o uso de uma classe
interna para capturar eventos. Este tipo de uso será tratado com maiores detalhes
no capítulo sobre AWT.
import java.awt.*;
import java.awt.event.*;
public class Janela extends Frame {
public Janela (String Titulo){
super(Titulo);
addWindowListener(new JanelaListener());
setSize(100,50);
}
private class JanelaListener implements WindowListener
{
public void windowOpened(WindowEvent we){}
public void windowClosed(WindowEvent we){}
public void windowIconified(WindowEvent we){}
public void windowDeiconified (WindowEvent we){}
public void windowActivated(WindowEvent we){}
public void windowDeactivated(WindowEvent we){}
public void windowClosing(WindowEvent we){
setVisible(false);
dispose();
System.exit(0);
}
}
}
Exemplo IV-30. Uso de classes internas para receber eventos.
Classes Internas Anônimas
Java permite que se crie um objeto de uma classe sem nome. As classes são
declaradas no momento do retorno de um método ou durante a passagem de
parâmetros e o objeto é criado quando o argumento ou a expressão de retorno é
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
108
avaliada. O exemplo IV-31 mostra o retorno de um objeto de uma classe
anônima.
public class TesteAnonima
{
public Object retAnon()
{
return new Object()
{ // Definição da classe anônima
private int i=10;
public int value(){return i;}
};
}
}
Exemplo IV-31. Retorno de um objeto de uma classe anônima.
Pode parecer pelo exemplo IV-31 que o objeto retornado é uma instância
da classe Object, no entanto, na verdade é uma instância de uma classe anônima
que é subclasse da classe Object. A classe anônima possui atributos e métodos
próprios que a diferenciam da classe Object e estão definidos na declaração da
classe. Usamos a classe Object apenas para exemplificar com uma classe
conhecida. Qualquer classe pode fazer o papel de superclasse. O exemplo IV-32
mostra o uso de uma classe anônima na criação de um objeto para lidar com
eventos sobre um botão.
import java.awt.*;
import java.awt.event.*;
public class BT extends Frame
{
Button bt;
public BT()
{
bt = new Button(“OK”);
bt.addActionListener( new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
dispose();
}
});
}
}
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
109
Exemplo IV-32. Uso de uma classe anônima para tratamento de eventos.
As classes anônimas são adequadas para situações onde necessitamos em um
local específico de um objeto de uma classe ligeiramente diferente de outra
classe já declarada. Com a classe anônima evitamos ter que declarar uma nova
classe para atender apenas uma necessidade local.
Inicialização e Finalização de Objetos
Inicialização automática
A inicialização automática de variáveis em Java se dá de duas formas:
1. Variáveis locais a métodos – nenhuma inicialização automática é
realizada. O compilador checa se as variáveis foram inicializadas
antes do seu uso.
2. Variáveis de instância ou de classe – tipos numéricos e caractere
são inicializados com zeros (caso o caractere seja impresso a saída
será um espaço), tipo booleano é inicializado com false e
referências são inicializadas com null.
Inicialização de atributos de Instância
Existem dois lugares onde os atributos de instância podem ser
inicializados: dentro do construtor ou na declaração do atributo. O exemplo
IV-33 mostra estes dois tipos de inicialização. O atributo a é inicializado na
declaração e o atributo b é inicializado no construtor.
public class ExInicia
{
int a = 10; // inicializa o atributo a
int b;
public ExInicia()
{
b = 20; // inicializa o atributo b
}
}
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
110
Exemplo IV-33. Tipos de inicialização de atributos de instância.
Qual é a diferença entre os dois tipos de inicialização? A primeira
diferença é que os construtores são parametrizados e, portanto, a inicialização
com construtor pode variar conforme os parâmetros passados. A segunda
diferença é que a inicialização no construtor é feita após a inicialização na
declaração.
A inicialização pode ser realizada com o uso de invocação de métodos e
com o uso de outros atributos, desde que já tenham sido inicializados.
Inicialização de atributos de classe
A inicialização das variáveis de classe não pode ocorrer dentro dos
construtores, uma vez que o construtor pertence ao objeto e não à classe. Outra
diferença é que a inicialização só ocorre uma vez, já que existe apenas uma
versão atributo. A inicialização ocorre quando o primeiro objeto é criado.
Blocos de inicialização
Java permite que as inicializações de atributos, tanto de classe como de
instâncias de classe sejam agrupadas em blocos. O exemplo IV-34 mostra o uso
de blocos de inicialização.
public class ExBlocoInicia
{
static int a;
static int b;
int c;
int d;
static {
a = 10;
b = 20;
}
{
c = 30;
d = 40;
}
}
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
111
Exemplo IV-34. Uso de blocos de inicialização.
Note que o bloco que inicia os atributos de instância é precedido pela
palavra reservada static.
Ordem de inicialização
Como já foi dito a inicialização na declaração é feita antes da
inicialização no construtor. E no caso da herança? O que é inicializado primeiro?
A classe ou a superclasse? Nesse caso a superclasse é inicializada primeiro.
Mesmo que não ocorra uma invocação explícita ao construtor da superclasse é
chamado o construtor default. O exemplo IV-35 mostra a seqüência de
inicialização em uma herança.
class X
{
public X()
{
System.out.println("X foi inicializada!");
}
}
public class Y extends X
{
public Y()
{
System.out.println("Y foi inicializada!");
}
public static void main(String a[])
{
new Y();
}
}
Saída:
X foi inicializada!
Y foi inicializada!
Exemplo IV-35. Sequência de inicialização em herança.
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
112
Finalização de Objetos
Com a linguagem Java não existe a preocupação de liberar a memória,
como é o caso de C/C++, uma vez que os objetos que não estão sendo
referenciados são coletados automaticamente pelo coletor de lixo. No entanto,
como Java pode invocar uma função de C/C++ por meio de uma chamada
nativa, e como esta função pode alocar memória que não será devolvida pelo
coletor de lixo, então pode ser necessária alguma forma de devolver
explicitamente esta memória quando o objeto for coletado. Para esses casos a
programador deve prover o método finalize(), que será invocado quando o
objeto for coletado. O método finalize() não é para ser chamado
explicitamente. Ele é invocado pela máquina virtual antes de devolver a
memória usada pelo objeto. Nele o programador deve devolver a memória
alocada por métodos nativos e associada ao objeto.
Conversão de Objetos
Uma variável declarada como referencia para objetos de uma
determinada classe pode receber referencias de objetos de subclasses sem a
necessidade de se usar qualquer operador de conversão. A conversão é feita
automaticamente, uma vez que toda instância de uma subclasse é também uma
instância da superclasse. Por exemplo:
Object o;
String s = “teste”;
o = s;
A atribuição acima é possível porque a classe Object é uma superclasse
de String (Na verdade é superclasse de todas as classes). Já a conversão inversa
só é possível se o objeto for realmente do tipo da subclasse e, mesmo assim, é
preciso utilizar um operador de conversão forçada (casting):
Object o;
String s = “teste”;
String t;
o = s;
t = (String) o;
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
113
Exceções
Quanto maior o número de situações de exceção que um determinado
programa consegue lidar, mais robusto é este programa. Para construir um
programa robusto o programador deve prever, além das situações onde os dados
de entrada e os recursos se apresentam de forma adequada, as situações onde
algo pode impedir a computação normal. Existem três abordagens típicas para
esses casos:
1) Ignorar – neste caso a ação será a padrão do sistema, provavelmente
a interrupção do programa, ou no mínimo do comando onde ocorreu
o erro, e a exibição de uma mensagem ininteligível na saída padrão,
gerada pelo sistema. Esta é abordagem mais simples, mas certamente
não é a mais interessante, principalmente do ponto de vista do usuário
final.
2) Retornar o código de erro. No caso da linguagem Java este retorno
só pode ser feito via valor de retorno da função. No entanto algumas
vezes não existem valores disponíveis para indicar o erro. Por
exemplo, quando um método retorna um valor inteiro costuma-se
usar os valores 0 ou –1 para sinalizar uma condição de erro, mas
como agir no caso desses e todos os outros valores de inteiros serem
valores válidos em uma execução normal? Existe ainda o problema
de que esta abordagem não resolve o problema de erros que ocorrem
nos construtores. Como os construtores não retornam valores não é
possível sinalizar erros dessa forma. Esta é uma abordagem muito
comum entre os programadores de C e Pascal, e é ainda usada pelos
programadores de C++. No entanto, as linguagens mais modernas
como C++ e Java disponibilizam recursos que permitem um melhor
tratamento de exceções, como veremos na terceira abordagem.
3) Declarar um ou mais tratadores de exceções. Um tratador de
exceção é uma parte do código que tem por objetivo recuperar a
execução do programa após a ocorrência da exceção, permitindo que
o sistema se comporte “suavemente”, mesmo sob condições adversas.
A sintaxe e semântica dos tratadores de exceção variam muito de
linguagem para linguagem, mas tem tendido a uma certa
uniformização nas linguagens mais modernas. A linguagem Java, por
ter como objetivo ser uma linguagem segura, obriga que os
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
114
programadores capturem todas as exceções, menos as derivadas da
classe RuntimeException.
Na linguagem Java a amarração do tratador à exceção é definida em
tempo de compilação. Nela, o programador envolve os comandos passíveis de
gerar exceções em blocos try/catch com o formato geral mostrado abaixo:
try
{
código que pode gerar exceções
} catch(classe de exceção1 objeto) {
tratamento da exceção1
} catch(classe de exceção2 objeto) {
...
} catch(classe de exceçãoN objeto) {
tratamento da exceçãoN
}
As exceções são representadas como objetos A porção catch do bloco
possui um bloco com comandos para tratamento da exceção. O bloco que trata
as exceções não tem acesso às variáveis declaradas dentro do bloco try{}, uma
vez que o escopo destas variáveis está delimitado pelo bloco. O exemplo IV-36
mostra um bloco para capturar a exceção NumberFormatException que pode ser
lançada pelo método estático parseInt() da classe Integer se o método
receber como argumento uma cadeia de caracteres que não pode traduzido para
uma representação inteira. Note que a variável i não pode ser acessada dentro do
bloco catch, uma vez que foi declarada dentro do bloco try.
public class Excecao1
{
public static void main(String a[])
{
try
{
int i = Integer.parseInt(a[0]);
System.out.println("A cadeia passada pode ser ”+
”tranformada para número :“+i);
} catch(NumberFormatException e)
{ System.out.println("A cadeia passada não pode ser ”+
”tranformada para número :“+a[0]);
}
}
}
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
115
Exemplo IV-36. Capturando a exceção NumberFormatException.
Continuação após o Tratamento da Exceção
Se o bloco catch não possuir nenhum comando return, throw ou
alguma chamada à função exit(), então o programa é reassumido após o último
bloco catch. O caso de relançamento de exceções (throw) será tratado mais
adiante.
A hierarquia de Exceções
As exceções são objetos que pertencem a classes que estão organizadas
em uma hierarquia. A classe que se encontra no topo da hierarquia é a classe
Throwable. A classe Throwable possui duas subclasses: Exception e Error.
Exception e suas subclasses são usadas para indicar condições que podem ser
recuperadas. Error e suas subclasses indicam condições que em geral não
podem ser recuperadas, causando a terminação do programa.
Todos os objetos da classes Exception devem ser capturados ou
explicitamente relançados para um nível acima na pilha de chamadas. Isto só não
é válido para os objetos das classes RuntimeException. A RuntimeException é
suas subclasses definem as exceções que podem ser lançadas durante a execução
normal da máquina virtual, como divisão por zero ou indexação fora dos limites
do array. A exceção NumberFormatException mostrada no exemplo é uma
subclasse da RuntimeException. Como essas exceções podem ser lançadas por
um número muito grande de métodos, seria trabalhoso para o programador se ele
fosse obrigado a incluir código para capturar todas essas exceções. Portanto,
instâncias da classe RuntimeException e suas subclasses não precisam ser
capturadas. A Figura IV-4 mostra uma parte da hierarquia Throwable até o nível
3. O programador pode também adicionar classes a esta hierarquia como
veremos mais adiante.
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
116
Figura IV-4. Hierarquia parcial das exceções.
Capturando mais de uma exceção
Um método pode lançar mais de uma exceção, assim como pode existir
mais de um método dentro de um bloco try/catch com potencial de lançar
exceções. Portanto, muitas vezes é preciso definir um bloco try/catch com
capacidade de tratar mais de uma exceção. Isto é feito por meio da definição de
mais de uma cláusula catch. Cada cláusula catch trata uma exceção. Como a
busca pela cláusula catch que deve tratar uma determinada exceção é feita de
cima para baixo é preciso tomar o cuidado de posicionar as o tratamento das
exceções mais genéricas mais abaixo do que o tratamento das exceções mais
específicas. Caso contrário o tratamento das exceções mais especificas nunca
será executado. O exemplo IV.xx mostra um trecho de código cujo bloco
IOException,
try/catch
é
capaz
de
capturar
as
exceções
NumberFormatException e Exception. Note que por ser uma exceção mais
genérica que as outras duas o tratamento da exceção Exception é posicionado
após os outros. Como as exceções NumberFormatException e IOException
não se encontram na mesma linha de herança, a posição de uma relativa a outra
não tem significado.
import java.io.*;
class Excecao2
{
public static void main(String a[])
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
117
{
try
{
BufferedReader in = new BufferedReader(
new InputStreamReader(System.in));
System.out.print("Entre um número:");
int i = Integer.parseInt(in.readLine());
}catch(IOException e)
{System.out.println(“Erro de leitura!”);}
catch(NumberFormatException e)
{System.out.println(“Não é um número!”);}
catch(Exception e)
{System.out.println(“ocorreu algum erro”);}
}
}
Exemplo IV.XX- Capturando a várias exceções.
Lançando exceções
Até agora temos visto apenas como capturar as exceções lançadas. Está
na hora de olharmos o outro lado da moeda, ou seja, como lançar as exceções.
As exceções são lançadas utilizando-se o operador throw. A forma geral do
comando é a seguinte:
throw <objeto>
Pode-se também criar o objeto no instante do lançamento, invocando o
construtor. Neste caso a forma geral do comando seria a seguinte:
throw new <classe>(<parâmetros>)
Note que pode-se passar parâmetros para o construtor que podem ajudar
na recuperação do problema pelo tratador de exceções. O método onde a
exceção é lançada deve tratar a exceção ou passá-la para método que o invocou.
Para passar a exceção para o método que o invocou a assinatura do método
corrente deve conter uma cláusula throws após a lista de argumentos, como
mostrado abaixo:
<modificador><tipo retorno> <identificador>(<argumentos>)
throws <exceção1>,<exceção2>,...,<exceçãon>
{
}
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
118
Note que é possível indicar na assinatura do método o repasse de várias
exceções. O exemplo IV.xx mostra tanto o lançamento de um objeto da classe
Exception caso os parâmetros para o construtor da classe não sejam corretos.
import java.io.*;
class NumPos{
private int num;
public NumPos(int aNum) throws Exception {
if (aNum < 1)
throw new Exception(“Número não positivo”);
num = aNum;
}
}
public class Excecao3 {
public static void main(String a[]) {
try
{
NumPos np = new NumPos(Integer.parseInt(a[0]));
}catch(Exception e)
{System.out.println(e.getMessage());}
}
}
Exemplo IV.XX- Lançando exceções.
Na verdade o pode-se usar a cláusula throws na assinatura do método
para repassar qualquer exceção que se desejar. O exemplo IV.xx , mostra o
método m1 que repassa qualquer objeto da classe Exception que for lançado em
seu corpo, mesmo os que não forem explicitamente lançado por meio do
comando throw:
public class Excecao4
{
public void m1(int i) throws Exception
{
if (i == 0 ) throw new NumberFormatException();
System.out.println(2/i);
}
}
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
119
Exemplo IV.XX- Repassando todas as exceções.
Comportamento do Sistema diante das Exceções
Agora que já vimos como tratar e como lançar exceções podemos discutir
como é o comportamento global do sistema diante das exceções. Ou seja, como
é a sequência de busca por um tratador após a ocorrência de uma exceção?
Quando ocorre uma exceção em um método o sistema verifica se o comando
onde ocorreu a exceção está incluso em bloco try/catch com uma cláusula
catch associada à exceção. Neste caso o controle é transferido para o bloco de
tratamento da cláusula catch. Caso contrário, o sistema desempilha um nível na
pilha de chamada de métodos e verifica se a chamada ao método anterior está
inclusa em bloco try/catch com uma cláusula catch associada à exceção. O
sistema prossegue desta forma até que um bloco tratador seja encontrado. Caso
encontre o bloco tratador e este não possua nenhum comando de saída (return
ou System.exit()), então o controle será retomado no nível de chamada onde
foi tratado após o último bloco catch. e não onde ocorreu a exceção. Caso não
encontre nenhum bloco tratador, ao atingir o nível mais alto da pilha de
chamadas o programa é abortado.
A figura IV.xx procura ilustrar o comportamento do sistema diante de
uma exceção, tratada em um nível da pilha de chamada diferente de onde
ocorreu.
Pilha de chamadas
m1
Código fonte
tratamento
da exceção
m2
public class Excecao5
{
public void m1(int i)
{
try {
m2(i)
}catch (Exception e){...};
...
}
public void m2(int i) throws Exception
{
m3(i);
}
m3
ocorrência
da exceção
public void m3(int i) throws Exception
{
if (i == 0 ) throw new Exception();
System.out.println(2/i);
}
}
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
120
Figura IV.XX- Propagação das exceções.
Criando suas próprias exceções
Se o programador concluir que nenhuma exceção pré-existente se
encaixa no tipo de erro que pretende sinalizar, então criar uma classe nova para
representar essa exceção. A única condição é que a nova classe seja uma
extensão da subclasse Throwable ou de uma de suas subclasses. Procure
estender a classe Exception ou uma de suas subclasses, exceto o ramo da
RuntimeException, uma vez que, como já foi dito, esta ramificação da
hierarquia de exceções é utilizada para exceções do sistema. Por exemplo,
podemos rescrever o exemplo IV.xx de modo que o método lance uma exceção
definida pelo programador.
import java.io.*;
class NumPosException extends Exception
{
public NumPosException(String m){super(m);}
}
class NumPos
{
private int num;
public NumPos(int aNum) throws NumPosException {
if (aNum < 1)
throw new NumPosException (“Número não positivo”);
num = aNum;
}
}
public class Excecao6 {
public static void main(String a[]) {
try
{
NumPos np = new NumPos(Integer.parseInt(a[0]));
}catch(NumPosException e)
{System.out.println(e.getMessage());}
}
}
Exemplo IV.XX- Lançando exceções.
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
121
A cláusula finally
Existe mais uma cláusula opcional do bloco try/catch. Esta é a cláusula
finally. A cláusula finally abriga uma trecho de código que deve ser
executado independente se ocorreu ou não uma exceção no bloco try/catch.
Pode ser usada para realizar operações de finalizações, como por exemplo
fechamento de arquivos e canais de comunicação. O exemplo IV.xx ilustra o uso
da cláusula finally.
import java.io.*;
class Excecao7
public static void main(String a[])
{
try
{
BufferedReader in = new BufferedReader(
new InputStreamReader(System.in));
System.out.print("Entre um número:");
int i = Integer.parseInt(in.readLine());
}catch(IOException e)
{System.out.println(“Erro de leitura!”);}
catch(NumberFormatException e)
{System.out.println(“Não é um número!”);}
finally()
{System.out.println(“Terminou.”);}
}
}
Exemplo IV.XX- Usando a cláusula finally.
No exemplo IV.xx se não ocorrer nenhuma exceção a cláusula finally é
executada e a execução é reassumida após a cláusula finally. Caso ocorra
alguma exceção no bloco try/catch e exista alguma cláusula catch associada à
exceção então a cláusula finally é executada após o tratamento da exceção e a
execução é reassumida após a cláusula finally. Caso ocorra alguma exceção no
bloco try/catch e não exista alguma cláusula catch associada à exceção então
a cláusula finally é executa antes da transferência do controle para o método
chamador.
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
122
Documentando o código
Uma das maiores dificuldades no desenvolvimento de software é
conseguir que os programadores produzam uma documentação relacionada com
os programas que estão desenvolvendo. Eles não gostam de interromper o
desenvolvimento para escrever paralelamente a documentação e após o término
da implementação não se animam a passar um grande período de tempo
documentando todo o código escrito. Pensando nisso os projetistas de Java
desenvolveram um meio do programador embutir a documentação no próprio
código, de modo que, não precisasse interromper a implementação.
A documentação é inserida como um comentário e usa uma sintaxe
especial que pode ser interpretada pelo programa javadoc para gerar um
documento HTML. Todo comentário que deve ser interpretado pelo javadoc
deve ser iniciado por “/**” e terminado por “*/”. O programador pode inserir
a documentação de duas formas:
1. rótulos (tags) iniciados pelo caractere ‘@’ e que denotam comandos
de documentação; ou
2. por meio de código HTML embutido.
Rótulos
O javadoc relaciona os comentários com a estrutura codificada após o
comentário. As estruturas relacionadas com os comentários são as classes,
variáveis e métodos. A exceção do rótulo @see todos os rótulos estão
relacionados com a documentação de classes ou métodos.
Rótulo @see
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
123
O rótulo @see é usado para gerar links para documentação de outras
classes. Os links não serão verificados pelo javadoc. Os formatos possíveis
são os seguintes:
@see <nome da classe>
@see <nome da classe>#<nome do método>
Rótulos para documentação de classes
Os seguintes rótulos são usados na documentação de classes:
Rótulo
Descrição
@version <versão> Usado para incluir informação sobre a versão.
@author <autor>
Usado para incluir informação sobre o autor.
Tabela IV.xx- Rótulos para documentação de classes.
Rótulos para documentação de métodos
Os seguintes rótulos são usados na documentação de métodos:
Rótulo
@param <nome> <descrição>
@return <descrição>
@exception <nome da classe>
<descrição>
@deprecated
Descrição
Usado para incluir informação sobre o
parâmetro do método.
Usado para incluir informação sobre o
valor de retorno do método.
Usado para incluir informação sobre a
exceção que pode ser lançada pelo
método.
Incluído a partir da versão 1.1. Indica
que o método pode não ser mantido
nas próximas versões da linguagem.
Tabela IV.xx- Rótulos para documentação de métodos.
Exemplo de Documentação
O exemplo IV.xx ilustra o uso dos rótulos para documentação do código.
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
/**
Pessoa
Mantem os dados pessoais de uma pessoa.
@author Alcione de Paiva Oliveira
@author [email protected]
@version 1.0
*/
public class Pessoa
{
private String Nome;
private String Tel;
private String End;
// Construtor
/**
@param n String contendo o nome
@param t String contendo o telefone
@param e String contendo o endereço
@return não retorna valor
*/
public Pessoa(String n, String t, String e)
{
Nome = n; Tel = t; End = e;
}
/**
Retorna o nome da pessoa
@return uma String contendo o nome
*/
public String getNome(){return Nome;}
/**
Retorna o telefone da pessoa
@return uma String contendo o telefone
*/
public String getTel(){return Tel;}
/**
Retorna o endereço da pessoa
@return uma String contendo o endereço
*/
public String getEnd(){return End;}
}
Exemplo IV.XX- Exemplo de documentação.
HTML embutida
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
124
Java na Prática
125
O programador pode incluir código HTML dentro dos comentários para
documentação da mesma forma que faria em um documento da Web, como
mostra o exemplo IV.xx.
/**
*
<b>Pessoa<\b>
* Possui os seguintes objetivos
* <ol>
* <li> Objetivo1
* <li> Objetivo2
* </ol>
*/
public class Pessoa
{
...
}
Exemplo IV.XX- Uso de HTML embutida para documentar código.
O javadoc irá ignorar os caracteres ‘*’ no início de cada linha ao gerar a
documentação. Não use rótulos de títulos como <h1> ou <hr>, uma vez que o
javadoc irá inserir rótulos de títulos automaticamente.
Agenda Eletrônica versão Console 1.0
Iniciaremos agora a primeira versão de um programa que implementa
uma agenda eletrônica de endereços e telefones. Este programa evoluirá ao
longo do livro, de acordo com os conhecimentos que serão apresentados. Em sua
primeira versão a agenda eletrônica terá uma interface baseada em linha de
comando e não armazenará o seu conteúdo em algum dispositivo de memória
permanente, o que não a torna muito útil em termos práticos. Nas versões
posteriores a agenda eletrônica armazenará o seu conteúdo em arquivos e Banco
de Dados, ganhará interface gráfica, poderá ser utilizada em navegadores de
Web, terá uma arquitetura Cliente/Servidor, e outras facilidades. Esperamos com
isso mostrar os vários aspectos da linguagem durante a evolução do programa.
Outros exemplos serão apresentados de acordo com a necessidade, porém a
agenda eletrônica permanecerá como exemplo principal.
O diagrama de classes da figura III.xx mostra os objetos que compõe a
versão console da agenda.
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
126
Pessoa
-String Nome
-String Tel
-String End
Agenda
+inserir(pessoa p)
+pessoas getPessoas()
+Pessoa getPessoa(String Nome)
0..*
+getNome()
+getTel()
+getEnd()
AgendaInt
+obterPessoa()
+exibirLista()
+exibirPessoa()
Figura III. – Diagrama de classes da versão console da agenda eletrônica.
Os objetos da classe Pessoa armazenam os dados de uma pessoa. Ela
fornece os métodos para o acesso a esses dados.
O objeto da classe Agenda é composto por vários dados de pessoa. Este
fato está representado no diagrama pela associação de agregação. A classe
Agenda possui os métodos necessários para inserção e recuperação dos dados. O
método inserir(), trata de inserir uma pessoa na lista de pessoas, o método
getPessoas(), retorna uma lista contendo todos os objetos da classe Pessoa e
o método getPessoa() retorna um objeto da classe Pessoa com o nome
especificado.
A Classe AgendaInt é responsável por fornecer os métodos que
implementam a interface com o usuário. Ela foi criada por questões de projeto,
uma vez que é importante manter separado o código das interfaces do sistema.
Primeiramente apresentaremos o código da classe Pessoa.
/**
Pessoa
*/
public class Pessoa
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
127
{
private String Nome;
private String Tel;
private String End;
// Construtor
public Pessoa(String n, String t, String e)
{
Nome = n; Tel = t; End = e;
}
// Metodos
public String getNome(){return Nome;}
public String getTel(){return Tel;}
public String getEnd(){return End;}
}
Por questões de simplicidade a classe pessoa possui apenas um
construtor. As variáveis de instância são declaradas private para prevenir
acessos que não sejam por meio dos métodos da classe. Segue abaixo o código
da classe Agenda:
/**
AGENDA Versão Console 1.0.
*/
public class Agenda
{
Pessoa pessoas[];
// Construtor
public Agenda()
{pessoas = null;}
// Metodos
/**
inserir
*/
public void inserir(Pessoa p)
{
if (pessoas == null) pessoas = new Pessoa[1];
else AumentaCapacidade();
pessoas[pessoas.length-1] = p;
}
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
128
/**
Consultar
*/
public Pessoa getPessoa(String nome)
{
Pessoa aux = null;
for (int i=0; i< pessoas.length; i++)
if (pessoas[i].getNome().equals(nome))
aux = pessoas[i];
return aux;
}
/**
listar
*/
public Pessoa[] getPessoas(){return
pessoas;}
/**
AumentaCapacidade
*/
private void AumentaCapacidade ()
{
Pessoa aux[] = new Pessoa[pessoas.length+1];
for (int i=0;i< pessoas.length;i++)
aux[i] = pessoas[i];
pessoas = aux;
}
}
De modo a manter o programa com poucas linhas não inserimos código
para tratar erros para testar os valores que são passados para os métodos ou
retornados por eles. O leitor deve ter isto em mente se por acaso pretende utilizar
trechos deste programa para aplicações profissionais.
Existem várias formas de representar a associação entre duas classes.
Escolhemos representar a agregação por meio de um array de objetos da classe
Pessoa. Um outra forma, mais fácil de se trabalhar, será apresentada na seção xx.
A variável que referencia o array é inicializada com null. Toda vez que é
inserido um novo objeto o array precisa ser redimensionado. Isto é feito pelo
método privado AumentaCapacidade(). Na verdade o método cria um novo
array maior em uma unidade que o anterior. Esta não é uma solução muito
eficiente. O melhor seria incrementar o array em várias unidades, de modo que o
redimensionamento seria necessário em intervalos menores. Contudo, o código
necessário para gerenciar a inserção seria bem mais complexo.
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
129
O método getPessoa() retorna um objeto com o dado nome igual ao
passado ao método. Se não existir tal objeto é retornado null. Já o método
getPessoas() retorna uma referencia para array de objetos do tipo Pessoa.
/**
AgendaInt
Interface console da agenda.
*/
import java.io.*;
public class AgendaInt
{
Agenda ag;
BufferedReader in;
// Construtor
public AgendaInt()
{
ag = new Agenda();
in = new BufferedReader(new
InputStreamReader(System.in));
}
// Metodos
/**
Exibirlista
*/
public void Exibirlista()
{
Pessoa p[]=ag.getPessoas();
for (int i= 0; i<p.length; i++)
System.out.println("\nNome:"+p[i].getNome()+"\nTelef
one:"
+p[i].getTel()+"\nEndereço:"+p[i].getEnd()+"\n");
}
/**
exibirPessoa
*/
public void exibirPessoa()
{
String nome=null;
try {
System.out.println("Entre com o nome:");
nome = in.readLine();
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
130
if (nome.length()<1) System.exit(-1);
} catch(IOException e)
{System.out.println(e.getMessage());System.exit(-1);}
Pessoa p = ag.getPessoa(nome);
if (p!=null)
{
System.out.println("\nNome:"+p.getNome()+"\nTelefone:"
+p.getTel()+"\nEndereço:"+p.getEnd());
}
}
/**
obterPessoa
*/
public void obterPessoa()
{
String nome;
String tel;
String end;
try {
System.out.println("Entre com o nome:");
nome = in.readLine();
if (nome.length()<1) System.exit(-1);
System.out.println("\nEntre com o Telefone:");
tel = in.readLine();
System.out.println("\nEntre com o Endereço:");
end = in.readLine();
ag.inserir(new Pessoa(nome ,tel,end));
} catch(IOException e)
{System.out.println(e.getMessage());System.exit(-1);}
}
// main
public static void main(String args[])
{
AgendaInt agInt = new AgendaInt();
String opcao="";
for(;;)
{
System.out.println(
"\nAgenda Tabajara\n***********************\n");
System.out.print(
"Opcoes:\n(i)nserir\n(c)onsultar\n(l)istar\n(f)im=>"
);
try {
opcao = agInt.in.readLine();
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
131
if (opcao.length()==0) continue;
} catch(IOException e)
{System.out.println(e.getMessage());System.exit(1);}
switch(opcao.charAt(0))
{
case 'f': System.exit(0); break;
case 'i': agInt.obterPessoa(); break;
case 'c': agInt.exibirPessoa(); break;
case 'l': agInt.Exibirlista(); break;
}
}
}
}
O método main() da classe AgendaInt cria um objeto da própria classe,
que por sua vez possui um objeto da classe Agenda. A partir de então o
programa entra em um laço, que aguarda e atende às solicitações do usuário.
Todos os métodos da classe recebem e exibem dados por meio dos dispositivos
de E/S padrão. Para ler os dados do dispositivo de entrada padrão, uma linha por
vez, foi necessário encapsular o objeto System.in em objetos das classes
BufferedReader e InputStreamReader . Não detalharemos aqui o uso destas
classes, que será abordado no próximo capítulo. No momento basta observarmos
que a entrada de dados será realizada por meio do método readLine(), que
retorna a linha digitada pelo usuário.
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
132
Capítulo V – Entrada e S aída
(java.io)
Acesso Sequencial
As funções de entrada e saída são suportadas por classes agrupadas no
pacote java.io. Todas se baseiam no conceito de stream de bytes (corrente ou
sequência), onde a entrada/saída é um duto onde se retira/coloca cada byte
como em uma fila do tipo First in-first out. A figura V.1 ilustra este tipo de
enfoque.
Figura V.1 Stream de bytes.
O produtor e o consumidor podem se selecionados de uma ampla
variedade que vai desde arquivos e portas de protocolos TCP/IP até arranjos
(arrays) de bytes e Strings. De modo a pode lidar com essa ampla variedade de
fontes/consumidores de dados e prover todo o tipo de facilidade de leitura o
pacote java.io possui um conjunto razoavelmente grande classes. Essas
classes são combinadas, formando camadas, onde as classes das camadas
inferiores fornecem serviços básicos como leitura/escrita de um byte, enquanto
que as classes superiores fornecem serviços de leitura/escrita mais sofisticados,
como leitura de linha, leitura de um tipo float, etc. A figura V.2 mostra este tipo
de combinação.
Camada n
Camada 1
Classe I/O
Básica
Nível
dos
serviços
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
133
Figura V.2 Organização das classes de I/O.
Para complicar um pouco mais a situação, a linguagem Java Possui dois
conjuntos distintos de classes para I/O. Um originado da versão 1.0 e outro
introduzido na versão 1.1. A razão para isso é que as classes projetadas na
versão 1.0 são orientadas para bytes e portanto não são adequadas para lidar
códigos Unicodes que utilizam dois bytes. A versão 1.2 não introduziu grandes
modificações, realizando apenas um aperfeiçoamento na java.io.File e
algumas mudanças na assinatura de alguns métodos . O leitor deve então estar
pensando que basta então utilizar o novo conjunto de classes e esquecer do
antigo, porém a coisa não é tão simples assim. Primeiramente, é importante
conhecer o conjunto antigo de classes uma vez que existe um grande número de
programas escrito na versão 1.0 e o leitor pode se ver obrigado a ler ou dar
manutenção no código destes programas. Em segundo lugar, algumas vezes será
preciso combinar as funções antigas com as novas para obter algum tipo de
funcionalidade. E em terceiro lugar a Sun adicionou novas facilidades ao
conjunto antigo na versão 1.1, dando sinais que não pretende simplesmente
abandoná-lo. Tudo isto faz com que o tratamento de entrada e saída em Java não
seja algo muito simples de se dominar. As figuras V.3 e V.4 apresentam
diagramas que mostram algumas das camadas dos dois conjuntos de classes, de
modo que o leitor possa ter uma idéia da sua equivalência, embora nem sempre
uma classe de um conjunto possua uma classe correspondente em outro
conjunto.
floats etc.
floats etc.
String
String
ints
ints
FilterInputStream
DataInputStream
BufferedInputStream
LineNumberInputStream
FilterReader
BufferedReader
LineNumberReader
caractere
bytes
StringBufferInputStream
ByteArrayInputStream
PipedInputStream
FileInputStream
InputStream
PipedReader
CharArrayReader
StringReader
FileReader
Reader
Figura V.3 Comparação entre as classes de entrada.
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
ints
floats etc.
Strings
ints
floats etc.
Strings
FilterOutputStream
PrintStream
DataInputStream
BufferedOutputStream
134
FilterWriter
BufferedWriter
PrintWriter
caractere
bytes
ByteArrayOutputStream
PipedOutputStream
FileOutputStream
OutputStream
PipedWriter
CharArrayWriter
StringWriter
FileWriter
Writer
Figura V.4 Comparação entre as classes de saída.
Note que não existe uma equivalência perfeita entre os dois conjuntos,
nem entre o par entrada/saída em um mesmo conjunto. Por exemplo, a classe
StringBufferedInputStream
não possui uma classe equivalente
StringBufferedOutputStream nem a DataInputStream possui uma
equivalente DataWriter. Esta é uma das razões para o uso combinado dos dois
conjuntos. Para realizar a conversão entre os dois conjuntos são fornecidas as
classes InputStreamReader e OutputStreamWriter.
As funções básicas são tratadas pelas classes abstratas básicas
InputStream e OutputStream. Para as funções de entrada e saída com Buffers
são utilizadas as classes BufferedInputStream, BufferedOutputStream,
BufferedReader e BufferedWriter. O tamanho padrão do buffer é de 2048
bytes. A tradução entre InputStream, OutputStream e BufferedReader,
BufferedWriter é feita por InputStreamReader e OutputStreamWriter. Para
IO em arquivos podem ser utilizadas classes de baixo nível: FileInputStream,
FileOutputStream, FileReader e FileWriter.
De modo a possibilitar a comparação e o entendimento do uso dos dois
conjuntos os exemplos abaixo mostram a mesma função implementada com
cada grupo de classes.
a) Streams
b) I/O 1.1
import java.io.*;
import java.io.*;
class TesteIO101
{
public static void main(String a[])
class TesteIO111
{
public static void main(String a[])
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
135
{
{
try
{
BufferedReader in =
new BufferedReader(
new FileReader(a[0]));
String str;
while((str = in.readLine())!= null)
System.out.println(str);
} catch(IOException e)
{System.out.println(e.getMessage());}
try
{
DataInputStream in =
new DataInputStream(
new BufferedInputStream(
new FileInputStream(a[0])));
String str;
while((str = in.readLine())!= null)
System.out.println(str);
} catch(IOException e)
{ System.out.println(e.getMessage());}
}
}
}
}
Exemplo V.1 – Leitura não formatada de arquivos.
O exemplo V.1 mostra a leitura de um arquivo passado pela linha de
comando feita (a) por meio de stream e (b) por meio de reader. Em ambos os
casos a leitura é utiliza buffers, de modo a otimizar os acessos a disco. No caso
(b) basta utilizar um objeto da classe BufferedReader, uma vez que a leitura
é não formatada, com o uso do método readLine(). Já no caso (a), é preciso
“envolver” o objeto da classe BufferedInputStream em um objeto da
classe DataInputStream, apesar de não ser uma leitura formatada, uma vez
que o método readLine() pertence à classe DataInputStream. Mesmo
assim o programa receberá a mensagem
The method java.lang.String readLine() in class
java.io.DataInputStream has been deprecated.
significando que o método readLine() está sendo descontinuado para a classe
DataInputStream, tendendo a desaparecer nas próximas versões.
a) Streams
b) I/O 1.1
import java.io.*;
import java.io.*;
class TesteIO102
{
public static void main(String a[])
{
try
{
PrintStream out = new PrintStream(
new BufferedOutputStream(
new FileOutputStream("saida.out")));
out.println("Linha de teste 1");
out.println("Linha de teste 2");
out.close();
class TesteIO112
{
public static void main(String a[])
{
try
{
PrintWriter out = new PrintWriter(
new BufferedWriter(
new FileWriter("saida.out")));
out.println("Linha de teste 1");
out.println("Linha de teste 2");
out.close();
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
136
}catch(IOException e) {
System.out.println(e.getMessage());}
}
}catch(IOException e) {
System.out.println(e.getMessage());}
}
}
}
Exemplo V.2 – Escrita não formatada em arquivos.
a) Streams
import java.io.*;
class TesteIO103
{
public static void main(String a[])
{
try
{
DataOutputStream out = new DataOutputStream(
new BufferedOutputStream(
new FileOutputStream("a.out")));
out.writeBytes("O valor de pi: \n");
out.writeDouble(3.14159);
out.close();
DataInputStream in = new DataInputStream(
new FileInputStream("a.out"));
System.out.println(in.readLine());
System.out.println(in.readDouble());
}catch(IOException e) {System.out.println(e.getMessage());}
}
}
Exemplo V.3 – Escrita e leitura formatada em arquivos..
A leitura/escrita formatada utiliza as classes DataInputStream e
DataOutputStream. Como não existem as correspondentes DataReader e
DataWriter, somos obrigados a usar o conjunto antigo de classes para realizar a
leitura e escrita formatada.
Os dispositivos de entrada e saída padrões (equivalentes na linguagem
C++ a cin, cout e cerr), são mantidos pela por variáveis estáticas da classe
System, referenciadas por System.in, System.out e System.error. Como
essas variáveis referenciam a objetos das classes InputStream e
OutputStream, se desejarmos manipular os dispositivos de E/S padrões com o
conjunto de classes novos devemos utilizar as classes tradutoras
InputStreamReader e OutputStreamWriter, como mostrado no exemplo V.4.
import java.io.*;
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
137
public class TesteIO114
{
public static void main(String a[])
{
try
{
BufferedReader in = new BufferedReader(
new InputStreamReader(System.in));
System.out.print("Entre uma linha:");
System.out.println(in.readLine());
}catch(IOException e)
{System.out.println(e.getMessage());}
}
}
Exemplo V.4 – Leitura e Escrita nos dispositivos de saída e entrada padrões.
Acesso Direto
Até agora lidamos com dispositivos de entrada e saída que tratam
sequência de bytes ou caracteres (streams). No entanto, alguns dispositivos
como disco rígido e CDs permitem o acesso direto à um byte sem a necessidade
de ler todos os outros bytes posicionados antes. Este tipo de acesso é chamado
de acesso direto e Java permite este tipo acesso a arquivos armazenados em
dispositivos que suportam o acesso direto por meio da classe
RandomAccessFile. Esta classes não pertence a nenhuma das hierarquias
apresentadas anteriormente uma vez que não acessa os dados na forma de
sequência. A única semelhança com as outras hierarquias é o fato de
implementar as interfaces InputStream e OutputStream. De fato, a classe
RandomAccessFile torna muito conveniente o acesso a arquivos por
implementar simultaneamente as duas interfaces, permitindo desta forma tanto a
leitura quanto a escrita no arquivo. Os construtores da classe são os seguintes:
public RandomAccessFile(File file, String mode)
throws IOException
public RandomAccessFile(String name, String mode)
throws IOException
O primeiro construtor recebe um objeto da classe File e um objeto da
String
classe
especificando o modo de acesso do arquivo. O acesso direto será
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
138
feito sobre o objeto da classe Files. Os modos acesso possíveis são: modo de
leitura, especificado pela String “r” e modo de leitura e escrita, especificado
pela String “rw”. O segundo construtor recebe um objeto da classe String
contendo o nome do arquivo e outro objeto da classe String especificando o
modo de acesso do arquivo. Ambos os construtores lançam a exceção
IOException.
A maioria dos métodos implementados por RandomAccessFile são
FileInputStream
implementados
pela
classe
ou
pela
classe
FileOutputStream, uma vez que ela lida tanto com leitura como saída, porém,
a classe RandomAccessFile possui alguns métodos adicionais que permitem
definir a posição corrente no arquivo e estabelecer onde será lido/escrito o
próximo dado. A tabela V.1 mostra os métodos da classe RandomAccessFile.
Método
Descrição
Fecha o arquivo.
Retorna o descritor arquivo objeto associado com
este stream.
getFilePointer()
Retorna a posição corrente neste arquivo.
length()
Retorna o tamanho deste arquivo.
read(byte[] b, int Lê até len bytes em um array de bytes.
close()
getFD()
off, int len)
read()
read(byte[] b)
readBoolean()
readByte()
readChar()
readDouble()
readFloat()
readFully(byte[]
b,
int off, int len)
readFully(byte[] b)
readInt()
readLine()
readLong()
readShort()
readUnsignedByte()
readUnsignedShort()
readUTF()
seek(long pos)
Lê um byte.
Lê até to b.length bytes em um array de bytes.
Lê um boolean.
Lê um byte.
Lê um caracter Unicode.
Lê um double.
Lê um float.
Lê exatamente len bytes no array byte.
Lê b.length bytes no array byte.
Lê um inteiro de 32-bit com sinal.
Lê a próxima linha de texto.
Lê um inteiro com sinal de 64-bits.
Lê um inteiro com sinal de 16-bits.
Lê um inteiro sem sinal de 8-bits.
Lê um inteiro sem sinal de 16-bits.
Lê um String usando a codificação UTF-8.
Define pos como a posição em bytes no arquivo
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
skipBytes(int n)
write(int b)
write(byte[] b,
off, int len)
write(byte[] b)
139
onde será feita a próxima leitura/escrita.
Salta exatamente n bytes.
Escreve um byte.
int Escreve len bytes do array iniciando em off.
writeBoolean(boolean
v)
writeByte(int v)
writeBytes(String s)
Escreve b.length bytes do array.
Escreve um boolean como um valor 1-byte.
Escreve um byte como um valor 1-byte.
Escreve uma String como uma sequência de
bytes.
writeChar(int v)
Escreve um caractere como um valor 2-byte, byte
alto primeiro.
writeChars(String s) Escreve uma String como uma sequência de
caracteres.
writeDouble(double
Escreve um double como um valor de 8-bytes.
v)
writeFloat(float v)
Escreve um float como um valor de 4-bytes.
Escreve um inteiro como quatro bytes.
Escreve um long como oito bytes.
writeShort(int v)
Escreve um short como dois bytes.
writeUTF(String str) Escreve uma String usando a codificação UTF-8.
writeInt(int v)
writeLong(long v)
Tabela V.1 – Métodos públicos da classe RandomAccessFile.
O Exemplo V.5 mostra o uso da classe RandomAccessFile para
acessar o byte no meio do arquivo.
import java.io.*;
class RandomTeste
{
public static void main (String args[])
{
if (args.length ==0)
{
System.err.println("Forneça o nome do arquivo!");
System.exit(0);
}
try
{
RandomAccessFile f = new RandomAccessFile(args[0], "r");
long tam = f.length();
if (tam==0)
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
{
System.out.println("Arquivo vazio!");
}
else
{
f.seek(tam>>1);
System.out.println("O byte no meio é: "+f.read());
}
} catch (Exception e) {
System.out.println("Erro: " + e.toString());
}
}
Exemplo V.1 – Uso da classe RandomAccessFile.
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
140
Java na Prática
141
Capítulo VI – java.util
O pacote java.util reúne um conjunto interessante de classes e
interfaces úteis ao desenvolvimento de aplicações. Contém recursos para lidar
com coleções, data e hora, internacionalização, arrays de bits, tokenização de
cadeias de caracteres e etc. Nesta seção abordaremos as classes e interfaces mais
utilizadas.
Lidando com Coleções
Antigamente, era muito comum que, durante o desenvolvimento de um
sistema, os programadores tivessem a necessidade de implementar uma estrutura
de dados como uma lista, pilha ou tabela hash para agrupar e gerenciar um
conjunto de elementos. Como as primeiras linguagens de programação não
possuíam recursos para que se implementasse estas estruturas de forma genérica,
de modo que pudessem ser usadas independentemente do tipo dos elementos, o
programador era obrigado a implementar deste o início toda a estrutura para
cada novo tipo de elemento.
Com as linguagens de programação mais recentes este quadro mudou. As
novas linguagens permitem que o programador implemente estruturas de dados
genéricas, independentes de tipos. Além disso, a maioria delas já implementam,
embutidas na linguagem ou na forma de bibliotecas, as estruturas de dados mais
comuns. Este é o caso da linguagem Java que possui, dentro do pacote
java.util classes e interfaces que implementam estruturas de dados como
listas (List), arrays crescentes (Vector), pilhas (Stack), tabela hash
(Hashtable) e etc. O pacote possui também interfaces para percorrer os
elementos das estruturas (Iterator e Enumeration). Apresentaremos aqui
apenas as classes e interfaces mais usadas.
As Interfaces Iterator e Enumeration
As interfaces Iterator e Enumeration são usadas na criação de
objetos que tem por objetivo percorrer sequencialmente os elementos de uma
coleção. Como ambas as interfaces possuem o mesmo objetivo o leitor deve
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
142
estar se perguntando sobre necessidade de existirem duas interfaces. O fato é que
até a versão 1.1 da linguagem Java existia apenas a interface Enumeration.
No entanto, como o nome deste tipo de objeto na comunidade de Engenharia de
Software é iterator a Sun aproveitou para incluir uma interface com este nome
na versão 1.2 e fazer algumas pequenas modificações.
O métodos que devem ser implementados em cada interface são
semelhantes e estão descritos nas tabelas VI.1 e VI.2.
Método
Descrição
boolean hasMoreElements() Testa se Enumeration possui mais elementos.
Object nextElement()
Retorna o próximo elemento da Enumeration.
Tabela VI.1 – Métodos da interface Enumeration.
Método
Descrição
boolean hasNext()
Object next()
void remove()
Testa se Iterator possui um próximo elemento.
Retorna o próximo elemento do Iterator.
Remove da coleção associada o último elemento
retornado pelo Iterator.
Tabela VI.2 – Métodos da interface Iterator.
Um objeto que implementa a interface Enumeration é retornado por
alguma classe que implementa uma estrutura de dados sempre que invocamos o
método elements() da classe. Por exemplo, suponha que desejamos imprimir
os elementos reunidos por um objeto v da classe Vector. O trecho de código
para cumprir essa tarefa poderia ter a seguinte forma:
for (Enumeration e = v.elements() ; e.hasMoreElements() ;)
{
System.out.println(e.nextElement());
}
Já no caso da interface é preciso usar o método iterator() herdado
por toda subclasse da interface Collection. Neste caso o trecho de código
para cumprir essa tarefa poderia ter a seguinte forma:
for (Iterator it = v.iterator() ; it.hasNext() ;)
{
System.out.println(it.next());
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
143
}
Vector
public class Vector
extends AbstractList
implements List, Cloneable, Serializable
A classe Vector implementa um array dinâmico. Ou seja, um array que
cresce ou diminui a medida da necessidade. O uso de objetos classe Vector é
indicado para situações onde o programador precisa manter uma lista de
elementos mas não sabe, a priori, o número de elementos que pertencerão à lista
ou o número de elementos da lista irá variar durante o processamento.
Hierarquia
java.lang.Object
|
+--java.util.AbstractCollection
|
+--java.util.AbstractList
|
+--java.util.Vector
Construtores
Construtor
Descrição
public Vector()
public Vector(Collection c)
public Vector(int iniCapac)
public Vector(int iniCapac,
int inc)
Constrói um Vector vazio com espaço inicial
para 10 elementos e incremento 0.
Constrói um Vector contendo os elementos do
objeto do tipo Collection.
Constrói um Vector vazio com espaço inicial
para iniCapac elementos e incremento 0.
Constrói um Vector vazio com espaço inicial
para iniCapac elementos e incremento inc.
Tabela VI.3 – Construtores da classe Vector.
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
144
Atributos
Atributo
Descrição
protected int capacityIncrement
protected int elementCount
protected Object[] elementData
Quantidade que é incrementada à
capacidade do Vector toda vez que o
tamanho se torna maior que a capacidade.
Número de elementos referenciados
Array onde são armazenadas as referencias
aos elementos.
Tabela VI.4 – Atributos públicos da classe Vector.
Métodos
Devido ao grande número de métodos da classe Vector mostraremos
apenas os mais usados.
Método
Descrição
void add(int i, Object o)
public boolean add(Object
o)
public boolean
addAll (Collection c)
public void
addElement(Object o)
public int capacity()
public void clear()
public Object clone()
public boolean
contains(Object o)
public boolean
containsAll(Collection c)
public void
copyInto (Object[] a)
public Object
elementAt(int i)
public Enumeration
elements()
public void
ensureCapacity(int min)
public boolean
equals(Object o)
Adiciona um elemento na posição indicada por i.
Adiciona um elemento no final do Vector.
Adiciona todos os elementos da coleção especificada
no final do Vector.
Adiciona um elemento no final do Vector.
Retorna a capacidade atual.
Remove todos os elementos do Vector.
Retorna um clone do Vector.
Testa se o objeto é um elemento do Vector.
Retorna true se o Vector contém todos os
elementos referenciados por c.
Copia os elementos do Vector no array.
Retorna o elemento na posição i.
Retorna um objeto do tipo Enumeration que
permite percorrer os elementos do Vector.
Aumenta a capacidade do Vector para o valor min.
Compara se o é igual ao Vector. Retorna true
somente se o objeto é também do tipo List, possui o
mesmo tamanho e referencia os mesmos elementos na
mesma ordem.
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
public Object
firstElement()
public Object get(int i)
public int
indexOf(Object o)
145
Retorna o primeiro elemento.
Retorna o elemento da posição i.
Retorna a posição da primeira ocorrência de o. O
método equals() do objeto é usado para o teste de
igualdade.
public int indexOf(Object Retorna a posição da primeira ocorrência de o a partir
o,int i)
de i. O método equals() do objeto é usado para o
teste de igualdade.
public void
Insere o objeto o na posição i.
insertElementAt(Object o,
int i)
public boolean isEmpty()
Testa se o Vector está vazio.
public Object
Retorna o último elemento.
lastElement()
public int
Retorna a posição da última ocorrência de o. O
lastIndexOf(Object o)
método equals() do objeto é usado para o teste de
igualdade.
public Object remove(int
Remove o elemento da posição i.
i)
public boolean
Remove todos os elementos que também pertencem à
removeAll(Collection c)
Collection.
public void
Remove todos os elementos.
removeAllElements()
public void
Remove o elemento da posição i.
removeElementAt(int i)
public boolean
Mantém apenas os elementos que também pertencem
retainAll(Collection c)
à Collection.
public Object set(int i,
Substitui o objeto na posição i pelo objeto o.
Object o)
public void setSize(int
Define o tamanho do Vector.
s)
public int size()
Retorna o tamanho do Vector.
public List subList(int
Retorna uma parte dos elementos do Vector entre
i, int f)
fromIndex, inclusive, e toIndex, exclusive.
public Object[] toArray() Retorna um array contendo todos os elementos no
Vector na ordem correta.
public String toString()
Retorna uma representação na forma de String do
Vector.
Tabela VI.5 – Métodos da classe Vector.
Exemplo
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
146
O exemplo a seguir mostra a inserção das strings passadas pela linha de
comando em um objeto da classe Vector. Os elementos do objeto Vector
podem ser exibidos na tela invocando o método print().
import java.util.*;
public class TesteVector
{
Vector v;
public TesteVector(String a[])
{
v = new Vector(a.length);
v.copyInto(a);
}
public void print()
{
for (Iterator it = v.iterator(); it.hasNext() ;)
{
System.out.println((String)it.next());
}
}
public static void main(String args[])
{
TesteVector teste = new TesteVector(args);
teste.print();
}
}
Exemplo VI.xx – Uso do Vector.
Stack
public class Stack
extends Vector
A classe Stack implementa a estrutura de dados pilha. Ou seja, uma
estrutura de dados que segue a regra LIFO (last-in-first-out – último a entrar,
primeiro a sair). Esta regra impõe define uma lista que é acessada apenas por
uma extremidade, de modo que o último elemento a ser inserido na pilha tenha
que ser o primeiro elemento a sair.
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
147
Hierarquia
java.lang.Object
|
+--java.util.AbstractCollection
|
+--java.util.AbstractList
|
+--java.util.Vector
|
+--java.util.Stack
Construtor
Construtor
Descrição
public Stack()
Constrói um Stack vazio.
Tabela VI.6 – Construtor da classe Stack.
Métodos
Método
Descrição
public boolean empty()
public Object peek()
public Object pop()
public Object
push(Object o)
public int
search(Object o)
Testa se a Stack está vazia.
Retorna uma referência para o elemento no topo da
Stack sem retirá-lo.
Retorna uma referência para o elemento no topo da
Stack e o retira.
Insere o objeto o no topo da Stack.
Retorna a distância do topo da pilha da primeira
ocorrência de o. O elemento no topo da pilha possui a
distância 1. O método equals() do objeto é usado
para o teste de igualdade.
Tabela VI.5 – Métodos da classe Stack.
Exemplo
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
148
O exemplo a seguir mostra a inserção das strings passadas pela linha de
comando em um objeto da classe Stack. Os elementos do objeto Stack
podem ser exibidos na tela invocando o método print().
import java.util.*;
public class TesteStack
{
Stack s;
public TesteStack(String a[])
{
s = new Stack();
for (int i=0; i<a.length; i++)
s.push(a);
}
public void desempilha ()
{
String aux = (String)s.pop();
while (aux !=null)
{
System.out.println(aux);
}
}
public static void main(String args[])
{
TesteStack teste = new TesteStack(args);
teste.desempilha();
}
}
Exemplo VI.xx – Uso do Stack.
Hashtable
public class Hashtable
extends Dictionary
implements Map, Cloneable, Serializable
A classe Hashtable implementa uma estrutura de dados conhecida
como tabela hash. Neste tipo de estrutura todo elemento armazenado possui uma
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
149
chave que permite recuperar diretamente o elemento, sem a necessidade de ir
examinando cada elemento armazenado até encontrar o elemento desejado,
como seria o caso em uma lista. Cada chave identifica apenas um elemento.
Existem vários bons livros sobre estruturas de dados nas livrarias que o leitor
pode consultar para se aprofundar no tema.
Hierarquia
java.lang.Object
|
+--java.util.Dictionary
|
+--java.util.Hashtable
Construtores
Construtor
Descrição
public Hashtable()
Constrói um Hashtable vazio com fator de
carga 0,75.
Constrói um Hashtable com capacidade
inicial capac e com fator de carga 0,75.
Constrói um Hashtable com capacidade
inicial capac e com fator de carga fatCarga.
Constrói um Hashtable com os elementos de
m.
public Hashtable(int capac)
public Hashtable(int capac,
float fatCarga)
public Hashtable(Map m)
Tabela VI.xx – Construtores da classe Hashtable.
Métodos
Devido ao grande número de métodos da classe Hashtable mostraremos
apenas os mais usados.
Método
Descrição
public void clear()
public Object clone()
public boolean
contains(Object o)
public boolean
containsKey(Object o)
Remove todos os elementos do Hashtable.
Retorna um clone raso do Hashtable.
Testa se o objeto é acessado por alguma chave do
Hashtable.
Testa se o objeto é chave no Hashtable.
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
150
public boolean
containsValue(Object o)
public Enumeration
elements()
public Set entrySet()
Testa se o objeto é acessado por alguma chave do
Hashtable.
Retorna um objeto do tipo Enumeration que
permite percorrer os elementos do Hashtable.
Retorna um Set contendo os elementos do
Hashtable.
public boolean
Compara se o é igual ao Hashtable. Retorna true
equals(Object o)
somente se o objeto é também do tipo Map e faz o
mesmo mapeamento.
public Object get(Object
Retorna o elemento da posição mapeado pela chave
key)
key.
public boolean isEmpty()
Testa se o Hashtable está vazio.
public Enumeration keys() Retorna um objeto do tipo Enumeration que
permite percorrer as chaves do Hashtable.
public Set keySet()
Retorna um Set contendo as chaves do Hashtable.
public Object put(Object
Insere o objeto o no Hashtable tendo como chave
key,Object o)
key.
protected void rehash()
Reorganiza internamente o Hashtable para acessar
e acomodar os elementos mais eficientemente.
public Object
Remove o elemento que tem como chave key.
remove(Object key)
public int size()
Retorna o número de chaves do Hashtable.
public String toString()
Retorna uma representação na forma de String do
Hashtable.
public Collection
Retorna um Collection contendo os elementos do
values()
Hashtable.
Tabela VI.xx – Métodos da classe Hashtable.
Exemplos
O exemplo a seguir mostra a inserção das objetos da classe Integer em
um objeto Hashtable. A chave usada para cada objeto é o número por
extenso.
Hashtable numbers = new Hashtable();
numbers.put("one", new Integer(1));
numbers.put("two", new Integer(2));
numbers.put("three", new Integer(3));
...
Integer n = (Integer)numbers.get("two");
if (n != null) {
System.out.println("two = " + n);
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
151
}
...
for (Enumeration e = numbers.elements();e.hasMoreElements();){
Integer i = (Integer)e.nextElement()
System.out.println( i.toString());
}
Exemplo VI.xx – Uso do Hashtable.
O exemplo a seguir mostra a inserção das objetos da classe Pessoa em
um objeto Hashtable. A chave usada para cada objeto é o nome da pessoa.
class pessoa {
String nome;
String tel;
public pessoa(String nome, String tel)
{this.nome = nome; this.tel = tel;}
public String getNome(){return nome;}
public String getTel(){return tel;}
}
...
Hashtable pessoas = new Hashtable();
...
pessoa P1 = new pessoa("Pedro", "899-1313");
pessoas.put(P1.getNome(), P1);
...
pessoa P2 =(pessoa) pessoas.get("Pedro");
if (P2!=null) System.out.println
("Nome:"+P2.getNome(), "\nTel:"+P2.getTel());
Exemplo VI.xx – Uso do Hashtable.
Miscelânea de Classes do pacote java.util
Arrays
public class Arrays
extends Object
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
152
Esta classe foi inserida no pacote java.util a partir da versão 1.2 do
SDK. É uma classe utilitária, fornecendo métodos de ordenação e busca em
arrays.
Hierarquia
java.lang.Object
|
+--java.util.Arrays
Métodos
Método
Descrição
public static List asList(Object[] a)
public static int
binarySearch(byte[] a, byte key)
public static int
binarySearch(char[] a, char key)
public static int
binarySearch(double[] a, double key)
public static int
binarySearch(float[] a, float key)
public static int
binarySearch(int[] a, int key)
public static int
binarySearch(long[] a, long key)
public static int
binarySearch(Object[] a, Object key)
public static int
binarySearch(short[] a, short key)
public static int
binarySearch(Object[] a, Object key,
Comparator c)
public static boolean
equals(boolean[] a, boolean[] a2)
public static boolean
equals(byte[] a, byte[] a2)
public static boolean
equals(char[] a, char[] a2)
public static boolean
equals(double[] a, double[] a2)
public static boolean
equals(float[] a, float[] a2)
public static boolean
equals(int[] a, int[] a2)
public static boolean
equals(long[] a, long[] a2)
public static boolean
equals(Object[] a, Object[] a2)
Retorna uma List criada a partir do array.
Realiza uma pesquisa binária retornando a
posição de key no array.
Realiza uma pesquisa binária retornando a
posição de key no array. O array tem que
estar ordenado ascendentemente de acordo
com o Comparator.
Retorna true se os dois arrays são iguais.
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
public static boolean
equals(short[] a, short[] a2)
public static void
fill(boolean[] a, boolean val)
public static void
fill(byte[] a, byte val)
public static void
fill(double[] a, double val)
public static void
fill(float[] a, float val)
public static void
fill(int[] a, int val)
public static void
fill(long[] a, long val)
public static void
fill(Object[] a, Object val)
public static void
fill(short[] a, short val)
public static void fill(boolean[] a,
int ini, int fim, boolean val)
public static void fill(byte[] a,
int ini, int fim, byte val)
public static void fill(double[] a,
int ini, int fim, double val)
public static void fill(float[] a,
int ini, int fim, float val)
public static void fill(int[] a,
int ini, int fim, int val)
public static void fill(long[] a,
int ini, int fim, long val)
public static void fill(Object[] a,
int ini, int fim, Object val)
public static void fill(short[] a,
int ini, int fim, short val)
public static void sort(byte[] a)
public static void sort(double[] a)
public static void sort(float[] a)
public static void sort(int[] a)
public static void sort(long[] a)
public static void sort(Object[] a)
public static void sort(short[] a)
public static void
sort(byte[] a, int ini, int fim)
public static void
sort(double[] a, int ini, int fim)
public static void
sort(float[] a, int ini, int fim)
public static void
sort(int[] a, int ini, int fim)
public static void
sort(long[] a, int ini, int fim)
public static void
sort(Object[] a, int ini, int fim)
public static void
sort(short[] a, int ini, int fim)
public static void sort(Object[] a,
Comparator c)
public static void sort(Object[] a,
int ini, int fim, Comparator c)
153
Preenche o array com o valor especificado.
Preenche o array com o valor especificado a
partir do elemento de índice ini inclusive
até o elemento de índice fim exclusive.
Ordena o array em ordem ascendente.
Ordena o array em ordem ascendente a
partir do elemento de índice ini inclusive
até o elemento de índice fim exclusive.
Ordena o array em ordem ascendente
definida pelo objeto Comparator.
Ordena o array em ordem ascendente
Comparator a
definida pelo objeto
partir do elemento de índice ini inclusive
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
154
até o elemento de índice fim exclusive.
Tabela VI.5 – Métodos da classe Arrays.
O algoritmo de ordenação
O algoritmo de ordenação é uma variação quicksort apresentado por Jon L. Bentley e
M. Douglas McIlroy's "Engineering a Sort Function", Software-Practice and Experience, Vol.
23(11) P. 1249-1265 (November 1993). Este algoritmo possui complexidade n*log(n) em
conjunto de dados que causariam a degradação de outros algoritmos de quicksort para um
desempenho de ordem quadrática.
Exemplo
O exemplo a seguir mostra a impressão ordenada das strings passadas
pela linha de comando.
import java.util.arrays;
class TestArrays
{
public static void main(String a[])
{
if(a.length>0)
{
Arrays.sort(a);
for(int i=0;i<a.length; i++) System.out.println(a[i]);
}
}
}
Exemplo VI.xx – Uso do Arrays.
Date
public class Date
extends Object
implements Serializable, Cloneable, Comparable
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
155
Esta classe é responsável pela representação da data com precisão de
milisegundos. Antes da versão 1.1 do SDK a classe Date também tinha as
funções de formatação e análise de datas no tipo String assim como de
interpretar as datas em anos, meses, dias, horas, minutos e segundos. Devido à
complexidade da internacionalização estas funções foram transferidas para as
classes DateFormat e Calendar. O métodos da classe Date que realizavam
essas funções tornaram-se deprecated. A data base padrão, a partir da qual são
contados os milisegundos é 01/01/1970, 00:00:00 GMT.
Hierarquia
java.lang.Object
|
+--java.util.Date
Construtores
Os construtores deprecated não estão listados.
Construtor
Descrição
public Date()
public Date(long data)
Constrói um Date representando a data do momento de
sua alocação.
Constrói um Date contendo a data calculada a partir
número de millisegundos representados por data tendo
como base a data 01/01/1970, 00:00:00 GMT.
Tabela VI.xx – Construtores da classe Date.
Métodos
Os métodos deprecated não estão listados.
Método
Descrição
public boolean after(Date d)
public boolean before(Date d)
public Object clone()
public int compareTo(Date d)
Testa se a data é anterior à data passada como
argumento.
Testa se a data é posterior à data passada
como argumento.
Cria um clone do objeto.
Compara duas datas. Retorna 0 se as duas
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
156
datas são iguais. Retorna um valor maior que
0 se a data passada como argumento é menor
e um valor menor que 0 se data passada como
argumento e maior.
public boolean equals(Object o) Retorna true se duas datas são iguais.
public long getTime()
Retorna o número de milisegundos desde
01/01/1970, 00:00:00 GMT até a data
representada pelo objeto corrente.
public void setTime(long time) Define a data desde objeto usando o número
de milisegundos desde 01/01/1970, 00:00:00
GMT passado como argumento.
Tabela VI.5 – Métodos da classe Date.
Exemplo
O exemplo a seguir exibe a representação da data corrente em
milisegundos.
import java.util.Date;
class TestDate
{
public static void main(String a[])
{
System.out.println(new Date().getTime());
}
}
Exemplo VI.xx – Uso da classe Date.
Observable
public class Observable
extends Object
A classe Observable permite criar objetos “observáveis”. Objeto
observável é um objeto que, quando sofre alguma alteração, notifica outros
objetos, chamados de “observadores”. Os objetos observáveis e observadores
fazem parte do padrão de projeto conhecido como MVC (Modelo–Visão–
Controle) introduzido na linguagem Smalltalk. A utilização deste padrão de
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
157
projeto facilita a implementação programas onde um modelo possui várias
visões que devem ser atualizadas toda vez que o modelo muda. O controle atua
sobre o modelo, ocasionando alterações. Por exemplo, suponha que modelo que
armazene os valores de ações da bolsa. Podemos exibir várias visões desses
dados, como gráficos de barras e em forma de pizza. Os controladores
modificariam o modelo que em seguida notificaria as visões para refletirem o
estado atual do modelo. As figuras VI.XX e VI.XX ilustram esquematicamente
o padrão MVC. Na linguagem Java, para um objeto ser um observador é preciso
implementar a interface Observer.
Controle
Visão
Modelo
Visão
Controle
Figura VI.XX – Esquema do padrão MVC.
X
window
x
a
30
b
45
window
a=30;
b=45;
a
b
Observável
Figura VI.XX – Duas formas de visualização de um modelo.
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
X
Java na Prática
158
Hierarquia
java.lang.Object
|
+--java.util.Observable
Construtor
Construtor
public Observable()
Descrição
Constrói um Observable possuindo zero
observadores.
Tabela VI.XX – Construtor da classe Observable.
Métodos
Método
Descrição
public void
addObserver(Observer o)
protected void clearChanged()
public int countObservers()
public void
deleteObserver(Observer o)
public void deleteObservers()
public boolean hasChanged()
public void notifyObservers()
public void
notifyObservers(Object arg)
protected void setChanged()
Adiciona um observador ao conjunto de
observadores do objeto.
Limpa a indicação de que o objeto mudou.
Retorna o número de objetos observadores
deste objeto.
Remove o objeto passado como argumento
do conjunto de objetos observadores.
Remove todos os objetos observadores do
conjunto de objetos observadores.
Retorna true se o objeto sofreu uma mudança.
Se o método hasChanged() retornar
true notifica todos os objetos observadores
e chama o método clearChanged().
Se o método hasChanged() retornar
true notifica todos os objetos observadores
e chama o método clearChanged(). O
objeto arg é passado como argumento para o
método update() do observador.
Marca este objeto como tendo sofrido uma
mudança.
Tabela VI.XX – Métodos da classe Observable.
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
159
Exemplo
O exemplo VI.xx mostra uma classe Observavel que muda de valor
aleatoriamente. A classe implementa a interface Runnable, uma vez que será
executada concorrentemente. A concorrência será abordada detalhadamente no
Erro! A origem da referência não foi encontrada..
import java.util.*;
public class Observavel extends Observable implements Runnable
{
int valor =0;
public void notifyObservers()
{
setChanged();
super.notifyObservers();
}
public int getValue(){return valor;}
public void run()
{
for(;;)
{
if (Math.random()>0.5)
{
valor =(new Double(Math.random()*200)).intValue();
this.notifyObservers();
}
try {Thread.sleep(300);} catch(Exception e){};
}
}
}
Exemplo VI.xx – Uma classe Observable.
O exemplo a seguir mostra uma classe que define um observador. Para
isso é necessário implementar a interface Observer, cujo único método a ser
codificado é o método update(). Este método é chamado quando o objeto
observado notifica alguma alteração. No exemplo o objeto observador simula
uma barra de progresso que movimenta de acordo com a mudança do valor do
objeto observado.
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
160
import java.awt.*;
import java.util.*;
public class FrameOb
{
Observavel ob
=
Thread t
=
Canvas progress =
extends Frame implements Observer
new Observavel();
new Thread(ob);
new Canvas();
public FrameOb()
{
setLayout(null);
setSize(300,60);
add(progress);
progress.setBackground(Color.blue);
progress.setBounds(0,0,0,30);
setTitle("Exemplo de Observável");
SymWindow aSymWindow = new SymWindow();
this.addWindowListener(aSymWindow);
ob.addObserver(this);
t.start();
}
static public void main(String args[])
{
(new FrameOb()).setVisible(true);
}
class SymWindow extends java.awt.event.WindowAdapter
{
public void windowClosing(java.awt.event.WindowEvent event)
{
System.exit(0);
}
}
public void update(Observable o, Object arg)
{
progress.setSize(ob.getValue(), 100);
}
}
Exemplo VI.xx – Uso da classe Observable.
StringTokenizer
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
161
public class StringTokenizer
extends Object
implements Enumeration
A StringTokenizer é uma classe que permite quebrar uma cadeia de
caracteres em subcadeias denominadas de tokens. um token é uma subcadeia
cercada por caracteres denominados delimitadores. Portanto, o que é um token
dependerá da especificação dos delimitadores. Por exemplo, dada cadeia de
caracteres “ola mundo,ola vida” teremos os tokens “ola mundo” e
“ola vida” caso o delimitador seja o caractere ‘,’ e os tokens “ola”,
“mundo,ola” e “vida” caso o delimitador seja o caractere ‘ ’
Hierarquia
java.lang.Object
|
+--java.util.StringTokenizer
Construtores
Construtor
public
StringTokenizer(String s)
public
StringTokenizer(String s,
String del)
public StringTokenizer
(String s, String del,
boolean voltaTokens)
Descrição
Constrói um StringTokenizer para s.
Constrói um StringTokenizer para s,
usando como delimitadores os caracteres
contidos em del.
Constrói um StringTokenizer para s,
usando como delimitadores os caracteres
contidos em del. Se voltaTokens possui o
valor true, então os caracteres contidos em
del também são retornados.
Tabela VI.XX – Construtores da classe StringTokenizer.
Métodos
Método
Descrição
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
public int countTokens()
public boolean
hasMoreElements()
public boolean hasMoreTokens()
public Object nextElement()
public String nextToken()
public String nextToken(String
del)
162
Retorna o número de tokens.
Mesmo efeito que hasMoreTokens().
Retorna true se existir mais tokens.
Retorna o próximo token como uma instância
de Object.
Retorna o próximo token como uma instância
de String.
Retorna o próximo token como uma instância
de String usando como delimitadores os
caracteres contidos em del.
Tabela VI.XX – Métodos da classe StringTokenizer.
Exemplo
import java.util.*;
public class TestToken
{
static public void main(String args[])
{
StringTokenizer st =
new StringTokenizer("ola mundo louco");
while (st.hasMoreTokens())
println(st.nextToken());
}
}
Saída:
ola
mundo
louco
Exemplo VI.XX – Uso da classe StringTokenizer.
Agenda Eletrônica versão Console 2.0
Neste ponto já estamos preparados para fazer alguns aperfeiçoamentos na
nossa agenda eletrônica de endereços. As alterações serão de dois tipos:
passaremos a utilizar um objeto da classe Hashtable para agrupar as pessoas
da agenda, e acrescentaremos métodos para gravar e recuperar os dados da
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
163
agenda em um arquivo. Estas modificações acarretarão mudanças apenas nos
códigos das classes Agenda e AgendaInt. O diagrama de classes da figura
IV.XX mostra as adições dos métodos gravarAgenda() e
recuperarAgenda(), responsáveis pela gravação e recuperação dos dados
em arquivo.
AgendaInt
Pessoa
+obterPessoa()
+exibirLista()
+exibirPessoa()
+gravarAgenda()
+recuperarAgenda()
Agenda
0..*
+inserir(pessoa p)
+getPessoas()
+getPessoa(String Nome)
-String Nome
-String Tel
-String End
+getNome()
+getTel()
+getEnd()
Figura VI.xx – Diagrama de classes da versão console 2.0 da agenda
eletrônica.
Como a classe Pessoa não sofrerá nenhuma modificação. Ela é
apresentada abaixo apenas por questões de comodidade.
/**
Pessoa
*/
public class Pessoa
{
private String Nome;
private String Tel;
private String End;
// Construtor
public Pessoa(String n, String t, String e)
{
Nome = n; Tel = t; End = e;
}
/**
getNome
*/
public String getNome(){return Nome;}
/**
getNome
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
164
*/
public String getTel(){return Tel;}
/**
getEnd
*/
public String getEnd(){return End;}
}
A classe Agenda será alterada de modo que o a associação de
agrupamento com a classe Pessoa seja implementada por meio de um objeto da
classe Hashtable e não por um array.
/**
AGENDA Versão Console 2.0.
*/
import java.util.*;
public class Agenda
{
Hashtable pessoas;
// Construtor
public Agenda() {pessoas = new Hashtable();}
/**
inserir
*/
public void inserir(Pessoa p) {pessoas.put(p.getNome(),p);}
/**
Consultar
*/
public Pessoa getPessoa(String nome)
{return (Pessoa) pessoas.get(nome);}
/**
listar
*/
public Enumeration getPessoas(){return pessoas.elements();}
}
Podemos notar que o código fica bem mais simples com a adoção da
instância da classe Hashtable. Foi alterada a assinatura do método
getPessoas(), de modo que não mais retorna um array e sim um objeto da
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
165
classe Enumeration. A classe AgendaInt será alterada para tratar o retorno
do método getPessoas() e receberá mais dois novos métodos. O método
main() também será alterado para receber solicitações para a execução das
novas funções.
/**
AgendaInt
Interface console da agenda.
*/
import java.io.*;
import java.util.*;
public class AgendaInt
{
Agenda ag;
BufferedReader in;
// Construtor
public AgendaInt()
{
ag = new Agenda();
in = new BufferedReader(new InputStreamReader(System.in));
}
/**
Exibirlista
*/
public void Exibirlista()
{
Pessoa p;
for(Enumeration e = ag.getPessoas(); e.hasMoreElements();)
{
p = (Pessoa) e.nextElement();
System.out.println("\nNome:"+p.getNome()+"\nTelefone:"
+p.getTel()+"\nEndereço:"+p.getEnd()+"\n");
}
}
/**
exibirPessoa
*/
public void exibirPessoa()
{
String nome=null;
try {
System.out.println("Entre com o nome:");
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
166
nome = in.readLine();
if (nome.length()<1) System.exit(-1);
} catch(IOException e)
{System.out.println(e.getMessage());System.exit(-1);}
Pessoa p = ag.getPessoa(nome);
if (p!=null)
{
System.out.println("\nNome:"+p.getNome()+"\nTelefone:"
+p.getTel()+"\nEndereço:"+p.getEnd());
}
}
/**
obterPessoa
*/
public void obterPessoa()
{
String nome;
String tel;
String end;
try {
System.out.println("Entre com o nome:");
System.out.flush();
nome = in.readLine();
if (nome.length()<1) System.exit(-1);
System.out.println("\nEntre com o Telefone:");
System.out.flush();
tel = in.readLine();
System.out.println("\nEntre com o Endereço:");
System.out.flush();
end = in.readLine();
ag.inserir(new Pessoa(nome ,tel,end));
} catch(IOException e)
{System.out.println(e.getMessage());System.exit(-1);}
}
/**
gravar
*/
public void gravar()
{
try
{
Pessoa p;
BufferedWriter fout = new BufferedWriter(
new FileWriter("agenda.dat"));
for (Enumeration e = ag.getPessoas();
e.hasMoreElements();)
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
{
p = (Pessoa) e.nextElement();
fout.write(p.getNome()+"\n"+p.getTel()+"\n"
+p.getEnd()+"\n");
}
fout.flush();
fout.close();
} catch(FileNotFoundException e)
{ System.out.println("Arq. Não encontrado");}
catch(IOException e)
{System.out.println("Erro na gravação!");}
}
/**
carregar
*/
public void carregar()
{
try
{
String nome;
String tel;
String end;
BufferedReader fin = new BufferedReader(
new FileReader("agenda.dat"));
while ((nome = fin.readLine()) != null)
{
tel = fin.readLine();
end = fin.readLine();
ag.inserir(new Pessoa(nome,tel,end));
}
fin.close();
} catch(FileNotFoundException e)
{System.out.println("Arq. Não encontrado");}
catch(IOException e)
{System.out.println("Erro na leitura!"); }
}
// main
public static void main(String args[])
{
AgendaInt agInt = new AgendaInt();
String opcao="";
for(;;)
{
System.out.println(
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
167
Java na Prática
168
"\nAgenda Tabajara\n***********************\n");
System.out.print("Opcoes:\n(i)nserir\n(c)onsultar"+
"\n(l)istar\n(g)ravar\n(r)ecuperar\n(f)im=>");
System.out.flush();
try
{
opcao = agInt.in.readLine();
if (opcao.length()==0) continue;
} catch(IOException e)
{System.out.println(e.getMessage());System.exit(-1);}
switch(opcao.charAt(0))
{
case 'f': System.exit(0); break;
case 'i': agInt.obterPessoa(); break;
case 'c': agInt.exibirPessoa(); break;
case 'l': agInt.Exibirlista(); break;
case 'g': agInt.gravar(); break;
case 'r': agInt.carregar(); break;
}
}
}
}
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
169
Capítulo VII - Serialização e
Persistência
Um objeto é serializável se podemos transformá-lo em uma sequência de
bytes de modo que seja possível o seu armazenamento em arquivo ou envio
através de um stream. Desta forma o estado do objeto pode ser preservado em
memória não volátil. Neste caso dizemos que o objeto possui persistência, ou
seja o estado do objeto persiste enter as execuções do programa. A persistência
implementada pela serialização é chamada de persistência leve, uma vez que o
programador deve providenciar o armazenamento. Em uma persistência
completa o programador apenas informa ao sistema que o objeto é persistente,
ficando a cargo do ambiente o armazenamento e recuperação do objeto de forma
transparente.
A serialização de objetos foi adicionada à linguagem Java a partir da
versão 1.1 com o intuito de possibilitar a transmissão de objetos entre máquinas
executando diferentes plataformas operacionais, viabilizando dessa forma o RMI
(Remote Method Invocation) e permitir a utilização JavaBeans, que são
componentes configuráveis em tempo de execução, e que mantém a
configuração entre ativações.
Para que os objetos de uma classe sejam serializáveis basta que a classe
implemente a interface Serializable. A partir de então é possível armazenar e
recuperar os objetos da classe por meio de instâncias das classes
ObjectOutputStream e ObjectInputStream, respectivamente. O exemplo
VII.1 mostra como armazenar e recuperar um objeto da classe Pessoa em um
arquivo p.dat.
class pessoa implements Serializable
{
String Nome;
String Tel;
public pessoa(String Nome, String Tel)
{this.Nome = Nome; this.Tel = Tel;}
}
...
Pessoa p = new pessoa(“Ana”,”234-6757”);
FileOutputStream fout = null;
ObjectOutputStream out;
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
170
try {
fout = new FileOutputStream(”p.dat");
out = new ObjectOutputStream(fout);
out.writeObject(p);
out.close();
} catch(Exception e) {System.out.println(”Erro:");return;}
FileInputStream fin = null;
ObjectInputStream in;
try {
fin = new FileInputStream(”p.dat");
in = new ObjectInputStream(fin);
p = (pessoa) in.readObject();
in.close();
} catch(Exception e)
{ System.out.println(”Erro:”+e); return;}
Exemplo VII.1 – Armazenamento e recuperação de um objeto da classe
Pessoa.
Uma questão interessante é: o que acontece com os objetos que são
referenciados pelo objeto que está sendo armazenado? Devem ser armazenados
também ou não? Se os objetos referenciados não forem armazenados juntos com
o objeto corrente, a recuperação do mesmo posteriormente poderá resultar em
uma instância incompleta. Por exemplo, um objeto do tipo Pessoa como
mostrado no exemplo VII.1, referencia dois objetos do tipo String que
armazenam o nome e o telefone da pessoa representada pelo objeto. Se estes
objetos não forem armazenados junto com a instância de Pessoa, qual o sentido
da posterior recuperação do objeto? Portanto, quando um objeto é armazenado
todos os objetos referenciados por ele também são armazenados, desde que os
objetos implementem a interface Serializable. Caso o objeto referenciado
também referencie outro objeto, este último também será armazenado, e assim
por diante, até que toda os objetos da cadeia de referencias sejam armazenados.
Isto pode fazer com que o objeto armazenado ocupe muito mais espaço do que o
imaginado inicialmente pelo programador. Outra pergunta seria: o que acontece
então quando um objeto é referenciado mais de uma vez ou quando acontece um
ciclo no grafo de referências? Neste caso, o objeto é armazenado apenas uma
vez.
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
171
Agenda Eletrônica versão Console 2.1
Com o objetivo de exemplificar o que foi discutido neste capítulo,
modificamos a agenda eletrônica para que a gravação e a carga do arquivo seja
feita através da serialização do objeto Agenda. Primeiramente, foi modificada
as classes Pessoa e Agenda de modo que implementassem a interface
Serializable. Para isto basta importar a interface do pacote java.io e
modificar a declaração das classes, como mostrado abaixo. Como o restante do
código dessas duas classes permanece inalterado, não o repetimos aqui. O leitor
deve se reportar às versões anteriores caso deseje recordar sobre o código
omitido.
/**
Pessoa
*/
import java.io.*;
public class Pessoa implements Serializable
{
...
Sem alteração
...
}
/**
AGENDA Versão Console 2.1.
*/
import java.util.*;
import java.io.*;
public class Agenda implements Serializable
{
...
Sem alteração
...
}
A classe AgendaInt sofre modificações nos métodos gravar() e
carregar(), que os tornam bem mais simples, uma vez que o trabalho de
seguir as referências do objeto da classe Agenda para serializá-los e armazenálos é feito implicitamente. Segue abaixo apenas o código dos métodos
modificados classe AgendaInt. Note que foi preciso usar a hierarquia stream
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
172
para gravar e recuperar os objetos, uma vez que não existem classes do tipo
ObjectReader e ObjectWriter no novo conjunto de classes de E/S.
/**
AgendaInt
Interface console da agenda.
*/
import java.io.*;
import java.util.*;
public class AgendaInt
{
...
Sem alteração
...
/**
gravar
*/
public void gravar()
{
try
{
ObjectOutputStream fout = new ObjectOutputStream(
new FileOutputStream("agenda.dat"));
fout.writeObject(ag);
fout.close();
} catch(FileNotFoundException e)
{ System.out.println("Arq. Não encontrado");}
catch(IOException e)
{System.out.println("Erro na gravação!");}
}
/**
carregar
*/
public void carregar()
{
try
{
ObjectInputStream fin = new ObjectInputStream(
new FileInputStream("agenda.dat"));
ag = (Agenda) fin.readObject();
fin.close();
} catch(FileNotFoundException e)
{System.out.println("Arq. Não encontrado");}
catch(Exception e)
{System.out.println("Erro na leitura!"); }
}
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
...
Sem alteração
...
}
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
173
Java na Prática
174
Capítulo VIII – AWT (Abstract
Window Toolkit)
Até este ponto, todos o exemplos apresentados neste livro foram de
aplicações em modo texto, também chamadas de aplicações não gráficas ou de
console. Hoje em dia, não é possível lançar no mercado uma linguagem de
ampla aplicação que não possua ferramentas para construção de interfaces
gráficas sofisticadas. A linguagem Java além de ter a necessidade de atender este
requisito possui a condição adicional de prover recursos gráficos que permitam o
desenvolvimento de interfaces independentes de plataforma. Talvez, devido a
esta forte exigência, a primeira versão do pacote que implementa o suporte
gráfico acabou sendo conhecido como um excelente exemplo de como não se
deve realizar um projeto. O pacote conhecido como AWT 1.0 (Abstract Window
Toolkit), não era muito harmônico, tinha um conjunto muito pobre de
componentes gráficos e possuía um modelo para tratamento de eventos muito
ineficiente. A versão AWT 1.1 solucionou vários deste problemas, com o
oferecimento de um novo modelo para tratamento dos eventos e com a
introdução de um modelo para programação de componentes, denominados de
JavaBeans. Na versão 1.2 da linguagem novas facilidades foram incorporadas
com a adição de um conjuntos de componentes leves, denominados de Swing, ou
JFC (Java Foundation Classes). A denominação de componentes leves para a
JFC se deve ao fato dos componentes não utilizarem os componentes nativos da
GUI (Graphical User Interface) do ambiente. Eles utilizam primitivas de baixo
nível para sua construção na tela, obtendo assim um alto nível de portabilidade.
Já a AWT utiliza os componentes da GUI (widgets), para sua exibição e por isso
são chamados de componentes pesados. Por isso, para atingir uma portabilidade
razoável, apenas componentes básicos estão presentes na AWT. Mesmo assim a
portabilidade da AWT não é tão grande quanto a JFC, apresentando, em alguns
casos, grandes diferenças de visualização entre ambientes operacionais.
Neste capítulo, abordaremos a versão 1.1 do AWT. Os componentes da
JFC/Swing serão tratados isoladamente no capítulo XX.
A Hierarquia de componentes
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
175
O AWT é um pacote contendo classes e interfaces para a criação de objetos
para interação em janelas gráficas. Com essas classes é possível criar e manter
objetos tais como:
♦
♦
♦
♦
♦
♦
♦
Botões (Classe Button)
Caixa de Escolha (Classe Choice)
Diálogos (Classe Dialog)
Janelas (Classe Window)
Menus (Classe Menu)
Barra de Rolamento (Classe Scrollbar)
Etc.
As classes se organizam em uma hierarquia. A figura VI.XX mostra a
organização hierárquica das classes.
! Color
! Component
" Button
" Canvas
" Checkbox
" Choice
" Container
# Panel
# ScrollPane
# Window
• Dialog
♦ FileDialog
• Frame
" Label
" List
" Scrollbar
" TextComponent
# TextArea
# TextField
! MenuComponent
" MenuBar
" MenuItem
# Menu
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
176
Figura VI.1 – Hierarquia das classes no pacote AWT.
Olá Mundo AWT
Para usar classes definidas na AWT é preciso importá-las:
import java.awt.*;
Algumas classes são usadas para criar objetos que servirão para conter outros
objetos. Estas classes são descendentes da classe Container. Cada classe
possui um conjunto de atributos que podem ser acessados por meio de métodos.
Como de praxe, apresentaremos primeiramente a versão AWT do “olá mundo”
para ilustrar os principais componentes de uma aplicação
import java.awt.*;
class OlaFrame extends Frame
{
public OlaFrame()
{
super(“Ola”);
setSize(100,50);
add(new Label("ola mundo"));
}
public static void main(String args[]){
new OlaFrame().setVisible(true);
}
}
Exemplo XX.XX – Versão AWT do “Olá mundo”.
Para criarmos uma aplicação gráfica precisaremos de algum tipo de
janela para colocarmos os componentes. Por isso a classe OlaFrame do
exemplo XX.XX é uma subclasse da classe Frame que, por sua vez, e
subclasse da classe Window. A classe Frame implementa uma janela gráfica
com título e margem, sendo que a área do Frame inclui a margem. No exemplo
construtor da classe invoca o construtor da superclasse, passando como
argumento o título da janela.
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
177
A classe Frame cria uma janela com 0 x 0 pixels e invisível. Para
dimensioná-la e torná-la visível é necessário usar os métodos setSize() e
setVisible(). A classe Label é usada para criar um objeto de texto que é
adicionado ao Frame. O objeto é posicionado dentro do Frame de acordo com
um esquema de posicionamento, denominado de layout. Se você executou este
programa deve ter notado que não foi possível finalizar o programa a não ser por
meio de um Ctrl-C na console ou algum outro método indireto como o kill da
shell Unix. Isto acontece porque os objetos gráficos na linguagem Java não
possuem tratadores implícitos de eventos. Ou seja, se você quer que algum
evento seja tratado, então forneça o código para isto.
Existem dois modelos para tratamento dos eventos que incidem sobre a
interface. O primeiro foi introduzido na versão 1.0 do AWT e o segundo foi
introduzido na versão 1.1 do AWT com o objetivo de substituir o primeiro, uma
vez que ele sofre de deficiências de projeto e desempenho. Neste livro
abordaremos apenas o modelo de eventos 1.1.
Tratamento de Eventos
O estilo de programação relacionado com interfaces gráficas é a
programação voltada para eventos. Ou seja, o usuário define a interface e um
conjunto de procedimentos para lidar com os eventos que incidirão sobre a
interface. A construção de uma aplicação gráfica exige a criação de pelo menos
um objeto que defina uma área no vídeo para exibir os componentes da
aplicação. Para isso pode-se utilizar um objeto da classe Window ou Frame. O
exemplo XX.XX contém o código de um programa que exibe uma mensagem na
tela. Posteriormente é necessário definir quem deve receber os eventos gerados
pela interação com a interface. Cada linguagem apresenta uma solução distinta
para o tratamento de eventos. A solução Java implementada no modelo de
eventos 1.1 é explicada a seguir.
Modelo de Eventos 1.1
O Modelo de eventos do AWT1.1 é um grande avanço sobre o modelo
1.0. Ele não só é mais flexível e orientado a objetos, como permitiu o
desenvolvimento da interface de aplicação JavaBeans, que viabiliza a descrição
de componentes visuais.
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
178
A idéia por trás do novo modelo é que uma fonte de eventos possa enviar
os eventos para um ou mais objetos cadastrados para o recebimento do evento,
denominados de event listeners. Geralmente, a fonte de evento é um
componente, ou seja, um objeto da classe Component. Um listener é um objeto
que implementa a interface EventListener ou alguma de suas subinterfaces. A figura IV.XX ilustra a estratégia adotada.
Componente
1
evento
Listener
Listener
A
Listener
Componente
2
evento
Listener
B
Listener
Figura VI.XX – Esquema de delegação de eventos AWT1.1.
Note que um Listener pode estar registrado em mais de um
componente. Um componente pode ter mais de uma lista de Listener,
dependendo dos tipos de eventos que ele pode gerar. Uma ação sobre um
componente pode gerar mais de um evento. Para ilustrar o tratamento de
eventos, o exemplo XX.XX mostra como criar uma janela da classe Frame que
trata o evento para fechar a janela, gerado quando o usuário clica o ícone ×,
situado no canto superior direito do Frame.
class FrameX extends Frame implements WindowListener
{
public FrameX() {this("FrameX");}
public FrameX(String Titulo){
super(Titulo);
addWindowListener(this);
setSize(100,50);
}
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
179
public void windowOpened(WindowEvent we){}
public void windowClosed(WindowEvent we){}
public void windowIconified(WindowEvent we){}
public void windowDeiconified (WindowEvent we){}
public void windowActivated(WindowEvent we){}
public void windowDeactivated(WindowEvent we){}
public void windowClosing(WindowEvent we){
setVisible(false);
dispose();
System.exit(0);
}
};
Exemplo XX.XX – Código para tratar o evento para fechar a janela.
Para simplificar o exemplo o próprio componente (do tipo Frame) se
registrou como um Listener para os eventos gerados por ele. De modo a se
habilitar a receber os eventos gerados por um componente do tipo Window a
classe deve implementar a interface WindowListener que é uma subinterface da EventListener.
class FrameX extends Frame implements WindowListener
É preciso também se registrar junto ao componente para receber os
eventos. Como no exemplo VI.XX o componente é uma instância da própria
classe que implementa a interface, basta então adicionar a linha
addWindowListener(this) no construtor da classe. Como a classe
implementa a interface é necessário incluir código para implementar todos os
métodos da interface. Cada método da interface trata um tipo de evento. No caso
do exemplo VI.XX, estamos interessados em tratar eventos que solicitam o
fechamento da janela. Portanto, é necessário incluir o código para tratamento do
evento neste método. Os outros métodos são implementados sem linhas de
código, para cumprir a exigência do compilador. Note que todos os métodos da
classe FrameX recebem um tipo de evento: WindowEvent. No modelo de
eventos 1.1, na versão SDK1.3, existem 14 classes para os eventos, definidas no
java.awt.event,
pacote
todas
subclasses
da
classe
java.awt.event.AwtEvent e cada uma representando um conjunto de
eventos relacionados, como mostrado na tabela VI.XX.
Evento
Descrição
ComponentEvent
Indica que um componente moveu, foi escondido, exibido ou mudou
de tamanho.
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
180
FocusEvent
KeyEvent
MouseEvent
ActionEvent
AdjustmentEvent
Indica que um componente recebeu ou perdeu o foco.
Indica que um componente recebeu uma entrada pelo teclado.
Indica que um componente recebeu uma entrada pelo mouse.
Indica que um componente foi ativado.
Indica que uma barra de rolamento ou componente similar foi
movido.
HierarchyEvent
indica uma mudança na hierarquia de componentes a qual o
componente pertence.
InputEvent
É o evento raiz de todos os eventos de entrada
de componentes.
InputMethodEvent Contém informação sobre o texto que está sendo editado. Sempre que
o texto for alterado este evento será enviado.
InvocationEvent O evento que executa o método run() em um tipo Runnable
quando disparado pelo thread despachador de eventos da AWT.
ItemEvent
Indica que um item de um componente como lista, checkbox, ou
similar foi selecionado.
ContainerEvent
Indica que um componente foi adicionado ou removido de um
container.
WindowEvent
Indica que a janela mudou de estado.
TextEvent
Indica que uma mudança ocorreu em um campo de texto.
Tabela VI.XX – Classes de Eventos.
O exemplo XX.XX mostra o código de uma janela contendo um botão. A
classe que implementa a janela se cadastra junto ao componente Button para
receber o eventos sobre os componente. Para diminuir a complexidade do código
a classe FrameBotao é subclasse da FrameX definida anteriormente. Toda
vez que o componente Button é acionado, gerando um evento
ActionEvent, o método actionPerformed() da instância
FrameBotao é invocado. O método alterna o texto exibido pelo botão a cada
chamada entre os textos “Aperte aqui” e “Valeu”.
import java.awt.*;
import java.awt.event.*;
public class FrameBotao extends FrameX
implements ActionListener
{
Button b;
public FrameBotao()
{
setLayout(null);
setSize(100,80);
b = new Button("Aperte aqui");
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
181
add(b);
b.setBounds(10,40,80,30);
b.addActionListener(this);
}
public void actionPerformed(ActionEvent e)
{
if (b.getLabel().equals("Aperte Aqui"))
b.setLabel(“Valeu”);
else b.setLabel("Aperte Aqui");
}
public static void main(String args[]){
(new FrameBotao()).setVisible(true);
}
}
Exemplo XX.XX – Código para tratar um evento gerado em botão.
Tratamento de Eventos com classes Internas
Agora vamos supor que uma determinada classe necessitasse receber
vários eventos de vários componentes. O código da classe poderia ficar um
pouco confuso, além da lista enorme de interfaces que necessitariam de ser
declarada após a palavra implements. Uma forma mais elegante de se lidar
com estes casos é por meio de classes internas, ou inner classes. Desta forma, o
código para tratar cada grupo de eventos fica localizado em uma única classe, e
como as classes internas possuem acesso à classe externa, os atributos e métodos
da classe externa podem ser diretamente referenciados. O exemplo XX.XX
mostra o exemplo XX.XX alterado de forma que o evento ActionEvent
gerado acionamento do componente Button seja tratado por uma classe
interna.
import java.awt.*;
import java.awt.event.*;
public class FrameBotao extends FrameX
{
Button b;
Public FrameBotao()
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
182
{
setLayout(null);
setSize(100,80);
b = new Button("Aperte Aqui");
add(b);
b.setBounds(10,40,80,30);
b.addActionListener(new Bl());
}
class Bl implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
if (b.getLabel().equals("Aperte Aqui"))
b.setLabel(“Valeu”);
else b.setLabel("Aperte Aqui");
}
}
public static void main(String args[]){
(new FrameBotao()).setVisible(true);
}
}
Exemplo XX.XX – Código para tratar um evento gerado em botão.
Cada componente pode receber um determinado conjunto de listener para
receber eventos. A Figura VI.XX mostra a relação entre alguns componentes e
os eventos que podem ser gerados por eles.
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
Button
183
ComponentEvent
Frame
FocusEvent
Choice
KeyEvent
TextField
MouseEvent
Scrollbar
ActionEvent
Component
AdjustmentEvent
Label
Dialog
Panel
Menu
ItemEvent
WindowEvent
ContainerEvent
MenuItem
TextEvent
Figura XX.XX – Relação entre os componentes e os eventos.
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
184
As instâncias das classes de eventos mantém as informações relativas ao
evento. Por exemplo, a classe MouseEvent possui os seguintes métodos para
recuperação de informação sobre o evento:
Método
int getId()
int getX()
int getY()
Point getPoint()
int getClickCount()
boolean isPopupTrigger()
void translatePoint(int x,
int y)
Descrição
tipo do evento. Por exemplo:
MouseEvent.MOUSE_CLICKED,
MouseEvent.MOUSE_PRESSED
coordenada x do evento.
coordenada y do evento.
coordenada x e y do evento.
retorna o número de mouse clicks.
indica se o evento é um disparo para menu pop-up.
move a posição do evento por x e y.
Tabela VI.XX – Métodos da classe MouseEvent.
Basicamente, para se codificar um tratador para um determinado evento
basta seguir os seguintes passos:
1. Determine o grupo de eventos deseja tratar.
2. No nome do grupo de eventos substitua o termo trecho Event por
Listener. Este é o nome da interface que você deve implementar. Crie
uma classe interna que implementa a interface.
3. Codifique os métodos que tratam os eventos que você deseja tratar. Todos os
métodos da interface tem que ser implementados, mas o corpo dos métodos
que não interessam podem ser deixados vazios.
4. Registre um objeto da classe interna junto ao componente, por meio do
método addXXX, onde XXX é o nome da interface implementada.
Por exemplo, no exemplo VI.XX, para tratar o grupo de eventos
ActionEvent foi criada uma classe interna que implementa a interface
ActionListener, e um objeto da classe interna é registrado no componente
por meio do método addActionListener. Portanto, tendo o nome do grupo
de eventos é fácil obter o nome da interface e do método de registro no
componente. Segue abaixo uma lista com alguns métodos para registro de
listeners.
♦ Button.addActionListener(ActionListener l)
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
185
♦
♦
♦
♦
Frame.addWindowListener(WindowListener l)
Choice.addItemListener(ItemListener l)
TextField.addTextListener(TextListener l)
Scrollbar.addAdjustamentListener(AdjustamentListene
r l)
♦ Component.addComponentListener(ComponentListener l)
♦ Component.addFocusListener(FocusListener l)
♦ Component.addKeyListener(KeyListener l)
Algumas interfaces exigem a implementação de um grande número de
métodos, como por exemplo a interface WindowListener, como foi visto no
exemplo VI.XX. Isto pode tornar a implementação das interfaces uma tarefa um
tanto tediosa. Com o objetivo de auxiliar esta tarefa, foi incluído no pacote
java.awt.event seis classes abstratas, denominadas adapters que facilitam
a implementação de Listener. O programador só precisa criar uma subclasse da
classe adapter escolhida e sobrescrever o método desejado. Para determinar o
nome da classe adapter basta substituir no nome da interface a subcadeia
Listener por adapter. A tabela VI.XX apresenta os nomes das classes adapters.
ComponentAdapter
ContainerAdapter
FocusAdapter
KeyAdapter
MouseAdapter
MouseMotionAdapter
WindowAdapter
Tabela VI.XX – Classes adapters.
O exemplo XX.XX mostra o exemplo XX.XX alterado de forma que o
evento ActionEvent gerado acionamento do componente Button seja
tratado por uma classe interna que é subclasse da classe MouseAdapter.
import java.awt.*;
import java.awt.event.*;
public class Botao extends FrameX {
Button b;
public Botao() {
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
186
setLayout(null);
setSize(100,80);
b = new Button("Aperte Aqui");
add(b);
b.setBounds(10,40,80,30);
b.addActionListener(new Bl());
}
class Bl extends MouseAdapter {
public void actionPerformed(ActionEvent e) {
if (b.getLabel().equals("Aperte Aqui"))
b.setLabel("Valeu");
else b.setLabel("Aperte Aqui");
}
}
public static void main(String args[]){
(new Botao()).setVisible(true);
}
}
Exemplo XX.XX – Código para tratar um evento gerado em botão.
No entanto, existem algumas desvantagens em se usar classes abstratas
no lugar de interfaces. A primeira delas é que elimina a possibilidade da classe
ser subclasse de outra classe e a segunda é que se o programador cometer algum
erro na digitação do nome de algum método, ele será encarado como um novo
método e o original não será sobrescrito.
Exemplo Básico
Apresentaremos agora um exemplo um pouco mais complexo, porém
simples o suficiente para que seja facilmente entendido. O objetivo deste
exemplo e servir de base para apresentarmos os principais componentes da
biblioteca AWT. Portanto, a medida que novos componentes forem
apresentados, iremos exemplificar o uso deste componentes acrescentando-os
em nosso exemplo básico.
A função de nosso exemplo é servir como ambiente para execução de
comandos MS-DOS, tais como dir e type. O exemplo foi projetado para
funcionar nos ambientes MS-WINDOWS 95 e MS-WINDOWS 98 mas pode ser
facilmente adaptado para funcionar em outros ambientes operacionais. A figura
XX.XX mostra a interface da primeira versão de nosso exemplo. Note que,
apesar de simples, o nosso exemplo apresenta três componentes em sua
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
187
interface: um botão e duas caixas de texto, sendo uma com barras de rolamento e
capacidade para múltiplas linhas.
Figura XX.XX – Aparência da aplicação para execução de comandos MSDOS.
A caixa de texto com capacidade para uma linha de texto é um componente
TextField e sua função é permitir a entrada de comandos dos para a
execução. A caixa de texto com capacidade para múltiplas linhas de texto é um
componente TextArea e sua função é exibir o resultado da execução do
comando. Finalmente, o botão é implementado por um componente Button e
sua função e gerar eventos para execução do comando digitado. O exemplo
XX.XX mostra o código da aplicação, implementado pela classe Comando.
1
2
3
4
5
6
import java.io.*;
import java.awt.*;
import java.awt.event.*;
public class Comando extends Frame {
private TextField tfComando;
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
188
private TextArea taSaida;
private Button btExec;
private String com;
public Comando() {
com = "command.com /c "; // Altere essa linha para outros ambientes operacionais
tfComando = new TextField(50);
taSaida = new TextArea(20,60);
btExec = new Button(“Executa”);
taSaida.setEditable(false);
add(tfComando,BorderLayout.NORTH);
add(btExec,BorderLayout.SOUTH);
add(taSaida,BorderLayout.CENTER);
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent evt) {
System.exit(0);
}
});
btExec.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
btExecActionPerformed(evt);
}
});
pack();
}
private void btExecActionPerformed(ActionEvent evt) {
String s = tfComando.getText();
if (s==null) return;
try {
Process p = Runtime.getRuntime().exec(com+s);
InputStream in = p.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(in));
String line;
taSaida.setText("");
while ((line = br.readLine()) != null) {
taSaida.append(line+'\n');
}
taSaida.append("Fim!");
} catch(Exception e) {taSaida.setText(e.getMessage()); return;}
}
public static void main(String args[]) {
new Comando().show();
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
56
57
189
}
}
Exemplo XX.XX – Código que executa um comando DOS.
Na linha 12 é criado um objeto String contendo o parte inicial do comando
necessário para invocar a shell do MS-DOS. Essa parte inicial deve ser alterada
para adaptar o exemplo para outros sistemas operacionais. Nas linhas 14 a 16
são criados os componentes que farão parte da interface. Na linha 14 o
componente TextField é criado com espaço para exibir 50 caracteres. Na
linha 15 o componente TextArea é criado com espaço para exibir 20 linhas e
50 caracteres por linha. Na linha 16 o componente Button é criado com um o
texto “Executa”. Na linha 18 é chamado o método setEditable() do
componente TextArea com o argumento false para impedir que o seu
conteúdo seja editado. Isso ocorre porque a função do componente é apenas
exibir o resultado da execução dos comandos.
O leitor deve ter notado que a criação das classes que irão tratar os eventos é
um pouco diferente do que mostramos até agora. Nas linhas 24 a 28 e 30 a 34 as
declarações das classes são feitas no mesmo local onde os objetos são
instanciados. Este tipo de classe interna é denominado de classe interna
anônima. A classe é denominada de anônima pois nenhum nome é associado a
ela. A classe anônima se torna automaticamente subclasse da classe mencionada
após o operador new. É vantajoso usar este tipo de construção quando apenas
uma instância da classe será gerada, uma vez que amarra a declaração da classe
com seu uso. No entanto, em alguns casos esta técnica pode diminuir a
legibilidade do código.
Outro fato que pode intrigar o leitor é a distribuição dos componentes na
janela. Aparentemente, pouco código foi dedicado ao posicionamento dos
elementos na interface. Mesmo assim os componentes foram arranjados de
forma razoavelmente ordenada na interface. Isto se deve ao fato de todos os
componentes do tipo Container seguirem um arranjo predefinido (layout).
Este arranjo é implementado pelas classes BorderLayout, CardLayout,
FlowLayout, GridBagLayout e GridLayout, sendo o BorderLayout
o arranjo default. Existe também a possibilidade de não se adotar nenhum
layout. Neste caso é preciso posicionar os componentes em termos de
coordenadas dentro do objeto do tipo Container. Os objetos de layout serão
tratados mais adiante. No momento basta saber que o layout adotado é o default
e que este tipo de layout arranja os componentes de acordo com os pontos
cardeais (NORTH, SOUTH, EAST, WEST, CENTER). As linhas 20 a 22
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
190
mostram as adições de componentes dentro do Frame, com a indicação do seus
posicionamentos relativos segundo o layout adotado. A adição de componentes
é feita por meio do método add().
As linhas 38 a 52 mostram o código do método que executa o comando MSDOS e exibe o resultado no componente TextArea. Na linha 42 o comando
MS-DOS é executado por meio do método exec() do objeto Runtime. O
objeto Runtime é obtido pelo método estático getRuntime() da classe
Runtime, uma vez que não pode ser instanciado diretamente. O processo filho,
uma vez iniciado, é executado de forma concorrente com o processo pai. O
método exec() retorna um objeto do tipo Process, tornando possível
controlar o processo e obter informações sobre ele. No nosso caso estamos
interessados em capturar a saída padrão do processo filho para exibi-la na
interface. Isso é feito na linha 43 por meio do método getInputStream()
do objeto Process. O objeto InputStream é canalizado para um objeto
BufferedReader com o objetivo de facilitar sua manipulação. As linhas
restantes do método tratam da leitura da saída padrão do processo filho e de sua
exibição na TextArea. Na linha 46 é usado o método setText() para
colocar um texto vazio no componente e, dessa forma, limpar a TextArea. Na
linha 48 a String lida da saída padrão do processo filho é anexada na
TextArea.
Acrescentando Cores
Podemos alterar as cores de alguns componentes do exemplo XX.XX para
torná-lo visualmente mais interessante por meio de objetos da classe Color.
Esta classe encapsula a criação de cores por meio dos níveis RGB. Ela mantém
também um conjunto de atributos constantes com valores de cores predefinidos.
Podemos, por exemplo, alterar a cor de fundo do componente TextArea por
meio do método setBackground().
taSaida.setBackground(Color.lightGray);
Podemos alterar também a cor de fundo do componente Button. Neste
caso vamos obter para definir a cor por meio de um número que representa seus
componentes RGB.
btExec.setBackground(new Color(0x08FDDF10));
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
191
Para modificar a cor frontal é preciso utilizar o método
setForeground(). Todos os componentes possuem métodos equivalentes a
estes para alteração das cores.
Gerenciando o Layout
O posicionamento de componentes dentro de objetos do tipo Container é
determinado por uma instância de objeto denominado de gerenciador de layout
(layout managers). A AWT fornece 5 classes para a implementação de layouts,
descritas na tabela XX.XX. O layout default é o implementado pela classe
BorderLayout. O pacote Swing acrescenta a classe BoxLayout ao conjunto
de gerenciadores de layout.
Gerenciador de Layout Descrição
BorderLayout
FlowLayout
CardLayout
GridBagLayout
GridLayout
Posiciona e redimensiona os componentes de um Container
em cinco regiões: norte, sul, leste e oeste. Cada região é
identificada respectivamente pelas constantes: NORTH, SOUTH,
EAST, WEST, e CENTER.
Posiciona os componentes de um Container em um fluxo da
esquerda para a direita, como linhas em um parágrafo. Cada
linha é centralizada.
Trata cada componente do Container como uma a carta.
Somente uma carta é visível em um determinado instante e o
Container funciona como uma pilha de cartas. O primeiro
componente adicionado é o primeiro visível quando o
Container é exibido.
É o gerenciador de layout mais flexível e o mais complexo. Ele
arranja os componentes verticalmente e horizontalmente sem
exigir que os componentes tenham o mesmo tamanho. Mantém
um grade retangular de células, sendo que cada componente
pode ocupar mais de uma célula.
Posiciona e redimensiona os componentes de um Container
em uma grade retangular. O Container é dividido em
retângulos de tamanhos iguais e um componente é colocado em
cada retângulo.
Tabela XX.XX –Gerenciadores de Layout.
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
192
Para definir o layout de um objeto do tipo Container basta utilizar o
método setLayout() do objeto, passando como argumento um objeto
gerenciador de layout, como mostrado abaixo:
Frame f = new Frame();
f.setLayout(FlowLayout);
É possível também não adotar layout algum, optando por posicionar os
componentes em coordenadas absolutas dentro do Container. Para isso basta
passar null como argumento para o método setLayout(). O
posicionamento absoluto de componentes pode ser feito pelos métodos
setLocation() ou setBounds() do componente, como mostrado abaixo.
Frame f = new Frame();
f.setLayout(null);
Button b = new Button(“Teste”);
f.add(b);
b. setLocation(10, 10);
Porém, a adoção de gerenciadores de layout permite o desenvolvimento que
se adaptam mais facilmente à redimensionamentos e à mudanças de linguagens.
Isto é o posicionamento e dimensionamento absoluto pode fazer com que
componentes tornem-se parcialmente ou totalmente ocultos com o
redimensionamento da janela. O mesmo pode ocorrer com os “labels” dos
componentes quando forem traduzidos para outra língua. Já estes problemas não
ocorrem com o posicionamento e dimensionamento relativo.
Exemplo com BorderLayout
O exemplo XX.XX utiliza este gerenciador de layout para distribuir os
componentes na interface. O exemplo abaixo mostra distribuição de cinco botões
em um Frame por meio deste tipo de gerenciador de layout.
import java.awt.*;
public class BorderTeste extends FrameX {
public BorderTeste() {
setLayout(new BorderLayout());
add(new Button("Norte"), BorderLayout.NORTH);
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
193
add(new Button("Sul"), BorderLayout.SOUTH);
add(new Button("Leste"), BorderLayout.EAST);
add(new Button("Oeste"), BorderLayout.WEST);
add(new Button("Centro"), BorderLayout.CENTER);
}
public static void main(String a[]){new BorderTeste().show();}
}
Exemplo XX.XX – Frame com BorderLayout.
A figura XX.XX mostra o resultado da execução do código do exemplo
XX.XX.
Figura XX.XX – Aparência do Frame com BorderLayout.
Exemplo com FlowLayout
O exemplo abaixo mostra distribuição de cinco botões em um Frame por
meio deste tipo de gerenciador de layout.
import java.awt.*;
public class FlowTeste extends FrameX {
public FlowTeste() {
setLayout(new FlowLayout());
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
194
add(new Button("UM"));
add(new Button("DOIS"));
add(new Button("TRÊS"));
add(new Button("QUATRO"));
add(new Button("CINCO"));
setSize(200,100);
}
public static void main(String a[]){new FlowTeste().show();}
}
Exemplo XX.XX – Frame com FlowLayout.
A figura XX.XX mostra o resultado da execução do código do exemplo
XX.XX. Note que quando acaba o espaço em uma linha os componentes são
posicionados na linha de baixo seguindo da esquerda para a direita. Note
também que cada linha é centralizada no Container.
Figura XX.XX – Aparência do Frame com FlowLayout.
Exemplo com CardLayout
O exemplo abaixo mostra usar o CardLayout para alternar a exibição de
componentes. Um botão é adicionado à interface para receber eventos. Quando o
botão é pressionado o tratador de eventos alterna a exibição dos componentes do
CardLayout por meio de chamadas ao método next() do gerenciador de
layout. A JFC fornece o componente JtabbedPane que apresenta maiores
facilidades.
import java.awt.*;
import java.awt.event.*;
public class CardTeste extends FrameX {
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
195
Panel p;
CardLayout cl;
Button b;
public CardTeste() {
setLayout(new BorderLayout());
p = new Panel();
b = new Button("Muda carta");
cl = new CardLayout();
p.setLayout(cl);
p.add(new Label("Este Label está na primeira carta"),"um");
p.add(new Label("Este Label está na segunda carta"),"dois");
add(b, BorderLayout.NORTH);
add(p, BorderLayout.SOUTH);
b.addActionListener( new ActionListener() {
public void
actionPerformed(ActionEvent evt) {
cl.next(p);
}
});
pack();
}
public static void main(String a[]){new CardTeste().show();}
}
Exemplo XX.XX – Frame com CardLayout.
Note que ao adicionar um componente ao Container é preciso passar um
identificador. A figura XX.XX mostra o resultado da execução do código do
exemplo XX.XX.
Figura XX.XX – Aparência do Frame com CardLayout.
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
196
Exemplo com GridLayout
O exemplo abaixo mostra distribuição de cinco botões em um Frame por
meio deste tipo de gerenciador de layout.
import java.awt.*;
public class GridTeste extends FrameX {
public GridTeste() {
setLayout(new GridLayout(3,2));
add(new Button("UM"));
add(new Button("DOIS"));
add(new Button("TRÊS"));
add(new Button("QUATRO"));
add(new Button("CINCO"));
}
public static void main(String a[]){new GridTeste().show();}
}
Exemplo XX.XX – Frame com GridLayout.
A figura XX.XX mostra o resultado da execução do código do exemplo
XX.XX.
Figura XX.XX – Aparência do Frame com GridLayout.
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
197
Exemplo com GridBagLayout
Este gerenciador é o mais complexo, mas é também o que permite um maior
controle sobre o posicionamento dos elementos, sem perder as vantagens de um
posicionamento relativo. O exemplo XX.XX mostra distribuição de nove botões
em um Frame por meio deste tipo de gerenciador de layout.
import java.awt.*;
public class GridBagTeste extends FrameX {
public GridBagTeste() {
Button b;
GridBagLayout gridbag = new GridBagLayout();
GridBagConstraints c = new GridBagConstraints();
setLayout(gridbag);
for (int i=1;i<10;i++) {
switch(i) {
case 1: c.fill = GridBagConstraints.BOTH;
c.weightx = 1.0;
break;
case 6:
case 3: c.gridwidth = GridBagConstraints.REMAINDER;
c.weightx = 1.0;
break;
case 4: c.weightx = 0.0;
break;
case 5: c.gridwidth = GridBagConstraints.RELATIVE;
c.weightx = 1.0;
break;
case 7: c.gridwidth = 1;
c.gridheight = 2;
c.weighty = 1.0;
break;
case 8: c.gridwidth = GridBagConstraints.REMAINDER;
c.weighty = 0.0;
c.gridheight = 1;
break;
}
b = new Button("Botão "+i);
gridbag.setConstraints(b, c);
add(b);
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
198
}
pack();
}
public static void main(String args[]) { new GridBagTeste().show(); }
}
Exemplo XX.XX – Frame com GridBagLayout.
O posicionamento relativo dos componentes é controlado por meio de um
objeto da classe GridBagConstraints. Ele possui vários atributos públicos que
orientam o gerenciador de layout no momento de posiciona e reorganizar os
componentes. O objeto é associado a cada componente por meio do método
setConstraints() da instância GridBagLayout. Os atributos utilizados no
exemplo foram:
fill
– usado quando a área para a exibição do componente é maior que a
área é requisitada pelo componente. Os valores possíveis (definidos na
classe GridBagConstraints) são NONE (default), HORIZONTAL (torna o
componente largo o suficiente para preencher a área horizontalmente),
VERTICAL (torna o componente alto o suficiente para preencher a área
na vertical), e BOTH (preenche nos dois sentidos).
– determina como o espaço excedente é distribuído,
definindo o comportamento para o redimensionamento. Ou seja define a
proporção do espaço que excedente que será distribuído entre os
componentes. Se não for especificado para pelo menos uma linha
(weightx) e coluna (weighty), todos os componentes serão agrupados
no centro do Container, uma vez que o peso de cada um é zero
(default).
weightx, weighty
gridwidth, gridheight – Especifica o numero de
(gridwidth) ou coluna (gridheight) ocupadas
células em um linha
pela área de exibição
de um componente. O valor default é 1. Os valores possíveis (definidos
na classe GridBagConstraints) são REMAINDER para especificar que o
componente deve ser o último da linha (para gridwidth) ou coluna
(para gridheight). RELATIVE para especificar que o componente deve
ser o vizinho do último na linha (para gridwidth) ou coluna (para
gridheight). Ou valores inteiros especificando o número de linhas e
colunas ocupadas.
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
199
A figura XX.XX mostra o resultado da execução do código do exemplo
XX.XX.
Figura XX.XX – Aparência do Frame com GridBagLayout.
Utilizando Listas
Muitas vezes é necessário que o usuário selecione um item a partir de uma
lista de itens disponíveis. A AWT torna possível a seleção de um item em uma
lista de itens por meio do componente Choice. Este componente possui métodos
para a adição, remoção e acesso a itens. O exemplo abaixo mostra a alteração do
exemplo XX.XX que usa uma lista de escolha para armazenar os comandos
anteriores. Desta forma o usuário economiza na digitação de comandos
repetidos.
1
2
3
4
5
6
7
8
9
10
11
12
13
import java.io.*;
import java.awt.*;
import java.awt.event.*;
public class Comando2 extends java.awt.Frame {
private TextField tfComando;
private TextArea taSaida;
private Button btExec;
private Panel panel;
private Choice ch;
private String com;
public Comando2() {
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
com = "command.com /c ";
tfComando = new TextField(50);
taSaida = new TextArea(20,60);
panel = new Panel();
btExec = new Button("Executa");
ch = new Choice();
ch.setVisible(false);
btExec.setBackground(new Color(0x08FDDF10));
taSaida.setBackground(Color.lightGray);
taSaida.setEditable(false);
add(panel,BorderLayout.NORTH);
panel.add(tfComando,BorderLayout.NORTH);
panel.add(ch,BorderLayout.CENTER);
add(btExec,BorderLayout.SOUTH);
add(taSaida,BorderLayout.CENTER);
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent evt) {
System.exit(0);
}
});
btExec.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
btExecActionPerformed(evt);
}
});
ch.addItemListener( new ItemListener() {
public void itemStateChanged(ItemEvent evt) {
if (evt.getStateChange() == ItemEvent.SELECTED)
tfComando.setText(ch.getSelectedItem());
}
});
pack();
}
private void btExecActionPerformed(ActionEvent evt) {
String s = tfComando.getText();
if (s==null) return;
boolean inclui =true;
for (int i= ch.getItemCount()-1;i>=0;i--)
if (s.equals(ch.getItem(i))) inclui=false;
if (inclui) {
ch.add(s);
ch.setVisible(true);
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
200
Java na Prática
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
201
pack();
}
try {
Process p = Runtime.getRuntime().exec(com+s);
InputStream in = p.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(in));
String line;
taSaida.setText("");
while ((line = br.readLine()) != null) taSaida.append(line+'\n');
taSaida.append("Fim!");
} catch(Exception e)
{taSaida.setText(e.getMessage()); return;}
}
public static void main(String args[]) { new Comando2().show(); }
}
Exemplo XX.XX – Aplicação para execução de comandos MS-DOS com lista
de escolha.
Na linha 21 é usado o método setVisible() para que o componente
Choice fique invisível inicialmente. Isto é feito porque no início a lista de itens
está vazia e, portanto, não é preciso exibi-la. Nas linhas 45 a 50 é adicionado um
objeto ItemListener à lista de listener do objeto Choice. Este objeto receberá
os eventos de seleção de itens na lista de escolha. Nas linhas 58 a 64 é verificado
se o comando digitado já está incluído na lista de itens. Caso não esteja o item é
incluído e o método pack() do Frame é chamado, para que a janela se ajuste ao
tamanho dos componentes.
A figura XX.XX mostra o resultado da execução do código do exemplo
XX.XX.
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
202
Figura XX.XX – Aparência da aplicação para execução de comandos MS-DOS
com lista de escolha.
Trabalhando com Menus e Diálogos
Outros componentes importantes para criação de interfaces com o usuário
são os menus e as caixas de diálogos. De forma a ilustrar o uso desses recursos
modificaremos o exemplo XX.XX para conter uma barra de menus com apenas
um menu. O menu conterá dois itens de menu. O acionamento do primeiro item
de menu ocasionará a exibição de um pequeno texto de ajuda no componente
TextArea. O acionamento do segundo item de menu ocasionará a exibição de
uma caixa de diálogo contendo informações sobre a aplicação.
1
2
3
4
5
6
import java.io.*;
import java.awt.*;
import java.awt.event.*;
public class Comando3 extends java.awt.Frame {
private TextField tfComando;
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
private TextArea taSaida;
private Button btExec;
private Panel panel;
private Choice ch;
private String com;
public Comando3() {
com = "command.com /c ";
tfComando = new TextField(50);
taSaida = new TextArea(20,60);
panel = new Panel();
btExec = new Button("Executa");
ch = new Choice();
ch.setVisible(false);
btExec.setBackground(new Color(0x08FDDF10));
taSaida.setBackground(Color.lightGray);
taSaida.setEditable(false);
Menu menu = new Menu("Ajuda");
menu.add("Resumo");
menu.addSeparator();
menu.add("Sobre");
MenuBar mb = new MenuBar();
mb.add(menu);
setMenuBar(mb);
add(panel,BorderLayout.NORTH);
panel.add(tfComando,BorderLayout.NORTH);
panel.add(ch,BorderLayout.CENTER);
add(btExec,BorderLayout.SOUTH);
add(taSaida,BorderLayout.CENTER);
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent evt) {
System.exit(0);
}
});
btExec.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
btExecActionPerformed(evt);
}
});
ch.addItemListener( new ItemListener() {
public void itemStateChanged(ItemEvent evt) {
if (evt.getStateChange() == ItemEvent.SELECTED)
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
203
Java na Prática
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
204
tfComando.setText(ch.getSelectedItem());
}
});
menu.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
if (evt.getActionCommand().equals("Sobre")) mostraSobre();
else if (evt.getActionCommand().equals("Resumo"))
taSaida.setText("Digite o Comando e pressione “+
”o botão <executa> para executar.");
}
});
pack();
}
private void mostraSobre() {
Dialog d = new Dialog(this,"Sobre Comando");
d.setModal(true);
Label lb1 = new Label("Comando Versão 1.2", Label.CENTER);
Label lb2 = new Label("Alcione de Paiva Oliveira", Label.CENTER);
lb1.setFont(new Font ("Dialog", Font.BOLD|Font.ITALIC, 18));
lb2.setFont(new Font ("Courier New", 0, 16));
d.add(lb1,BorderLayout.NORTH);
d.add(lb2,BorderLayout.SOUTH);
d.setBackground(Color.cyan);
d.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent evt) {
evt.getWindow().dispose();
}
});
d.pack();
d.show();
}
private void btExecActionPerformed(ActionEvent evt) {
String s = tfComando.getText();
if (s==null) return;
boolean inclui =true;
for (int i= ch.getItemCount()-1;i>=0;i--)
if (s.equals(ch.getItem(i))) inclui=false;
if (inclui) {
ch.add(s);
ch.setVisible(true);
pack();
}
try {
Process p = Runtime.getRuntime().exec(com+s);
InputStream in = p.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(in));
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
105
106
107
108
109
110
111
112
113
114 }
205
String line;
taSaida.setText("");
while ((line = br.readLine()) != null) taSaida.append(line+'\n');
taSaida.append("Fim!");
} catch(Exception e)
{taSaida.setText(e.getMessage()); return;}
}
public static void main(String args[]) { new Comando3().show(); }
Exemplo XX.XX – Aplicação para execução de comandos MS-DOS com barra
de menus e uma caixa de diálogo.
As linhas 27 a 33 contém o código para a inserção da barra de menus e do menu.
Na linha 27 é criado um componente Menu com o label “Ajuda”. Nas linhas 28 a
30 são adicionados dois itens de menu e um separador ao componente Menu. Nas
linha 31 e 32 é criado um componente MenuBar e o componente Menu é
adicionado ao MenuBar. Na linha 33 o componente MenuBar é definida como a
barra de menu da janela corrente. Nas linhas 60 a 67 é adicionado um objeto
ActionListener à lista de Listener do objeto Menu. Este objeto receberá os
eventos de seleção de itens do menu. Caso o item de menu selecionado seja o
“Sobre” então o método mostraSobre() é invocado. Caso o item de menu
selecionado seja o “Resumo” então é exibido um texto de ajuda no componente
TextArea. As linhas 71 a 88 contém o código do método mostraSobre(). Este
método é responsável pela montagem e exibição da caixa de diálogo com
informações sobre a aplicação. Na linha 72 é criada uma instância da classe
Dialog. O construtor desta classe exige que seja passado como parâmetro um
objeto da classe Frame ou Dialog para deter a posse do objeto a ser criado.
Quando o objeto possuidor é tornado invisível ou minimizado a janela Dialog é
automaticamente tornada invisível ou minimizada. No caso do exemplo o Frame
corrente é passado como parâmetro. Na linha 73 é invocado o método
setModal() para torna a o diálogo modal. O diálogo modal bloqueia toda
entrada em todas as janelas no contexto da aplicação, exceto pelas janelas
criadas tendo o diálogo como possuidor.
A figura XX.XX mostra o resultado da execução do código do exemplo
XX.XX.
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
206
Figura XX.XX – Aparência da aplicação para execução de comandos MS-DOS
com barra de menus.
Capturando Eventos do Teclado
Principais Classes
Color
Esta classe encapsula a criação de cores por meio dos níveis RGB. Ela
mantém também um conjunto de atributos constantes com valores de cores
predefinidos.
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
207
Hierarquia
java.lang.Object
|
+--java.awt.Color
Atributos públicos
Atributo
Color
Color
Color
Color
Color
Color
Color
Color
Color
Color
Color
Color
Color
Descrição
black
blue
cyan
darkGray
gray
green
lightGray
magenta
orange
pink
red
white
yellow
A cor preta.
A cor azul.
A cor azul claro.
A cor cinza escuro.
A cor cinza.
A cor verde.
A cor cinza claro.
A cor violeta.
A cor laranja.
A cor rosa.
A cor vermelha.
A cor branca.
A cor amarela.
Construtores
Construtor
Descrição
Color(float r, float g, Cria uma cor opaca RGB com os valores red, green, e
float b)
blue especificados dentro da faixa (0.0 - 1.0).
Color(int rgb)
Cria uma cor opaca com os valores RGB combinados no
parâmetro rgb. O componente red está definido nos
bits 16-23, o componente green está definido nos bits 815, e o componente blue está definido nos bits 0-7.
Color(int r, int g,
Cria uma cor opaca RGB com os valores red, green, e
int b)
blue especificados dentro da faixa (0 - 255).
Métodos mais usados
Método
Descrição
Color brighter()
Color darker()
Color decode(String nm)
Cria uma versão mais clara da cor corrente.
Cria uma versão mais escura da cor corrente.
Converte a String em um inteiro e retorna a cor
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
equals(Object obj)
int getBlue()
int getGreen()
int getRed()
int getTransparency()
String toString()
208
opaca relacionada.
Verifica se o objeto é igual a essa cor.
Retorna o componente azul da cor.
Retorna o componente verde da cor.
Retorna o componente vermelho da cor.
Retorna o modo de transparência desta cor.
Retorna a representação em String desta cor.
Exemplo
Color c = new Color(0xFFC0C0C0);
Component
Um componente é um objeto que possui uma representação gráfica que
pode ser exibida em uma tela e pode interagir com o usuário.
Hierarquia
java.lang.Object
|
+--java.awt.Component
Atributos públicos
Atributo
Descrição
float BOTTOM_ALIGNMENT
float CENTER_ALIGNMENT
loat LEFT_ALIGNMENT
float RIGHT_ALIGNMENT
float TOP_ALIGNMENT
Alinhamento na parte inferior.
Alinhamento ao centro.
Alinhamento à esquerda.
Alinhamento à esquerda.
Alinhamento na parte superior.
Construtor
Construtor
Component()
Descrição
Constrói um novo Component.
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
209
Métodos mais usados
Método
Descrição
void add(PopupMenu popup)
void addComponentListener(
ComponentListener l)
boolean contains(int x,
int y)
float getAlignmentX()
float getAlignmentY()
Color getBackground()
Rectangle getBounds()
Component getComponentAt(
int x, int y)
Cursor getCursor()
Font getFont()
Color getForeground()
Graphics getGraphics()
int getHeight()
Point getLocation()
String getName()
Container getParent()
Dimension getSize()
Toolkit getToolkit()
int getWidth()
int getX()
int getY()
boolean isDisplayable()
boolean isEnabled()
boolean isFocusOwner()
boolean isVisible()
void list(PrintStream out)
void paint(Graphics g)
void processEvent(AWTEvent e)
void remove(MenuComponent
popup)
void setBounds(int x, int y,
int width,
Adiciona um menu do tipo PopupMenu ao
componente.
Adiciona o ComponentListener para
receber eventos sobre o componente.
Verifica se o componente possui a coordenada.
Retorna o alinhamento no eixo x.
Retorna o alinhamento no eixo y.
Retorna a cor de fundo do componente.
Retorna as dimensões do componente.
Retorna o componente do Component que
contém a coordenada especificada. Apenas o
primeiro nível de descendência é examinado.
Retorna o cursor deste componente.
Retorna o fonte deste componente.
Retorna a cor de frente deste componente.
Cria um contexto gráfico para este
componente.
Retorna a altura deste componente.
Retorna a localização deste componente.
Retorna o nome deste componente.
Retorna o Container onde está inserido este
componente.
Retorna as dimensões deste componente.
Retorna o Toolkit deste componente.
Retorna a largura deste componente.
Retorna a coordenada X deste componente.
Retorna a coordenada Y deste componente.
Verifica se este componente pode se exibido.
Verifica se este componente está habilitado.
Verifica se este componente possui o foco.
Verifica se este componente esta visível.
Lista este componente no PrintStream.
Pinta o este Container. Este método deve
ser sobrescrito se o programador deseja
desenhar no Container. Neste caso
super.paint(g) deve ser invocado.
Processa os eventos ocorrendo neste
componente.
Remove o menu popup menu deste
componente.
Define os limites deste componente.
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
int height)
setEnabled(boolean b)
setFont(Font f)
void setName(String name)
setSize(int width,
int height)
void setVisible(boolean b)
String toString()
void
void
void
void
210
Define este componente como habilitado.
Define o fonte deste Component.
Define o nome deste componente.
Define o tamanho deste componente.
Define este componente como visível.
Retorna a representação em String deste
componente.
Exemplo
Component c= new Component();
Button
Os objetos da classe Button são objetos gráficos que simulam botões e
geram eventos quando são pressionados.
Hierarquia
java.lang.Object
|
+--java.awt.Component
|
+--java.awt.Button
Construtores
Construtor
Descrição
Button()
Button(String lbl)
Constrói um botão sem texto.
Constrói um botão com um texto.
Métodos mais usados
Método
Descrição
String getLabel()
void setLabel(String lbl)
Retorna o texto apresentado no botão.
Define o texto apresentado no botão como o
referenciado por lbl.
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
211
Exemplo
Button b= new Button(“ok”);
Label
Os objetos da classe Label servem para exibir textos que não serão
editados pelo usuário.
Hierarquia
java.lang.Object
|
+--java.awt.Component
|
+--java.awt.Label
Construtores
Construtor
Descrição
Label()
Label(String lbl)
Label(String lbl,
int alin)
Constrói um Label sem texto.
Constrói um Label com um texto.
Constrói um Label com um texto e com o alinhamento
especificado por alin. Os valores possíveis para o
alinhamento são Label.LEFT, Label.RIGHT, e
Label.CENTER.
Métodos mais usados
Método
Descrição
String getText()
void setText(String lbl)
int getAlignment()
void setAlignment(int alin)
Retorna o texto apresentado no Label.
Define o texto apresentado no Label como o
referenciado por lbl.
Retorna o alinhamento.
Estabelece o novo alinhamento, especificado
por alin. Os valores possíveis para o
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
212
alinhamento são Label.LEFT,
Label.RIGHT, e Label.CENTER.
List
O componente List permite que o programador crie uma lista de
elementos passíveis de rolamento. Dependendo de como a lista foi definida o
usuário pode selecionar um item ou múltiplo itens.
Hierarquia
java.lang.Object
|
+--java.awt.Component
|
+--java.awt.List
Construtores
Construtor
Descrição
List()
List(int linhas)
List(int linhas,
boolean sm)
Constrói uma nova lista rolável.
Constrói uma nova lista rolável com o número de linhas
visíveis igual a linhas.
Constrói uma nova lista rolável com o número de linhas
visíveis igual a linhas e o parâmetro sm indicando se
é para permitir seleções múltiplas.
Métodos mais usados
Método
Descrição
void add(String item)
void add(String item,
int ind)
Adiciona o item referenciado por item no
final da lista.
Adiciona o item referenciado por item na
posição indicada por ind. A base é 0. Se o
valor de ind for menor que 0 ou maior que o
número de elementos, o item será inserido no
final da lista.
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
void deselect(int ind)
String getItem(int ind)
int getItemCount()
String[] getItems()
int getRows()
int getSelectedIndex()
int[] getSelectedIndexes()
String getSelectedItem()
String[] getSelectedItems()
boolean isMultipleMode()
void makeVisible(int ind)
void remove(int ind)
void removeAll()
void select(int ind)
213
remove a seleção do item no índice
especificado.
Obtém o item no índice especificado.
Obtém o número de itens na lista.
Obtém todos os itens da lista.
Obtém o número de linhas visíveis da lista.
Obtém o índice do elemento selecionado.
Retorna -1 se nenhum elemento ou mais de um
elemento estiver selecionado.
Retorna todos os itens selecionados.
Obtém o elemento selecionado.
Obtém todos os elementos selecionados.
Indica se o modo de seleção é múltiplo.
Torna visível o elemento no índice
especificado.
Remove o elemento no índice especificado.
Remove todos os elementos na lista.
Seleciona o elemento no índice.
Exemplo
List lista = new List(3);
lista.add("Laranja");
lista.add("Abacate");
lista.add("Pera");
lista.add("Uva");
TextField
O componente TextField é usado para implementar entrada e edição
de uma linha de texto.
Hierarquia
java.lang.Object
|
+--java.awt.Component
|
+--java.awt.TextComponent
|
+--java.awt.TextField
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
214
Construtores
Construtor
Descrição
TextField()
TextField(int cols)
TextField(String txt)
TextField(String
txt,int cols)
Constrói um novo TextField.
Constrói um novo TextField com o número de
colunas especificado.
Constrói um novo TextField exibindo o texto
especificado.
Constrói um novo TextField com o número de
colunas e o texto especificados.
Métodos mais usados
Método
Descrição
int getColumns()
String getText()
void setColumns(int cols)
void setText(String txt)
Retorna o número de colunas.
Retorna o texto exibido no componente.
Define o número de colunas.
Define o número o texto a ser editado.
Exemplo
TextField tf;
tf = new TextField("Ola mundo!";
TextArea
O componente TextArea é usado para implementar entrada e edição
de texto com múltiplas linhas.
Hierarquia
java.lang.Object
|
+--java.awt.Component
|
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
215
+--java.awt.TextComponent
|
+--java.awt.TextArea
Construtores
Construtor
Descrição
TextArea()
TextArea(int lin,
int cols)
TextArea(String txt)
Constrói um novo TextArea.
Constrói um novo TextArea com o número de linhas
e colunas especificados.
Constrói um novo TextArea exibindo o texto
especificado.
TextArea(String txt,int Constrói um novo TextArea com o número de linhas,
cols)
colunas e o texto especificados.
Métodos mais usados
Método
Descrição
int getColumns()
int getRows()
String getText()
void setColumns(int cols)
void setRows(int lin)
void setText(String txt)
Retorna o número de colunas.
Retorna o número de linhas.
Retorna o texto exibido no componente.
Define o número de colunas.
Define o número de linhas.
Define o número o texto a ser editado.
Exemplo
new TextArea("Ola mundo!", 3, 20);
Containers
Containers são componentes usados para conter outros componentes. Um
objeto da classe Container pode conter inclusive outros containers,
permitindo assim a construção de interfaces complexas.
Hierarquia
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
216
java.lang.Object
|
+--java.awt.Component
|
+--java.awt.Container
Construtor
Construtor
Descrição
Container()
Constrói um novo Container.
Métodos mais usados
Método
Descrição
Component add(
Component comp)
Component add(
Component comp, int ind)
Component findComponentAt(
int x, int y)
float getAlignmentX()
float getAlignmentY()
Component getComponent(
int n)
Component getComponentAt(
int x, int y)
int getComponentCount()
Component[] getComponents()
Insets getInsets()
LayoutManager getLayout()
boolean
isAncestorOf(Component c)
void list(PrintStream out,
int indent)
void paint(Graphics g)
Adiciona o componente no final da lista de
componentes deste Container.
Adiciona o componente na posição ind da
lista de componentes deste Container.
Retorna o componente visível do Container
que contém a coordenada especificada. Todos
os descendentes são examinados (SDK 1.2).
Retorna o alinhamento no eixo x.
Retorna o alinhamento no eixo y.
Retorna o componente na posição n na lista de
componentes deste Container.
Retorna o componente do Container que
contém a coordenada especificada. Apenas o
primeiro nível de descendência é examinado.
Retorna o número de componentes deste
Container.
Retorna todos os componentes deste
Container.
Retorna o Insets deste Container.
Insets indicam o tamanho da borda do
Container.
Retorna o Layout deste Container.
Indica se o componente está contido neste
Container.
Imprime uma lista com o conteúdo deste
Container.
Pinta o este Container. Este método deve
ser sobrescrito se o programador deseja
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
void remove(Component comp)
void remove(int index)
void removeAll()
void setFont(Font f)
void setLayout(LayoutManager
mgr)
217
desenhar no Container. Neste caso
super.paint(g) deve ser invocado.
Remove o componente deste Container.
Remove o componente com o índice
especificado deste Container.
Remove todos os componentes deste
Container.
Define o fonte deste Container.
Define o layout deste Container.
Panel
Esta classe é o Container mais simples. O Panel fornece um espaço
onde podem ser colocados outros componentes, inclusive outros objetos do tipo
Panel.
Hierarquia
java.lang.Object
|
+--java.awt.Component
|
+--java.awt.Container
|
+--java.awt.Panel
Construtor
Construtor
Descrição
Panel()
Panel(LayoutManager layout)
Constrói um novo Panel.
Constrói um novo Panel usando o LayoutManager
especificado.
Método
Método
Descrição
void addNotify()
Cria um par (peer) para o Panel. O par permite modificar o
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
218
Panel sem alterar a sua aparência.
Exemplo
Ver exemplo VI.XX.
Frame
Um Frame é uma janela com título e borda. A área do Frame inclui a
área destinada para a borda. As dimensões da borda pode ser obtida por meio do
método getInsets().
Hierarquia
java.lang.Object
|
+--java.awt.Component
|
+--java.awt.Container
|
+--java.awt.Window
|
+--java.awt.Frame
Construtores
Construtor
Descrição
Frame()
Frame(String title)
Constrói um novo Frame.
Constrói um novo Frame com o título especificado.
Métodos mais usados
Método
Descrição
static Frame[] getFrames()
Image getIconImage()
Retorna todos os frames criados pela aplicação.
Retorna a imagem usada pelo Frame quando
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
MenuBar getMenuBar()
int getState()
String getTitle()
boolean isResizable()
void setIconImage(Image im)
void setMenuBar(MenuBar mb)
void setResizable(boolean r)
void setState(int state)
void setTitle(String title)
219
minimizado.
Retorna a barra de menu deste Frame.
Retorna o estado deste Frame.
Retorna o título deste Frame.
Indica se o tamanho deste Frame pode ser
mudado.
Define a imagem a ser usada como ícone para
este Frame.
Define o menu a ser usada como ícone para
este Frame.
Define se o tamanho deste Frame pode ser
mudado.
Define o estado deste Frame.
Define o título deste Frame.
Exemplo
Ver exemplo VI.XX.
###
Exemplo
import java.awt.*;
import java.awt.event.*;
class testeFrame3 extends FrameX
{
Button b;
class SymMouse extends MouseAdapter
{
public void mouseClicked(MouseEvent event)
{
Object object = event.getSource();
if (object == b) b.setLabel("Clik");
}
}
public testeFrame3 (String Titulo)
{
super(Titulo);
b = new Button("ola");
b.addMouseListener(new SymMouse());
this.add(b);
setSize(100,80);
}
public static void main(String args[])
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
220
{
testeFrame3 t =
t.show();
new testeFrame3("Teste 3");
}
}
public class testeFrame4 extends FrameX {
public testeFrame4(String titulo) {
super(titulo);
setLayout(new GridLayout(1,2));
textField1 = new TextField(20);
add(textField1);
button1 = new Button("Duplica");
add(button1);
button1.addMouseListener(new SymMouse());
}
public synchronized void show()
move(50, 50);
setSize(200,50);
super.show();
}
{
TextField textField1;
Button button1;
class SymMouse extends java.awt.event.MouseAdapter {
public void mouseClicked(java.awt.event.MouseEvent
event){
Object object = event.getSource();
if (object == button1) {
int i = Integer.parseInt(textField1.getText());
textField1.setText(""+i*2);
}
}
}
public static void main(String args[]){
new testeFrame4("Teste 4").show();
}
}
public class testeFrame5 extends FrameX {
public testeFrame5(String titulo) {
super(titulo);
setSize(220,70);
setLayout(null);
textField1 = new TextField();
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
221
textField1.setBounds(10,30,100,25);
add(textField1);
button1 = new Button("Duplica");
button1.setBounds(110,30,100,25);
button1.addMouseListener(new SymMouse());
add(button1);
}
public class testeFrame6 extends FrameX {
public testeFrame6(String titulo) {
super(titulo);
setSize(200,250);
move(50, 50);
theImage = tk.getImage("skisor.gif");
}
private static Toolkit tk = Toolkit.getDefaultToolkit();
private Image theImage;
public void paint(Graphics g) {
g.drawImage(theImage, 20, 20, getSize().width-20,
getSize().height-20, this);
}
public static void main(String args[]){
new testeFrame6("Teste 6").show();
}
}
O objeto Toolkit oferece recursos para carregar imagens da forma
correta para cada plataforma. O método paint() é responsável por desenhar a
figura. Ele é chamado toda vez que ocorre um evento sobre o componente, de
modo que a figura será sempre redesenhada.
Agenda Eletrônica versão Gráfica 1.0
/**
AgendaInt
Interface Gráfica da agenda.
*/
import
import
import
import
java.awt.*;
java.awt.event.*;
java.io.*;
java.util.*;
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
public class AgendaInt extends FrameX
{
Agenda ag;
TextArea
TextField
TextField
TextField
Button
Button
Button
Button
Button
Label
Label
Label
Label
textArea1
txtNome
txtEnd
txtTel
btCarregar
btGravar
brInserir
btListar
btConsultar
label1
lbStatus
label2
label3
=
=
=
=
=
=
=
=
=
=
=
=
=
new
new
new
new
new
new
new
new
new
new
new
new
new
TextArea();
TextField();
TextField();
TextField();
Button();
Button();
Button();
Button();
Button();
Label();
Label();
Label();
Label();
public AgendaInt()
{
ag = new Agenda();
setLayout(null);
setBackground(new Color(112,170,192));
setSize(400,320);
setVisible(false);
add(textArea1);
textArea1.setBackground(Color.white);
textArea1.setBounds(12,120,372,168);
btCarregar.setLabel("Carregar");
add(btCarregar);
btCarregar.setBackground(Color.lightGray);
btCarregar.setBounds(12,12,72,24);
btGravar.setLabel("Gravar");
add(btGravar);
btGravar.setBackground(Color.lightGray);
btGravar.setBounds(84,12,72,24);
brInserir.setLabel("Inserir");
add(brInserir);
brInserir.setBackground(Color.lightGray);
brInserir.setBounds(156,12,72,24);
btListar.setLabel("Listar");
add(btListar);
btListar.setBackground(Color.lightGray);
btListar.setBounds(228,12,84,24);
btConsultar.setLabel("Consultar");
add(btConsultar);
btConsultar.setBackground(Color.lightGray);
btConsultar.setBounds(312,12,72,24);
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
222
Java na Prática
add(txtNome);
txtNome.setBackground(Color.white);
txtNome.setBounds(60,48,144,24);
label1.setText("Nome:");
add(label1);
label1.setFont(new Font("Serif", Font.BOLD, 12));
label1.setBounds(12,48,48,24);
add(lbStatus);
lbStatus.setBackground(Color.lightGray);
lbStatus.setForeground(Color.red);
lbStatus.setBounds(0,300,396,16);
label2.setText("Telefone:");
add(label2);
label2.setFont(new Font("Serif", Font.BOLD, 12));
label2.setBounds(216,48,60,24);
add(txtTel);
txtTel.setBackground(Color.white);
txtTel.setBounds(276,48,108,24);
label3.setText("Endereço:");
add(label3);
label3.setFont(new Font("Serif", Font.BOLD, 12));
label3.setBounds(12,84,48,24);
add(txtEnd);
txtEnd.setBackground(Color.white);
txtEnd.setBounds(72,84,312,24);
setTitle("Agenda Eletônica");
// REGISTRA OS LISTENERS
SymWindow aSymWindow = new SymWindow();
this.addWindowListener(aSymWindow);
SymMouse aSymMouse = new SymMouse();
btGravar.addMouseListener(aSymMouse);
btCarregar.addMouseListener(aSymMouse);
btListar.addMouseListener(aSymMouse);
brInserir.addMouseListener(aSymMouse);
btConsultar.addMouseListener(aSymMouse);
}
public AgendaInt(String title)
{
this();
setTitle(title);
}
class SymWindow extends WindowAdapter
{
public void windowClosing(WindowEvent event)
{
Object object = event.getSource();
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
223
Java na Prática
if (object == AgendaInt.this)
AgendaIntAwt_WindowClosing(event);
}
}
void AgendaIntAwt_WindowClosing(WindowEvent event)
{
setVisible(false);
}
class SymMouse extends MouseAdapter
{
public void mouseClicked(MouseEvent event)
{
Object object = event.getSource();
if (object == btGravar)
btGravar_MouseClicked(event);
else if (object == btCarregar)
btCarregar_MouseClicked(event);
else if (object == btListar)
btListar_MouseClicked(event);
else if (object == btConsultar)
btConsultar_MouseClicked(event);
else if (object == brInserir)
brInserir_MouseClicked(event);
}
}
void btGravar_MouseClicked(MouseEvent event)
{
gravar();
}
void btCarregar_MouseClicked(MouseEvent event)
{
carregar();
}
void btListar_MouseClicked(MouseEvent event)
{
Exibirlista();
}
void btConsultar_MouseClicked(MouseEvent event)
{
String nome = txtNome.getText();
if (nome.length()>0) exibirPessoa(nome);
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
224
Java na Prática
else lbStatus.setText("É necessário preencher o campo
Nome!");
}
void brInserir_MouseClicked(MouseEvent event)
{
String nome = txtNome.getText();
String tel = txtTel.getText();
String end = txtEnd.getText();
if (nome.length()>0) ag.inserir(new Pessoa(nome
,tel,end));
else lbStatus.setText("É necessário preencher o campo
Nome!");
}
/**
gravar
*/
public void gravar()
{
ObjectOutputStream fout;
try {
fout = new ObjectOutputStream(
new FileOutputStream("agenda.dat"));
fout.writeObject(ag);
fout.close();
} catch(FileNotFoundException e)
{ System.out.println("Arq. Não encontrado");}
catch(IOException e){System.out.println("Erro na
gravação!");}
}
/**
carregar
*/
public void carregar()
{
ObjectInputStream fin;
try {
fin = new ObjectInputStream(
new FileInputStream("agenda.dat"));
ag = (Agenda) fin.readObject();
fin.close();
} catch(FileNotFoundException e)
{ System.out.println("Arq. Não encontrado");}
catch(Exception e) {System.out.println("Erro na
leitura!");}
}
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
225
Java na Prática
226
/**
Exibirlista
*/
public void Exibirlista()
{
Pessoa p;
textArea1.setText("");
for (Enumeration e = ag.getPessoas();
e.hasMoreElements();)
{
p = (Pessoa) e.nextElement();
textArea1.append("\nNome:"+p.getNome()+"\nTelefone:"
+p.getTel()+"\nEndereço:"+p.getEnd()+"\n");
}
}
// main
public static void main(String args[])
{
(new AgendaIntAwt("Agenda")).setVisible(true);
}
/**
exibirPessoa
*/
public void exibirPessoa(String nome)
{
Pessoa p = ag.getPessoa(nome);
if (p!=null)
{
txtTel.setText(p.getTel());
txtEnd.setText(p.getEnd());
}
else textArea1.setText("Pessoa não cadastrada!");
}
}
Exercícios
Faça um programa para converter Graus Celcius em Farenheit
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
227
Capítulo IX - Applets
Applets são aplicações Java projetadas para serem executadas por
navegadores da World Wide Web (WWW). Isto é feito através de chamadas
especiais dentro do código HTML (Hyper Text Markup Language) das páginas.
A possibilidade de desenvolver páginas HTML com a capacidade de invocar
programas em Java foi uma das razões para o sucesso inicial, inédito em termos
de linguagem de programação, de Java. Até então, as páginas HTML eram
basicamente objetos passivos, com capacidade limitada de interação, a não ser
por meio de CGI (Common Gateway Interface), que implementa a interatividade
com o uso de códigos executáveis (geralmente escritos em Perl) rodados no
servidor. Esse tipo de arquitetura onera muito o servidor uma vez qualquer
processamento tem que ser efetuado nele.
Servidor
Cliente com browser
Requisição de página Web
Página Web construída
pelo programa CGI
800
O servidor Web
executa o programa
CGI que constrói a
página Web.
APO
Figura VII.1 – Interação via CGI.
A idéia do uso de Java é enviar código para o lado cliente através da
rede, para que parte do processamento seja realizado no próprio cliente,
liberando o servidor para tarefas mais importantes. Claro que isto será razoável
se o código puder trafegar pela rede rapidamente. Os programas em Bytecodes
ocupam pouco espaço e podem ser compactados antes de serem enviados pela
rede, o que reduz bastante o tempo de transmissão.
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
Cliente com browser
Executa o
código em
bytecodes
228
Servidor
O servidor Web
envia página
Web e código
em bytecodes
Requisição de página Web
800
Página Web e bytecodes
APO
Figura VII.2 – Interação via Java.
No entanto, é preciso ter cuidado quando se trata de enviar código pela
rede para se executado em uma máquina cliente. Existe um grande potencial
para o desenvolvimento acidental ou proposital de programas que podem atuar
nocivamente nas máquinas clientes, seja pela leitura de dados privados, seja por
destruição não autorizada de informações e programas. Preocupados com o isso,
os projetistas da linguagem Java determinaram que, no caso de Applets, a
máquina virtual impede que o programa tenha acesso limitado aos recursos da
máquina. Nessa forma de execução o programa é dito estar sendo executado
dentro de uma “caixa de areia” (Sand Box). Esta solução é em alguns casos por
demais restritiva e tem sido relaxada nas últimas versões de Java para permitir o
acesso aos recursos das máquinas clientes a Applets digitalmente assinados,
denominados de Trusted (confiáveis).
Outro benefício do uso de Java é possibilidade da produção de páginas
para a WWW com animação. Inicialmente, as páginas em HTML não possuíam
recurso para animação, a não ser por meio de GIFs animadas. Com Java é
possível desenvolver animações bem mais sofisticadas.
Para desenvolver um Applet é preciso criar uma subclasse da classe classe
java.applet.Applet. Abaixo é mostrada forma geral de um código que
implementa um Applet:
import java.awt.*;
import java.applet.Applet;
public class indentificador extends Applet
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
229
{
corpo do applet
}
A figura VII.3 mostra a hierarquia de classes para a classe Applet.
java.lang.Object
java.awt.Component
java.awt.Container
java.awt.Panel
java.applet.Applet
Figura VII.3 – Hierarquia de classes.
O exemplo VII.1 mostra o código de um Applet que exibe o texto “ola
mundo!” quando executado.
import java.awt.*;
import java.applet.*;
public class Applet1 extends Applet
{
Label label1 = new Label();
public void init()
{
setLayout(null);
setSize(150,87);
label1.setText("Ola Mundo!");
add(label1);
label1.setBounds(24,12,84,48);
}
}
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
230
Exemplo VII.1 – Applet simples.
O leitor pode observar algumas diferenças do código do exemplo VII.1
para o código do exemplo III.1 que implementava um programa que exibia “ola
Mundo” na console. A primeira diferença é que não existe um método main().
Isto ocorre porque os Applets foram projetados para serem executados por outra
aplicação: um navegador Web. A aplicação é responsável por criar uma
instância da subclasse da classe Applet e chamar os métodos de acordo com a
necessidade. No exemplo acima será executado o método init(), que é o
primeiro método executado em um Applet e é executado apenas uma vez.
Para executarmos o Applet, além de ser necessário compilá-lo, é preciso
criar um arquivo com código HTML com um link para o código em bytecode.
Isto é necessário já que o Applet vai ser executado por um navegador. O
exemplo VII.2 mostra o código HTML que possui um link para a classe Applet1.
O link para o código em bytecodes pode possuir vários parâmetros. No exemplo
VII.2 são mostrados apenas os parâmetros que indicam a largura e altura que o
Applet ocupará na página. Os outros parâmetros serão abordados mais adiante.
<html>
<applet code=Applet1.class
width=400
height=300>
</applet>
</html>
Exemplo VII.2 – HTML simples.
Para executar o código basta direcionar o navegador para o HTML do
exemplo VII.2. O SDK possui também uma aplicação capaz de executar
Applets. É o appletviewer. A linha de comando abaixo mostra como
executar o Applet por meio do appletviewer. Note que é preciso passar o
nome do arquivo HTML como parâmetro:
appletviewer applet1.html
A figura VII.4 mostra o resultado da execução do Applet1 por meio do
appletviewer.
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
231
Figura VII.4 – Execução do Applet1.
Descrição do código HTML
Não entraremos em detalhes da codificação em HTML. Abordaremos
aqui apenas os parâmetros relacionados com o link para o código em bytecode.
A forma geral de um link deste tipo é mostrada abaixo.
<html>
<applet..code= ... width=... height=...
archive=...
codebase=...
align=...
alt=...
name=...
hspace=...
vspace=...
mayscript=... >
<param name=“...” value=“...”>
...
<param name=“...” value=“...”>
</applet>
</html>
O link HTML possui vários parâmetros, sendo a maioria deles opcionais.
A tabela abaixo descreve cada um dos parâmetros.
Parâmetro
Descrição
code
width
height
archive
Nome da classe em bytecode. Por exemplo: Applet1.class.
Largura que o Applet vai ocupar na página HTML.
Altura que o Applet vai ocupar na página HTML.
Opcional. Se a classe estiver armazenada em um arquivo JAR (Java
Archive), então este parâmetro indica o nome do arquivo. Por
exemplo: minhasclasses.jar.
Opcional. URL onde está localizada o Applet.
Alinhamento do Applet na página. (left, right, top,
texttop, middle, absmiddle, baseline, bottom e
absbottom).
Mensagem alternativa caso o navegador não seja capaz de executar
bytecodes.
Nome do Applet. Pode ser usado por outros Applets na mesma
página para comunicação.
codebase
align
alt
name
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
hspace
vspace
mayscript
param
232
Define o número de pixels livres nos lados direito e esquerdo do
Applet.
Define o número de pixels livres acima e abaixo do Applet.
Valor booleano que indica se um Applet pode interagir com código
Javascript.
Opcional. É usado para passar parâmetros para o Applet. Cada
entrada param possui dois subparâmetros, name e value,
indicando o nome do parâmetro e o valor respectivamente. Mais
adiante será mostrado como o código Java recebe esses parâmetros.
Tabela VII.1 – Parâmetros do link para o código em bytecodes.
Métodos da Classe Applet
A classe Applet define um número razoável de métodos. No entanto, na
maioria das aplicações, o programador sobrescreverá um pequeno subconjunto
deste métodos. A tabela VII.2 descreve os métodos mais utilizados na
implementação de Applets.
Método
Descrição
init
É o primeiro método executado e é executado apenas uma vez. É utilizado
para as inicializações do Applet.
É executado toda vez que o Applet aparece no navegador. É utilizado para
iniciar a operação normal do Applet.
É executado toda vez que o Applet passa a não ser exibido pelo navegador.
É usado para terminar operações caras em termos computacionais.
É executado quando o navegador não precisa mais do Applet. É usado para
liberar recursos.
É executado toda vez que o Applet aparece no navegador. Recebe uma
instância da classe Graphics. O usuário pode solicitar a sua invocação por
meio de uma chamada ao método repaint(). Neste caso o método
paint() não será chamado imediatamente, e sim escalado para ser
executado em um tempo menor do que 100msec.
start
stop
destroy
paint
Tabela VII.2 –Principais métodos da classe Applet.
Esses métodos são invocados pelo navegador conforme as situações descritas
acima. A motivação por trás dos métodos start() e stop() é que não faz
sentido desperdiçar ciclos de processador com um Applet que não está sendo
visto.
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
233
O método que talvez possa causar mais dúvidas quanto a sua utilidade é o
método paint(). O método paint() é na verdade um método herdado da
classe container e é utilizado para desenhar em um objeto. O objeto da classe
Graphics, recebido pelo método, representa a superfície onde podem ser
feitos os desenhos e possui um conjunto de métodos para desenhar. Para ilustrar
o uso do paint() o exemplo VII.3 mostra o código de um Applet que desenha
um quadrado na tela que segue o mouse.
import java.awt.*;
import java.applet.*;
import java.awt.event.*;
public class Applet2 extends Applet {
int x=0, y=0;
public void init(){
setLayout(null);
setSize(426,266);
this.addMouseMotionListener(new SymMouseMotion());
}
public void paint(Graphics g) {
if (x>0)g.drawRect(x, y, 30, 30);
}
class SymMouseMotion extends MouseMotionAdapter{
public void mouseMoved(MouseEvent event){
if (event.getSource()== Applet2.this) {
x = event.getX();
y = event.getY();
repaint();
}
}
}
}
Exemplo VII.3 – Applet que desenha um quadrado que acompanha o mouse.
Note que o método paint() não é chamado diretamente pelo método
mouseMoved(). O que ocorre é que o método repaint() solicita a
máquina virtual para chamar o método update() que por sua vez chama o
método paint(). A máquina virtual não executa imediatamente o método
update(). Ela escala o método update() para ser executado em pelo menos
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
234
100 milisegundos. Se antes deste período ocorrer outra solicitação ela será
ignorada. O diagrama da figura VII.5 ilustra o ciclo repaint-update-paint.
repaint()
paint(Graphics g)
update(Graphics g)
Chama
escala (dentro de 100 ms)
Figura VII.5 – Ciclo repaint-update-paint.
Ao executar o Applet do exemplo VII.3 o leitor notará que apenas um
quadrado permanece desenhado, apesar de existir código apenas para desenhar e
nenhum para apagar. A pergunta é: o que ocorre com os quadrados desenhados
anteriormente? Acontece que o método update() sempre apaga os desenhos
que feitos anteriormente desenhando um retângulo com o tamanho da área de
desenho preenchido com a cor de fundo. É possível sobrescrever o método
update() para alterar esse comportamento, como veremos no capítulo sobre
animação, no entanto é necessário sempre repintar todos os desenhos que
precisam permanecer a cada ciclo, até por que eles podem ser afetados pela
movimentação ou encobrimento da área de desenho. No exemplo VII.3
utilizamos um método do objeto da classe Graphics para desenhar um quadrado.
Essa classe possui vários métodos semelhantes para desenho de figuras e texto.
A tabela VII.3 descreve alguns métodos desta classe.
Método
Descrição
drawArc
drawChars
drawline
drawRect
drawRoundRect
drawImage
draw3Drect
drawOval
drawPolygon
drawString
fillRect
fillRoundRect
fill3Drect
fillOval
fillPolygon
getColor
setColor
getFont
desenha um arco elíptico.
desenha o texto especificado por um array de bytes
desenha uma linha entre dois pontos.
desenha um retângulo.
desenha um retângulo com bordas arredondadas.
desenha um imagem
desenha um retângulo 3D.
desenha um oval.
desenha um polígono usando um vetor de pontos.
desenha um string.
desenha um retângulo preenchido.
desenha um retângulo com bordas arredondadas preenchido.
desenha um retângulo 3D preenchido.
desenha um oval preenchido.
desenha um polígono usando um vetor de pontos preenchido.
obtém a cor corrente.
define a cor corrente.
obtém a fonte corrente.
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
setFont
235
define a fonte corrente.
Tabela VII.3 –Principais métodos da classe Graphics.
O exemplo VII.4 mostra os uso de alguns métodos da classe Graphics. O
programa, ao ser executado, mostra uma figura com um texto ao usuário e cada
vez que o usuário pressiona o mouse a figura muda.
import java.awt.*;
import java.awt.event.*;
import java.applet.*;
public class Applet3 extends Applet
{
private int i =0;
private static final int x[] = {10,100,105,80,20};
private static final int y[] = {10,15,70,40,65};
public void init()
{
setLayout(null);
setSize(200,150);
this.addMouseListener(new SymMouse());
}
public void paint(Graphics g)
{
switch(i)
{
case 0: g.setColor(Color.green);
g.fillRect(10,10,100,100);
g.setColor(Color.black);
g.drawString("Quadrado",30,50);
break;
case 1: g.setColor(Color.red);
g.fillOval(10,10,100,70);
g.setColor(Color.black);
g.drawString("Oval",50,50);
break;
case 2: g.setColor(Color.blue);
g.fillPolygon(x,y,5);
g.setColor(Color.black);
g.drawString("Poligono",40,30);
break;
}
i = (i+1)%3;
}
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
236
class SymMouse extends MouseAdapter
{
public void mouseClicked(MouseEvent event)
{
if (event.getSource()== Applet3.this) repaint();
}
}
}
Exemplo VII.4 – Applet que desenha várias figuras na tela.
Exibindo uma imagem
Um dos métodos mais interessantes desta classe Graphics é o método
drawImage(), que permite exibir uma imagem. O exemplo VII.5 contém o
código de um applet que mostra uma imagem que muda quando o mouse é
pressionado. A imagem é carregada no exemplo está codificada no formato jpeg.
Os tipos de imagens suportadas dependem do navegador sendo utilizado. A
figura VII.6 mostra o resultado da execução do Applet.
import java.awt.*;
import java.awt.event.*;
import java.applet.*;
public class Applet4 extends Applet
{
Image img[];
private static int i=0;
public void init()
{
img = new Image[2];
img[0] = getImage(getDocumentBase(),"baleia1.jpg");
img[1] = getImage(getDocumentBase(),"baleia2.jpg");
this.addMouseListener(new SymMouse());
}
public void paint(Graphics g)
{
g.drawImage(img[i],0,0,this);
i=i==0?1:0;
}
class SymMouse extends MouseAdapter
{
public void mouseClicked(MouseEvent event)
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
237
{
if (event.getSource() == Applet4.this) repaint();
}
}
}
Exemplo VII.5 – Applet que exibe imagens.
Primeiramente foi necessário obter objetos to tipo Image. Para isso
utilizamos o método getImage() que possui dois parâmetros. O primeiro é
uma URL que indica a localização do arquivo contendo a figura e o segundo é o
nome do arquivo da figura, que pode conter informação de diretório relativo à
URL. Se o arquivo estiver no mesmo diretório de onde foi carregado o arquivo
HTML então basta usar a método getDocumentBase(), como no exemplo
VII.5. Se o Se o arquivo estiver no mesmo diretório de onde foi carregado o
Applet então basta usar o método getCodeBase().
Figura VII.6 – Saídas do Applet4.
O método getImage() retorna null se o arquivo não foi encontrado,
caso contrário um objeto do tipo Image é retornado. A carga de uma imagem
pode ser muito demorada, dependendo do tamanho é formato de
armazenamento, principalmente em se tratando de imagens transmitidas através
de uma rede com alto trafego de dados. Além disso, no caso de Applets, o
usuário pode mudar de página antes mesmo de uma imagem ser completamente
exibida. Por essas razões, o método getImage() foi projetado para retornar
imediatamente, antes mesmo da carga da imagem, de modo que a tarefa de
carregar a imagem é iniciada no momento em que a imagem é usada e é feita em
paralelo com a execução do programa por outra linha (Thread) de execução.
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
238
Threads são descritos em detalhes no capítulo VIII. Por ora basta visualizá-los
como um tipo de processo leve que compartilha o mesmo espaço de
endereçamento com outros Threads.
Uma vez de posse de um objeto tipo Image podemos exibi-lo por meio
do método:
Graphics.drawImage(Image img,int x,int y, ImageObserver O)
O primeiro parâmetro é o objeto tipo Image. Os segundo e terceiro
definem a posição dentro Applet. O quarto parâmetro do tipo
ImageObserver é o que necessita ser melhor descrito. O objeto passado
como quarto parâmetro deve implementar a interface ImageObserver, o que
o habilita a receber notificações sobre a carga da imagem. Como a classe
Component implementa esta interface, então qualquer subclasse da classe
Component também a implementa, portanto, podemos passar o próprio Applet
como parâmetro, usando a palavra chave this.
O método drawImage() desenha a imagem no estágio em que ela
estiver. Deste modo, se apenas metade da imagem tiver sido carregada então
apenas metade será mostrada. Em alguns casos o programador pode querer
exibir uma imagem apenas quando ela for completamente carregada. Isto pode
ser feito por meio de um objeto da classe MediaTracker. A classe
MediaTracker é usada para acompanhar o status de objetos de mídia. Um
objeto de mídia pode ser imagens ou sons, no entanto, no momento apenas
imagens são suportadas. O exemplo XX.XX mostra o uso de um objeto desta
classe para monitorar o status de uma imagem.
public void init() {
MediaTracker tr = new MediaTracker(this);
im = getImage(getDocumentBase(),”img.gif”);
tr.addImage(im,0);
try {
tr.waitForID(0);
}
catch (InterruptedException excep) {}
}
Exemplo XX.XX – Uso do MediaTracker.
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
239
No exemplo XX.XX é criado uma instância da classe MediaTracker e os
objetos que devem ser monitorados são adicionados à instância por meio do
método addImage(). Além da imagem também é atribuído um identificador
único que será usado para controlar a ordem de prioridade de carga do objeto.
Também pode ser usado para identificar subconjuntos imagens. As imagens com
número menor possuem maior prioridade no processo de carga em relação as
imagens com número maior. O início da carga da imagem é feita por meio do
método:
public void waitForID(int id) throws
InterruptedException
O identificador da imagem (ou do subconjunto de imagens) é passado por
parâmetro para o método. Este método espera até que todas as imagens com o
identificador sejam carregadas. Se ocorrer algum erro na carga a imagem é
considerada carregada. O programador deve usar os métodos isErrorAny()
e isErrorID() para detectar a ocorrência de erros.
Áudio
O versão JDK1.0 oferece suporte para execução de arquivos de áudio
apenas no formato AU. No entanto, o SDK1.2 estendeu este suporte para
arquivos de áudio no formato AIFF, WAV, MIDI (tipo 0 e 1) e RMF. Java pode
manipular dados de áudio em 8 e 16bits. A classe usada tratamento de áudio é a
AudioClip, que contém os seguintes métodos:
•
•
•
play() – Inicia a execução do audio clip.
loop() – Executa o audio clip em um loop.
stop() – Para a execução do audio clip.
Para se criar uma instância da classe AudioClip pode-se usar o método
getAudioClip() da classe Applet:
getAudioClip(URL url, String nome)
O método retorna imediatamente mesmo que o arquivo de áudio não
exista. O arquivo será carregado quando o programa tentar usá-lo. O Exemplo
VII.xx mostra um Applet que faz uso de um objeto AudioClip.
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
240
import java.lang.*;
import java.net.URL;
public class ExSom extends java.applet.Applet
{
AudioClip som;
public void init()
{
som = getAudioClip(getDocument(),”ola.au”);
}
public void start() {som.play();}
}
Exemplo VII.XX – Applet que executa um arquivo de audio.
Obtendo parâmetros
Na seção XX comentamos o formato do link HTML que faz referência a
um arquivo em bytecodes. Comentamos também a existência de marcadores no
formato
<param name=“...” value=“...”>
que são usados para passagem de parâmetros do HTML para o programa em
bytecodes. Nesta seção é mostrado o código Java necessário para capturar esses
parâmetros. De modo a facilitar a explicação faremos uso de um exemplo. O
exemplo VII.7 mostra o código HTML que passa dois valores e o exemplo VII.8
mostra um código em Java que recebe os parâmetros.
<HTML>
<applet code=“Applet5.class” width=400 height=250>
<param name=“numero” value=“5”>
<param name=“nome” value=“Ana”>
</APPLET>
</HTML>
Exemplo VII.7 – Código HTML passando valores.
import java.awt.*;
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
241
public class Applet5 extends java.applet.Applet
{
Label label1 = new Label();
public void init()
{
setLayout(null);
int i = Integer.parseInt(getParameter(“numero”));
setSize(150,87);
label1.setText(getParameter(“nome”)+ “:”+i);
add(label1);
label1.setBounds(24,12,84,48);
}
}
Exemplo VII.8 – Applet que recebe parâmetros.
Os valores são recebidos por meio do método getParameter() que
recebe como argumento o nome do parâmetro definido no código HTML. Os
valores são retornados como objetos do tipo String. Se não existir um parâmetro
com o nome passado para o método getParameter() o retorno será null.
O programador também pode definir o método getParameterInfo(),
cuja única ação é retornar um array de duas dimensões, contendo as informações
sobre os parâmetros. As informações armazenadas são o nome, tipo e descrição
de cada parâmetro. Este método pode ser usado pelos navegadores Web para
ajudar ao usuário a definir os valores a serem passados para o Applet.
public String[][] getParameterInfo()
{
String[][] info = {{“numero”,”int”,”number qualquer”},
{“nome”,”string”,”nome de alguem”}};
return info;
}
Exemplo VII.9 – Definindo informações sobre os parâmetros .
Executando um Applet como aplicação
Algumas vezes desejamos desenvolver um programa que possa funcionar
como aplicação e como Applet. Podemos atingir este objetivo criando um
método main() dentro da subclasse Applet que está sendo implementada. O
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
242
método main() deve criar uma instância da classe Frame e adicionar a ele um
objeto da subclasse do Applet. O exemplo VII.10 mostra como criar um método
main() para o Applet do exemplo VII.1.
import java.awt.*;
import java.applet.*;
public class Applet6 extends Applet
{
Label label1 = new Label();
public void init()
{
setLayout(null);
setSize(150,87);
label1.setText("Ola Mundo!");
add(label1);
label1.setBounds(24,12,84,48);
}
static public void main(String[] args)
{
FrameX frame = new FrameX("OLA");
Applet6 applet = new Applet6();
frame.add("Center", applet);
frame.setSize(200,100);
frame.setVisible(true);
applet.init();
applet.start();
}
}
Exemplo VII.10 – Applet com método main.
Preparando Applets para produção e arquivos JARs
Após o desenvolvimento e teste do Applet chega o momento de colocá-lo
em produção. Isto é feito colocando a página HTML que faz referência ao
arquivo em bytecodes, resultante da compilação do Applet, em um diretório
acessado por um servidor Web. Você já deve ter notado que cada classe Java
gera um arquivo em bytecodes. Deste modo, um arquivo contendo várias classes
em Java gerará, após a compilação vários arquivos de bytecodes, um para cada
classe. No caso de Applets com um tamanho razoável pode ser gerado um
grande número de arquivos. Isto pode ser um problema em se tratando de uma
aplicação que é projetada para ser transmitida em uma rede de longa distância
por meio do protocolo HTTP, já que cada classe irá gerar uma transação HTTP
quando for referenciada pela primeira vez. Além disso, apesar do código em
bytecodes ser menor do que o código gerado pela compilação da maioria dos
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
243
programas escritos em outras linguagens de programação seria importante
diminuir o máximo possível o tamanho dos arquivos, de modo a minimizar o
tráfego na rede. Como já foi visto, para solucionar esses e outros problemas foi
introduzido a partir da versão 1.1 da linguagem Java o formato de arquivo JAR
(Java Archive). O formato de arquivo JAR permite o agrupamento de vários
arquivos em um único arquivo. Além disso, o formato JAR permite a
compressão de dados de modo otimizar o armazenamento e a transmissão de
dados. O principais benefícios do uso do formato JAR são os seguintes:
•
•
•
Menor tempo de carga de Applets: Se todos os arquivos relacionados com
um Applet estão agrupados em um único arquivo JAR, apenas uma transação
HTTP será necessária para carregar o Applet. Este tempo será ainda menor
se os arquivos estiverem compactados.
Segurança: o conteúdo do arquivos JAR podem ser assinados digitalmente.
Deste modo, os usuários que reconhecerem a sua assinatura podem conceder
privilégios para acessar recursos que não estariam disponíveis, caso
contrário.
Portabilidade: a API para manipulação dos arquivos JAR é parte integrante
do núcleo da biblioteca de classes de Java, o que a torna independente de
plataforma.
Outros benefícios foram adicionados com o lançamento da versão 1.2 do
SDK, como por exemplo, informações sobre versão.
Para invocar um Applet inserido em um arquivo JAR utilize a palavra
chave archive no arquivo HTML. O exemplo VII.11 mostra como invocar um
Applet desta forma.
<applet code=meuApplet.class
archive=meuarquivo.jar
width=width height=height>
</applet>
Exemplo VII.10 – Applet com método main.
Exercícios
Faça uma calculadora com o seguinte layout:
9 8 7 +
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
6 5 4 3
2
1
*
0
.
=
/
Java na Prática
Agenda Eletrônica versão Applet 1.0
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
244
Java na Prática
245
Capítulo X JavaBean
O que é um JavaBean?
A API JavaBeans é a especificação que torna possível escrever
componentes de software na linguagem Java. Componentes são peças de
software, análogas a bloquinhos de montar, reutilizáveis, auto-contidas e que
podem ser usadas para comporem visualmente outros componentes, Applets,
aplicações e Servlets usando um ambiente de desenvolvimento que suporte a
especificação de JavaBeans. Componentes JavaBean são conhecidos como
Beans.
Diferente de outras ferramentas de programação, como Delphi ou VB, o
suporte ao desenvolvimento de componentes foi acrescentado, de forma
elegante, na linguagem Java sem alterá-la.
JavaBeans são classes Java comuns que seguem na sua construção uma
especificação que permite o seu uso visual em ambientes RAD.
JavaBeans foram projetados para trabalhar em conjunto com ferramentas
de desenvolvimento "JavaBeans-enabled".
JavaBeans e ferramentas RAD
As ferramentas de desenvolvimento expõem as características públicas do
Bean visualmente. Eles obtêm as características do Bean (propriedades, métodos
e eventos) em um processo conhecido como introspection. A introspection pode
ser feita de duas maneiras:
•
Através da utilização de Reflection para o obtenção dos métodos, e do uso
das convenções para determinação dos eventos e propriedades.
•
Por uso de uma class de informações denominada BeanInfo. Uma classe
BeanInfo implementa a interface BeanInfo. Uma classe BeanInfo
explicitamente lista todas as características do Bean que devem ser expostas
para o ambiente de desenvolvimento.
As tão faladas propriedades são características do Bean que podem ser
mudadas durante a programação. O ambiente de desenvolvimento faz a
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
246
introspeção em um Bean para descobrir as propriedade e expo-las para
manipulação.
Os Beans utilizam eventos para se comunicarem com outros Beans. Os
ambientes de desenvolvimento pode examinar os Beans e determinarem que
eventos um determinado Bean pode disparar (fire) ou que eventos ele pode
manipular (receive).
Os métodos das classes que compõem um Bean são métodos Java simples,
que podem ser chamados por qualquer outras classes.
JavaBean é um padrão desenhado para ser utilizado por um ambientes de
desenvolvimento RAD. E todas as suas características foram projetas para
permitir o uso fácil por programadores sem modificar a linguagem Java. Nos
próximos tópicos veremos as convenções de nomes e o funcionamento e
utilização dessa poderosa especificação.
Propriedades
Em JavaBeans propriedade é um atributo público simples que pode ser de
leitura/escrita, apenas leitura ou apenas escrita. Existem quatro tipos de
propriedades: simples, indexada, ligada (bound), e restrita (constrained).
Simples
Representa um valor simples (Ex.: int, float, Object, ...). Pode ser
definida com um par de métodos getXxx/setXxx, onde xxx é o nome da
propriedade. Se for definido os dois métodos para a propriedade, esta será de
leitura/escrita, caso seja definido apenas o método getXxx a propriedade será
apenas leitura, e finalmente se for definida apenas o método setXxx será apenas
escrita.
Quando uma propriedade for booleana, a convenção e modificada para
isXxx ou invés de getXxx (no desenho dessa característica resolveram aderir a
máxima “toda regra possui uma exceção”).
Abaixo temos o exemplo de uma classe que define uma propriedade:
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
247
Class Pessoa
{
private String nome;
private String telefone;
private String endereço;
public Pessoa(String n, String t, String e)
{
nome = n; telefone = t; endereço = e;
}
public void setNome(String n) { nome=n; }
public void setTelefone(String t) telefone = t; }
public void setEndereço(String e) { endereço = e; }
public String getNome() { return nome; }
public String getTelefone() { return telefone; }
public String getEndereço() { return endereço;
}
}
A classe acima define as propriedades simples: telefone, nome e
endereço, sendo todas as três propriedades de leitura/escrita.
Indexada
Representa um array de valores (ex.: int[], float[], Object[] , ...). Os
métodos getXxx/setXxx que definem a propriedade devem ter como parâmetro
um inteiro que será o índice de acesso ao array. A propriedade pode também
suportar getXxx/setXxx para todo o array.
O exemplo abaixo trás a definição parcial de uma classe agenda:
Public class Agenda
{
private Pessoa pessoas[];
.
.
.
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
248
public Pessoa[] getPessoas(){return pessoas;}
public void setPessoas(Pessoa[] i){
this.pessoas = i;
}
public Pessoa getPessoas(int indice){
return pessoas[indice];
}
public void setPessoas(int indice, Pessoa p){
pessoas[indice] = p;
}
.
.
.
}
Neste exemplo, definimos uma propriedade indexada pessoas, que pode
ser acessada tanto na forma de uma array completo, quanto nos seus elementos,
através dos seus índice.
Ligada (Bound)
Uma propriedade bound notifica outros objetos quando o seu valor e
alterado. Toda vez que o valor de uma propriedade bound é modificado, a
propriedade dispara um evento PropertyChange que contém o nome, o novo
e o velho valores da propriedade.
Class Pessoa {
private String nome;
private String telefone;
private String endereço;
Private PropertyChangeSupport changes =
New PropertyChangeSupport(this);
public Pessoa(String n, String t, String e)
{nome = n; telefone = t; endereço = e;}
public
void addPropertyChangeListener(PropertyChangeListener l){
changes.addPropertyChangeListener(l);
}
public
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
249
void removePropertyChangeListener(PropertyChangeListener l)
{
changes.removePropertyChangeListener(l);
}
public void setNome(String n) {
String nomevelho = nome;
Nome = n;
Changes.firePropertyChange(“nome”, nomevelho, nome);
}
public void setTelefone(String t) {
String telefonevelho = telefone; telefone = t;
Changes.firePropertyChange(“telefone”, telefonevelho,
telefone);
}
public void setEndereço(String e) {
String endereçovelho = endereço; endereço = e;
Changes.firePropertyChange(“endereço”, nomeendereço,
endereço);
}
public String getNome() { return nome; }
public String getTelefone() { return telefone; }
public String getEndereço() { return endereço;
}
}
A classe Pessoa já foi implementada anteriormente neste capítulo, as
modificações introduzidas tornaram as propriedades nome, telefone e endereço
ligadas, i. e., sempre que elas sofrerem alterações, os listener que implementam
PropertyChangeListener e estão registrados como ouvintes da nossa
instância da classe, serão notificados.
Além das alterações no corpo dos métodos setXxx, que adicionou o
código que dispara o evento PropertyChange, nosso Bean também ganhou
um novo objeto changes e dois métodos que cuidam do registro e exclusão de
listener.
Restringidas(Constrained)
Um objeto com propriedade restrita permite que outros objetos vetem a
alteração do valor dessa propriedade. listener de propriedades restritas podem
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
250
vetar uma mudança de valor da propriedade através de uma exceção
PropertyVetoException que é levantada. Geralmente, apesar de não ser
obrigatório, as propriedades restringidas também são ligadas.
class CartãoCrédito
{
private String número;
private PropertyChangeSupport changes =
new PropertyChangeSupport(this);
private VetoableChangeSupport vetos =
new VetoableChangesSupport(this);
public
addVetoableChangeListener(VetoableChangeListener l){
vetos.addVetoableChangeListener(l);
}
public
removeVetoableChangeListener(VetoableChangeListener l){
vetos.removeVetoableChangeListener(l);
}
.
.
.
public void setNúmero(String n) {
String numeroVelho = numero;
Vetos.fireVetoableChange(número,numeroVelho,n);
número = n;
changes.firePropertyChange(número,numeroVelho,numero);
}
.
.
.
}
No exemplo acima definimos parcialmente uma classe com o nome
CartãoCrédito, esta classe possui uma propriedade de nome número que é uma
propriedade restringida. Para implementamos esta característica, primeiro
introduzimos uma novo objeto, veto, que nos dá o suporte ao registro dos
listener que terão poder de veto sobre a propriedade.
Em seguida aparecem dois métodos que cuidam apropriadamente,
repassando para VetoableChangeSupport, do registro dos listener que
podem vetar alterações.
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
251
A parte mais interessante fica por conta do método setNúmero, ele é muito
parecido com um método de uma propriedade ligada, mas acrescenta uma linha
antes de alterar o valor da variável número. Sendo assim, a alteração só será
realizada, se o evento disparado com fireVetoableChange não levantar
nenhuma exceção. Depois disso tudo, as coisa voltam a funcionar como em uma
propriedade ligada convencional.
Eventos
Sempre que estudamos algo, inicialmente obtemos uma grande
quantidade de informações e depois começamos a fazer generalizações
transformando o estudado em conhecimento. Bem, até agora vimos que
JavaBeans é um padrão que nos permite utilizar um ambiente de programação
RAD, e vimos algumas características de classes que seguem este padrão.
Mas além desse padrão definir como as classes interage com o ambiente
de programação, ele também define como objetos destas classes interagem entre
si. Aparece agora o conceito de eventos, que já vimos em capítulo anterior e
agora veremos com as convenções de nossa API de componentes.
Um bom lugar para começarmos é: o que é um evento? Para nossos
propósitos um evento é um objeto que encapsula dados a respeito de alguma
coisa que ocorreu. Por exemplo, o mouse foi movido, uma tecla foi digitada,
chegou um pacote UDP pela rede. Todas estas ocorrências podem ser modeladas
como eventos e informações sobre o que aconteceu podem ser inclusas no objeto
evento.
Só para relembrar, no novo modelo de eventos da biblioteca de classes
Java, um evento e “escutado” por classes que estão “capacitadas” para tratá-los,
estas classes são chamadas de EventListener, e são um mecanismo geral de
comunicação entre objetos sem a utilização de herança.
Nesta seção, devemos nos preocupar apenas com as convenções de
nomes de eventos e classes relacionadas para utilização na construção de Beans.
Suponhamos que nos desejamos construir um Bean de nome Timer. Nosso
evento teria o nome TimerEvent, e nosso Listener para este evento
TimerEventListener.
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
252
Na próxima seção veremos como juntar todos estes conceitos e o que
aprendemos ate agora em um exemplos de Bean completo um não-visual, um
Bean visual será mostrado no capítulo sobre MVC.
Um Bean para ser visual deve ser subclasse de java.awt.Component ou de
classes que herdem java.awt.Component.
Desenvolvimento do exemplo
O Bean de exemplo que estudaremos agora é um componente que dispara
um evento periodicamente em um intervalo de tempo definido pelo seu
utilizador, este componente e semelhante ao componente Timer do Delphi e ao
ActiveX Timer do VB. Suas aplicações incluem atualizar o display de um
relógio ou acionar o método que mostra o próximo frame de uma animação.
Nosso primeiro Bean será composto das seguintes classes:
TimerBean - A classe principal do Bean;
TimerEvent - A classe do evento disparado pelo Bean;
TimerEventListener - A interface “ouvinte” para o evento do Bean;
TimerEventListener
A classe TimerEventListener implementa a interface que será utilizada para que
o nosso Bean notifique seus listeners sobre cada evento.
Package timer;
public interface TimerEventListener extends
java.util.EventListener
{
void timerEventDisparado(TimerEvent te);
}
Todos os listeners para JavaBeans devem herdar java.util.EventListener
ou de alguma subclasse dela. O nome da interface deve ser XxxxListener, onde
Xxxx corresponde ao nome do evento que esta interface habilita a tratar. No
nosso exemplo temos o evento TimerEvent e consequentemente a interface se
chama TimerEventListener.
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
253
Uma interface listener pode declarar quantos métodos seu desenvolvedor
desejar. Normalmente estes métodos têm como parâmetro um objeto event. No
nosso caso declaramos somente um método.
TimerEvent
A classe TimerEvent implementa o evento que será passado para cada
TimerEventListener.
package timer;
public class TimerEvent extends java.util.EventObject
{
public TimerEvent(Object source)
{
super(source);
}
}
Todos eventos JavaBeans devem herdar java.util.EventObject e seu nome
deve obedecer ao padrão XxxxEvent, onde Xxxx é arbitrário. No nosso caso
ficamos com o nome TimerEvent.
Nosso construtor para a classe TimerListener tem como parâmetro um
objeto do tipo Object, e quando este construtor for chamado passaremos a
referência para o objeto que o esta chamando.
Eventos, por serem classes comuns, também podem carregar informações
adicionais, o que não é o nosso caso.
TimerBean
Esta é a nossa classe principal, e é onde realmente implementamos nosso
Bean.
package timer;
import java.util.*;
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
import java.beans.*;
public class TimerBean implements
java.io.Serializable, Runnable
{
protected long intervalo = 1000;
transient protected Thread th;
protected java.util.Vector listeners = new
java.util.Vector();
private boolean ativo = false;
private java.beans.PropertyChangeSupport mudancas = new
java.beans.PropertyChangeSupport(this);
public TimerBean()
{
super();
th = new Thread(this);
th.start();
}
public void addPropertyChangeListener(
PropertyChangeListener l)
{
mudancas.addPropertyChangeListener(l);
}
public void addTimerEventListener(TimerEventListener tl)
{
listeners.addElement(tl);
}
public void disparaEvento(TimerEvent e)
{
Vector v;
synchronized (this)
{
v = (Vector) listeners.clone();
}
for (int elem = 0; elem < v.size(); elem++)
{
((TimerEventListener)
v.elementAt(elem)).timerEventDisparado(e);
}
}
public long getIntervalo()
{
return intervalo;
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
254
Java na Prática
}
public synchronized void inicia()
{
ativo = true;
}
public synchronized void para()
{
ativo = false;
}
public void
removePropertyChangeListener(PropertyChangeListener l)
{
mudancas.removePropertyChangeListener(l);
}
public void removeTimerEventListener(TimerEventListener tl)
{
listeners.removeElement(tl);
}
public void run()
{
try
{
while (true)
{
th.sleep(intervalo);
if (ativo)
{
TimerEvent evento = new TimerEvent(this);
disparaEvento(evento);
}
}
}catch (Exception e) {e.printStackTrace(); }
}
public void setIntervalo(long interv)
{
long valorVelho = intervalo;
intervalo = interv;
mudancas.firePropertyChange("intervalo",
new Long(valorVelho), new Long(intervalo));
}
}
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
255
Java na Prática
256
Comecemos pelo princípio. Todos os Beans devem ser serializáveis, este
é o mecanismo que é normalmente utilizado para armazenar aplicações
JavaBeans. Para tornarmos nossa classe serializável declaramos que esta
implementa a interface java.io.Serializable e marcamos nossos campos que não
são serializáveis como transient ou providenciarmos métodos de serialização
customizados.
Nossa classe também implementa Runnable para podermos utilizá-la em
um thread. Em nossa classe possuímos os campos:
1. intervalo: que armazena de quanto em quanto tempo desejamos que um
evento seja disparado. Através dos métodos getIntervalo() e
setIntervalo(long) implementamos uma propriedade bound de nome
“intervalo”.
2. th: Este campo é transient, isto é, quando esta classes for serializada esta
informação não será gravada. Para nosso Bean th é o thread que
periodicamente dispara eventos para seus listeners.
3. listeners: Um Vector armazena a lista de listeners que estão registrados neste
Bean para receber os eventos. Novos métodos são colocados em nossa
classes para registrar ou remover listeners, este métodos tem nomes que
seguem o padrão JavaBeans e têm as formas addXxxx e removeXxxx,
onde Xxxx é o nome do tipo de listener que deve ser registrado ou removido.
addTimerEventListener
No
nosso
exemplo,
temos
e
removeTimerEventListener.
4. ativo: este campo é utilizado para indicar para o Bean se ele deve ou não
disparar eventos para os seus listeners. Ativo não é acessado diretamente, e
sim através dos métodos sincronizado para() e inicia().
Os métodos que merecem destaque são:
1. run(): método que implementa o thread, em nosso caso ele contém
um laço infinito e dentro do laço, depois de fazer nosso thread
“dormir”, com sleep, ele verifica se deve disparar um evento. Se deve
dispará-lo, um evento e criado e o método disparaEvento e chamado.
2. disparaEvento(TimerEvent e): inicialmente este método cria dentro
de uma seção sincronizado uma cópia do Vector listener. Em seguida
através de um laço for, percorre este Vector chamando o método
timerEventDisparado para cada listener registrado.
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
257
Temos agora um exemplo completo de um JavaBeans. Mas esta tecnologia tem
pouca ou nenhuma utilidade se não a utilizarmos com ferramentas de
programação visual como Visual Age, Visual Café, BDK e tantos outros.
Como o BDK é de graça, nós o utilizaremos para mostrar nosso Bean em
ação.
Instalando o Beans Development Kit (BDK)
O Beans Development Kit (BDK) é uma aplicação Java pura, portanto só
depende do SDK para rodar. O BDK possui suporte para a API JavaBeans e um
container de teste (BeanBox), que manipula Beans visualmente.
Para instalar o BDK basta ter na maquina o SDK instalado (acima do
1.1.4), entrar na pagina http://www.javasoft.com
e fazer download e
descompactá-lo. Pronto, e só rodar. Acompanhando o BDK temos também
documentação e JavaBeans com código fonte para serem explorados.
Testando exemplo no BDK
Para rodar o BDK utilizamos o arquivo run.bat ou run.sh, dependendo do
seu sistema operacional. Quando o aplicativo e inicializa sua área de trabalha
fica parecido com a figura abaixo. O BDK é composto de 3 janelas:
•
•
•
BeanBox: que é a janela onde os Beans são manipulados
visualmente;
ToolBox: uma lista de Beans que podem ser usados;
Properties: um janela que se altera dependendo do Bean que esta
selecionado no BeanBox. Ela mostra as propriedades do Bean
selecionado e através dela o desenvolvedor pode alterar estas
propriedade no Componente;
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
258
Com o BDK rodando nosso próximo
contendo o nosso Bean compilado.
passo e criar um arquivo jar
Para criarmos um jar contendo Beans, devemos informar para a
ferramenta na qual este Bean será utilizado, quais classes são Beans. Para isso
utilizamos o arquivo de manifesto do jar. Que ficaria assim para o nosso caso:
Nome do arquivo: manifest.mf
Manifest-Version: 1.0
Name: timer/TimerBean.class
Java-Bean: True
Name: timer/TimerEventListener.class
Name: timer/TimerEvent.class
Para criar o arquivo jar, dentro do diretório que contenha o diretório
timer, que é onde estão nossas classes compiladas, digite:
jar
cvfm timer.jar manifest.mf .
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
259
Com nosso jar criado temos agora que apresenta-lo ao BDK. Para isso
devemos utilizar a opção File->LoadJar do menu da Janela BeanBox.
Escolhemos então o arquivo jar que geramos, e o BDK irá carrega-lo. Após
carregar nosso Bean a lista de Beans do ToolBean será adicionada de um entrada
chamada TimerBean, como mostrado na figura abaixo:
Agora é tudo mais divertido, acabou a parte chata!!!
Vamos começar a colocar os Beans na janela BeanBox para criarmos
uma aplicação. Devemos colocar os seguintes Beans:
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
Bean
Ourbutton
Ourbutton
TimerBean
EventMonitor
Propriedade(mudada)
label
label
intervalo
260
Novo valor
Iniciar
Parar
2000
A posição dos Beans deve ser como na figura abaixo:
Depois de colocar todos os Beans no lugar e mudarmos suas
propriedades, devemos conectar os eventos nos métodos corretos de cada Bean.
Para ligar um evento disparado por um Bean a um método de outro, devemos
selecionar o primeiro e clicarmos em Edit->Events, escolhermos o evento e
clicarmos no Bean alvo. Um dialogo aparecerá com os métodos que podem ser
escolhidos para serem chamados.
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
261
Bean fonte
Evento
Bean alvo
Método
TimerBean
TimerEventtimerEventDisparado
action-actionPerformed
EventMonitor
TimerBean
InitiateEventSourceMonito
ring()
inicia()
action-actionPerformed
TimerBean
para()
OurButton
(iniciar)
OurButton
(parar)
A figura abaixo mostra parte da operação.
Depois de tudo ligado, para testar basta clicar no botão de iniciar, então
eventos serão disparados e aparecerão no Monitor de eventos. Depois clique no
botão parar parar para que os eventos deixem de ser disparados pela instância de
TimerBean . Uma opção interessante para teste no BDK e clicarmos na opção de
menu View->Disable Design Mode.
A figura abaixo mostra o teste do Bean.
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
262
Java na Prática
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
263
Java na Prática
264
Capítulo XI Perguntas Frequentes
Como executar um programa a partir de uma aplicação em Java?
Resposta: isso pode ser feito com o método de instância exec da classe
Runtime.
Como criar um TextField que não exiba o que está sendo digitado para
se usado como campo de entrada de senhas?
Resposta: use o método setEchoChar() do TextField para definir qual
caractere que deve ser ecoado.
Como aumentar a área de ambiente do DOS para caber o CLASSPATH?
Resposta: coloque a seguinte linha no autoexec.bat
set shell=c:\command.com /e:4096
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
265
Bibliografia
Eckel B. Thinking in Java. 2nd Ed. New Jersey : Prentice Hall, 2000.
Gosling J., Joy W., Steele G. The Java Language Specification. Massachusetts :
Addison-Wesley, 1996.
Oaks S. Java Security. California : O’Reilly & Associates, Inc, 1998.
Oaks S., Wong H. Java Threads. 2ª Ed. California : O’Reilly & Associates, Inc,
1999.
Watt D. A. Programming Language Concepts and Paradigms. Great Britain :
Prentice Hall, 1990.
Ethan H., Lycklama E. How do you Plug Java Memory Leaks? Dr. Dobb´s
Journal, San Francisco, CA, No. 309, February 2000.
Wahli U. e outros. Servlet and JSP Programming with IBM WebSphere Studio
and VisualAge for Java, IBM RedBooks, California, May 2000.
Sadtler C. e outros. Patterns for e-business: User-to-Business Patterns for Topology
1 and 2 using WebSphere Advanced Edition, IBM RedBooks, California, April
2000.
Bagwel D. e outros. An Approach to Designing e-business Solutions, IBM
RedBooks, California, December 1998.
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
266
Links
Revistas
http://www.javaworld.com/
Revista online sobre Java.
Livros
http://www.eckelobjects.com/
Página do autor do livro Thinking in Java, atualmente em segunda edição. O
livro pode ser baixado gratuitamente no site.
http://www.redbooks.ibm.com/booklist.html
Livros da IBM
Servidores
http://jakarta.apache.org
Página do projeto Jakarta que desenvolveu o Tomcat.
http://www.metronet.com/~wjm/tomcat
Lista Tomcat
http://www.jboss.org
Servidor de aplicação gratuito habilitado para EJB
Dicas Java e recursos
http://java.sun.com/
Página da Sun com informações, tutoriais e produtos Java.
http://gamelan.earthweb.com/
Página da com informações, Applets, Lista de discussão, tutoriais.
http://www.inquiry.com/techtips/java_pro
Ask the Java Pro
http://www.jguru.com/
jGuru.com(Home): Your view of the Java universe
http://www.soujava.org.br
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
Bem Vindo ao SouJava!
Servlets e JSP
http://www.servlet.com/srvdev.jhtml
Servlet Inc : Developers Forum
http://www.servlets.com
Servlets.com
http://www.jspin.com/home
Jspin.com - The JSP Resource Index
http://www.burridge.net/jsp/jspinfo.html
Web Development with JSP: JSP, Java Servlet, and Java Bean
Information
http://www.apl.jhu.edu/~hall/java/ServletTutorial
A Tutorial on Java Servlets and Java Server Pages (JSP)
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
267
Java na Prática
268
Índice
A
Acesso Direto.........................................137
Acesso Sequencial .................................132
Agregação ................................................24
amarração.................................................99
Arrays .............................................36, 151
Assert ...................................................62
Assertivas...................... Consulte Assert
Assinatura dos métodos .....................17, 20
Associação comum ..................................22
Atribuição ...............................................48
atributos ...................................................67
Atributos ..................................................10
AWT ......................................................174
B
Bloco........................................................51
boolean.....................................................32
booleanos
boolean ................................................35
break .....................................................59
Button .................................................210
Bytecodes.............................................6, 27
C
cadeias de caracteres ......... Consulte Strings
Calendar ................................................1
Caractere
char......................................................36
cardinalidade da relação...........................22
casting............ Consulte Conversão de tipos
char ..........................................................32
Classe .................................................10, 66
classe anônima .......................................189
Classes Abstratas ...................................101
Classes Internas....................................105
Classes Internas Anônimas .................107
clone.........................................................81
Coleta de Lixo............................................7
Color ...................................................206
Comentários ...........................................50
Component..........................................208
Construtor ................................................15
Construtores........................................... 68
Containers.............................................. 215
continue.............................................. 59
Convenções................................................ 9
Conversão de Objetos.......................... 112
Conversão de Tipos ............................... 40
Cópia profunda ...........Consulte deep copy
Cópia rasa ............. Consulte Shallow copy
Criptografia................................................ 8
D
Date ..................................................... 154
deep copy................................................. 81
Derivando classes.......... Consulte herança
Diagrama de Classes................................ 19
double ...................................................... 31
do-while .................................................. 57
E
escape ...................................................... 33
Escopo..................................................... 51
escopo de pacote...................................... 72
Estruturas de Controle.......................... 52
Exceções................................................ 113
extends............................................... 16, 96
F
final ..................................................... 76
Finalização ........................................... 109
finalize() ................................................ 112
finally .............................................. 121
float.......................................................... 31
for............................................................ 58
Frame ................................................... 218
G
Garbage Collection ..Consulte coleta de lixo
Generalização .......................................... 24
H
Hashtable ......................................... 148
herança ................................................... 94
Herança.............................................. 11, 13
Herança Múltipla ..................................... 96
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
hexadecimal .............................................30
I
Identificadores........................................28
if.............................................................52
Inicialização ..........................................109
Instância ...................................................10
Inteiros
int 35
Interfaces ..............................................102
1
Observer.................................... 157, 159
octal ......................................................... 30
Ocultação de informação ................... 13, 15
Olá mundo ............................................... 26
Operadores............................................. 41
P
J2EE...........................................................9
J2ME..........................................................9
JAR ..................................................89, 242
java.io...............................................132
JavaBean ................................................245
JCE.............................................................8
JDK ............................................................8
Packages ................................................. 86
Pacotes...........................Consulte Packages
Palavras Reservadas.............................. 29
Panel ................................................... 217
polimorfismo ......................................... 100
Polimorfismo ........................................... 18
Ponteiro ..................................................... 6
Ponto Flutuante
float, double ........................................ 35
Precedência entre Operadores.............. 49
private...................................................... 15
Propriedades ............................................ 10
L
R
Label ...................................................211
linha de comando ...................................61
List......................................................212
Literais ....................................................30
RandomAccessFile ................................ 137
recursão ................................................... 56
Referências Compartilhadas................. 78
return................................................... 69
M
S
main .........................................................27
manifest....................................................90
Máquina Virtual .........................................6
Mecanismo de Extensão.........................92
método de classe ......................................71
método de instância..................................71
métodos....................................................67
Métodos ...................................................14
Modificadores de acesso ........................72
MVC ......................................................156
separadores .............................................. 33
shallow copy ............................................ 81
Smalltalk.................................................... 6
Sobrecarga ............................................... 17
Sobrescrita ......................................... 17, 98
Stack ................................................... 146
static................................................... 74
String ....................................................... 32
StringBuffer .................................... 79
Strings..................................................... 37
StringTokenizer ........................... 160
super ..................................................... 73
Superclasse .............................................. 10
switch................................................... 54
synchronized .................................... 78
J
N
native ...................................................78
null........................................................33
O
Object.......................................................18
Objeto
Comportamento...................................11
Objetos...............................................10, 69
Observable .......................................156
T
TextArea.................................... 187, 214
TextField ................................. 187, 213
this ....................................................... 85
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
Throwable..........................................115
Tipos de dados ........................................34
toString()..................................................41
Tratamento de Eventos...........................177
try/catch..........................................114
U
2
V
Vector................................................. 143
vinculação.....................Consulte amarração
Visibilidade.............................................. 21
void ....................................................... 69
W
UML.........................................................19
Unicode....................................................28
Unidade de Compilação ...........................67
while ..................................................... 56
widgets................................................... 174
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
Java na Prática
Alcione de P. Oliveira, Vinícius V. Maciel - UFV
1
Download