0% acharam este documento útil (0 voto)
9 visualizações185 páginas

Programação Java

Enviado por

Marcelo Biss
Direitos autorais
© © All Rights Reserved
Levamos muito a sério os direitos de conteúdo. Se você suspeita que este conteúdo é seu, reivindique-o aqui.
Formatos disponíveis
Baixe no formato PDF, TXT ou leia on-line no Scribd
0% acharam este documento útil (0 voto)
9 visualizações185 páginas

Programação Java

Enviado por

Marcelo Biss
Direitos autorais
© © All Rights Reserved
Levamos muito a sério os direitos de conteúdo. Se você suspeita que este conteúdo é seu, reivindique-o aqui.
Formatos disponíveis
Baixe no formato PDF, TXT ou leia on-line no Scribd
Você está na página 1/ 185

Sumário

1 – Operações Iniciais 3

2 – Identificadores, palavras-chave e tipos 13

3 – Expressões e controle de fluxo 25

4 – Matrizes 33

5 – Objetos e Classes 37

6 – Recursos avançados da linguagem 55

7 – Construção de GUIs Java 71

8 – O modelo de evento AWT 87

9 – A biblioteca de componentes AWT 95

10 – Introdução aos Applets Java 111

11 – Exceções da linguagem Java 129

12 – Fluxo de E/S e arquivos 137

13 – Java Database Connectivity 147

14 – Threads 153

15 – Utilização de rede com Java 173

16 – Introdução a API Servlets 179

Apendice A – Apresentações de Introdução 185


Linguagem de Programação Java 2
Capítulo 1

Operações Iniciais

Objetivos

Após concluir este módulo, você será capaz de:


• Descrever os principais recursos da linguagem Java
• Descrever a Máquina Virtual Java (JVM)
• Descrever como funciona a coleta de lixo da linguagem Java
• Descrever como funcionam os recursos de segurança da linguagem Java
• Criar um aplicativo Java simples, compilá-lo e executá-lo
• Utilizar a document ação on-line da API Java

Linguagem de Programação Java 3


O que é Java?
Java é:

• Uma linguagem de programação


• Um ambiente de desenvolvimento
• Um ambiente de aplicativo

A linguagem Java é o resultado da pesquisa de uma linguagem de programação que pudesse


fornecer a eficiência da linguagem C++ e ainda a vantagem de uma linguagem que oferecesse
segurança na utilização de tipos, como a SmallTalk.

Os principais objetivos do desenvolvimento dessa nova linguagem foram:


• Criar uma linguagem orientada a objetos
• Fornecer um ambiente interpretado por dois motivos:
• Velocidade de desenvolvimento – Eliminar o ciclo de compilação/vinculação/carga/teste
• Portabilidade de códigos – Um ambiente interpretado faz chamadas específicas, em nível de
sistema operacional, no lugar do ambiente de tempo de execução.
• Eliminar as práticas de codificação que afeta a eficiência dos códigos:
• Aritmética de ponteiros
• Alocação / desalocação de memória
• Fornecer uma maneira para que os programas executem mais de um thread de execução
• Fornecer um meio de alterar dinamicamente os programas em tempo de execução, permitindo
que carreguem e descarreguem módulos de código
• Fornecer um meio de verificar os módulos de códigos carregados para evitar problemas de
segurança

A arquitetura da linguagem Java foi desenvolvida para atingir os objetivos mencionados acima. Os
seguintes recursos atingem esses objetivos:
• A máquina virtual Java (JVM)
• A coleta de lixo (garbage collector)
• A segurança de códigos

A Máquina Virtual Java (JVM – Java Virtual Machine)

A Especificação JVM define a Máquina Virtual Java (JVM) como:

Uma máquina imaginária que é implementada através de sua emulação em um software


contido em uma máquina real. O código da Máquina Virtual Java é armazenado nos arquivos
.class, que contém o código de, no máximo, uma classe pública.

A Especificação JVM fornece as especificações de plataformas de hardware de acordo com as quais


o código Java é compilado. Essa especificação permite que programas Java sejam independentes
de plataforma, já que a compilação é feita em uma máquina imaginária. O interpretador Java de
cada plataforma de hardware específica é responsável pela execução do código compilado para a
JVM.

A especificação JVM fornece definições concretas para a implementação do seguinte:


• Conjunto de instruções (equivalente ao conjunto de instruções de uma CPU)
• Conjunto de registradores
• Formato do arquivo de classes
• Pilha
• Heap de coleta de lixo
• Área de memória

Linguagem de Programação Java 4


O formato de código da JVM é constituído de bytecodes compactos e eficientes. Os programas
representados pelos códigos de bytes da JVM devem manter a disciplina adequada de tipos . A
maior parte da verificação de tipos da linguagem Java é realizada no tempo de compilação pelo
verificador de códigos de bytes.

Qualquer interpretador compatível com Java deve ser capaz de executar qualquer programa com os
arquivos de classes que estão em conformidade com o formato de arquivo de classes descrito na
Especificação JVM.

Garbage Collector

Várias linguagens de programação permitem a alocação dinâmica de memória em tempo de


execução. O processo de alocação de memória varia de acordo com a sintaxe da linguagem, mas
sempre envolve o retorno de um ponteiro ao endereço inicial do bloco de memória.

Quando a memória alocada não for mais necessária (o ponteiro que faz referência à memória fica
sem escopo), é aconselhável que o programa ou ambiente de tempo de execução desaloque a
memória para evitar que o programa fique sem memória.

Nas linguagens C e C++ (entre outras), o desenvolvedor do programa é responsável pela


desalocação da memória. Ás vezes, isso pode ser difícil, especialmente porque nem sempre se
sabe, de antemão, quando a memória deve ser liberada. Quando não houver mais memória para ser
alocada no sistema, é possível que ocorram falhas em programas que não desalocam a memória.
Diz-se que esses programas contém “perdas de memória” (memory leak).

Ao utilizar o Java, o programador não mais terá a responsabilidade de liberar a memória, pois essa
linguagem fornece um thread de segundo plano, em nível de sistema, que registra toda a alocação
de memória e mantém uma contagem do número de referências feitas a cada ponteiro de memória.
Durante os ciclos de inatividade no tempo de execução da Máquina Virtual Java, o thread de coleta
de lixo verifica se há algum ponteiro de memória em que o número de referências tenha sido
reduzido a zero (0). Se houver, a memória “marcada” pelo thread de coleta de lixo é “limpa”
(desalocada).

A coleta de lixo ocorre automaticamente durante a vida útil de um programa Java elimina a
necessidade de desalocar a memória e perdas de memória.

Segurança de códigos

A figura abaixo ilustra o ambiente do programa Java. Utilizaremos esta figura para descrever como a
linguagem Java impõe a segurança de códigos.

Os arquivos da linguagem Java são “compilados” para que sejam convertidos do formato de texto
em que os programadores o criam para um conjunto de códigos de bytes independente de máquina.

Em tempo de execução, os códigos de bytes que constituem o programa Java são carregados,
verificados e, em seguida, executados em um interpretador. O interpretador tem duas funções:
executar o bytecode Java e fazer as chamadas apropriadas, através de um aplicativo em tempo de
execução do sistema, para o hardware subjacente.

Em alguns ambientes run-time Java, uma parte dos bytecodes verificados é compilada no código
original da máquina e executada diretamente na plataforma de hardware.

O interpretador Java

O interpretador Java deve executar o código compilado para a Máquina Virtual Java. Esse
interpretador é responsável por três tarefas principais:

Linguagem de Programação Java 5


• Carregamento do código – Executada pelo carregador de classes (class loader)
• Verificação do código – Executada pelo verificador de bytecodes
• Execução do código – Executada pelo interpretador de tempo de execução

Carregador de classes

O carregador de classes carrega todas as classes necessárias à execução de um programa. Ele


adiciona segurança separando os nomes de espaços das classes do sistema de arquivos locais e os
que foram importados das fontes de rede. Esse procedimento limita qualquer aplicativo “cavalo de
Tróia”, pois as classes embutidas sempre são verificadas primeiro.

Uma vez carregadas todas as classes, o layout da memória do arquivo executável é determinado.
Nesse ponto, endereços específicos de memória são atribuídos a referências simbólicas e a tabela
de pesquisa é criada. Como o layout de memória ocorre em tempo de execução, um interpretador
Java adiciona proteção contra o endereçamento inválido de código, que possa enviar um programa
que cause danos ao sistema operacional.

Verificador de bytecodes

O código Java passa por vários testes antes de ser efetivamente executado na máquina. O
programa executa o código através de um verificador de bytecodes que testa o formato dos
fragmentos de código e aplica um provador de teoremas para verificar a existência de códigos
ilegais, os quais falsificam ponteiros, violam os direitos de acesso a objetos ou tentam alterar o tipo
ou a classe do objeto.

Observação- Todos os arquivos de classes importados na rede passam através do verificador de


bytecodes.

Processo de verificação

O verificador de bytecodes faz quatro passagens no código de um programa. Além disso, assegura
que o código esteja em conformidade com as especificações JVM e que não viole a integridade do
sistema. Se o verificador concluir todas as quatro passagens sem retornar uma mensagem de erro,
você poderá ter certeza do seguinte:
• As classes estão de acordo com o formato de arquivo de classe contido nas especificações JVM.
• Não há violações de restrição de acesso.
• O código não causa estouros nem estouros negativos em pilhas de operandos.
• Os tipos de parâmetros referentes a todos os códigos operacionais são sempre considerados
como corretos, é uma linguagem fortemente tipada.
• Não ocorreu nenhuma conversão de dados inválida, como a conversão de inteiros em ponteiros.
• Os acessos a campos de objeto são considerados como válidos, dependendo do modificador.

Linguagem de Programação Java 6


Aplicativo Java básico
Como qualquer outra linguagem de programação, a linguagem Java é utilizada para criar aplicativos.
O código mínimo de aplicativo normalmente exibe a string Olá mundo! na tela. O código Java
abaixo mostra um exemplo de um arquivo Java mínimo.

OlaMundo.java
1. // Exemplo de aplicativo
2. //
3. public class OlaMundo {
4. public static void main (String args[]) {
5. System.out.println(“Ola mundo!”);
6. }
7. }

Essas linhas reúnem os componentes mínimos necessários para imprimir Ola mundo! na tela.

As primeiras duas linhas do programa são de comentários, iniciando por // .

A próxima linha declara o nome da classe como OlaMundo . O nome da classe especificado no
arquivo de origem cria um arquivo nomedaclasse.class no diretório em que você executou a
compilação. Nesse caso, o compilador cria um arquivo chamado OlaMundo.class .

A próxima linha do programa é o local onde inicia a execução do programa. O interpretador Java
deve encontrá-la exatamente como foi definida ou ele se recusará a executar o programa.

Outras linguagens de programação, especialmente C e C++ também utilizam a declaração main()


como o ponto inicial da execução.

Se o programa receber argumentos em sua linha de comando, eles serão passados para o método
main(), em uma matriz de Strings. Nesse exemplo, não são esperados ou tratados argumentos.
• public – O método main()
• static – Uma keyword que informa ao compilador que o método main() pode ser utilizado no
contexto da classe, não exigindo uma instância para sua execução.
• void – Indica que o método main() não retorna nada. Isso é importante porque a linguagem
Java executa uma verificação de tipos cuidadosa, que inclui verificar se os métodos chamados
retornam os tipos com os quais foram declarados.
• String args[ ] - A declaração de um array de Strings. Estes são os argumentos digitados
na linha de comandos após o nome da classe. O nome da classe não é incluída nos argumentos.
Java OlaMundo arg1 arg2 . . .

A linha System.out.println(“Olá mundo!”); ilustra a utilização de um nome de classe, nome


de objeto e uma chamada de método. Ela faz com que a string Ola Mundo! seja impressa na saída
padrão.

O método println recebe um argumento String e passa-o para o fluxo de saída padrão.

Por fim as duas linhas finais fecham os blocos com chaves, de maneira semelhante a C e C++.

Linguagem de Programação Java 7


Compilar e Executar o OlaMundo
Compilar

Após criar o arquivo de origem OlaMundo.java, compile-o com a seguinte linha:

C:\> javac OlaMundo.java

Se o compilador não retornar mensagens, o novo arquivo OlaMundo.class será armazenado no


mesmo diretório que o arquivo de origem.
Se houver algum problema na compilação do aplicativo, verifique as mensagens de solução de
problemas.

Executar

Para executar o aplicativo OlaMundo, utilize o interpretador Java, java, também localizado no
diretório bin.

C:\> java OlaMundo


Olá mundo!

Observação – O sistema anexa o local das classes do sistema ao fim do caminho da classe, a
menos você utilize a opção –classpath para especificar um caminho.

Linguagem de Programação Java 8


Solução de problemas de compilação
Erros de tempo de compilação

Estes são erros comuns encontrados em tempo de compilação:

• javac: Command not found

A variável de caminho não foi definida corretamente para incluir o compilador javac. O compilador
está localizado no diretório bin, abaixo do JDK (Java Developers Kit).

• OlaMundo.java:3: Method printl(java.lang.String)


not found in class java.io.PrintStream.
System.out.printl(“Ola mundo!”);

O nome do método println está digitado incorretamente.

• In class OlaMundo: main must be public and static

Ocorre esse erro porque a palavra static ou public não foi inserida na linha que contém o
método main.

Erros de tempo de execução

• Can’t find class OlaMundo

(Esse erro é gerado quando você digita java OlaMundo)

Em geral, isso significa que o nome da classe foi digitado de forma diferente do nome do arquivo de
origem e que um arquivo nomedoarquivo.class foi criado com a mesma ortografia (inclusive
maiúsculas e minúsculas) que a definição da classe.

Por exemplo,

class OlaMundo {

Cria uma classe OlaMundo que não tem o nome de classe esperado pelo compilador.

Linguagem de Programação Java 9


Layout do arquivo de origem Java
Erros de tempo de execução

• Atribuição de nomes

Se o arquivo .java contiver uma classe pública, seu nome deverá ser o mesmo que o desta classe.

• Contagem de classes

Uma regra na linguagem Java estabelece que no máximo uma classe pública pode ser definida em
um arquivo fonte.

• Layout

Um arquivo fonte .java pode conter três elementos de “nível superior”:

São eles:

• uma declaração de package opcional


• qualquer número de declarações de importação
• declarações de classe e de interface

Esses itens devem aparecer nessa ordem. Isto é, as instruções de importação devem preceder
todas as declarações de classe e, se uma declaração de package for utilizada, deverá preceder as
classes e as importações.

Linguagem de Programação Java 10


Utilizar a documentação da linguagem Java
O JDK contém um conjunto de arquivos HTML que documenta a API incluída nos pacotes
fornecidos. O layout dessa documentação e hierárquico, de modo que a home page do conjunto
relacionem todos os pacotes como vínculos. Se você selecionar um pacote específico, a página
mostrada relacionará todas as classes que são membros do pacote. Cada classe é mostrada como
um vínculo e a seleção desse vínculo apresentará uma página de informações sobre essa classe.
Cada documentação de classe têm o mesmo formato, embora alguns aspectos possam ser omitidos
se não forem relevantes para uma determinada classe.

Estas são as principais seções de uma documentação de classe:


• A hierarquia da classe
• Uma descrição da classe e de sua finalidade geral
• Uma lista de variáveis membros
• Uma lista de construtores
• Uma lista de métodos
• Uma lista detalhada de variáveis, com descrições completas sobre a finalidade e o utilização
de cada uma
• Uma lista detalhada de construtores, com descrições completas
• Uma lista detalhada de métodos, com descrições completas

O fato de um Button ser herdado de um Component é indicado pela a hierarquia de classes. Por
enquanto, você precisa saber que vários recursos de uma classe talvez não estejam documentadas
na página referente a ela e, em vez disso, estarão descritos na página selecionada à classe "pai".
Portanto para localizar o método setColor() de um Button, você deverá procurar em
Component na documentação.

Linguagem de Programação Java 11


Linguagem de Programação Java 12
Capítulo 2

Identificadores, palavras-chave e tipos

Objetivos

Após concluir este módulo, você será capaz de:

• Utilizar comentários em um programa fonte Java


• Distinguir identificadores válidos e inválidos
• Reconhecer as palavras-chave da linguagem Java
• Listar os oito tipos primitivos da linguagem Java
• Definir valores literais para tipos numéricos e textuais
• Descrever o significado básico dos termos classe, o objeto, variável
membro e referência
• Criar uma definição de classe Java para uma classe simples que contenha
variáveis membro primitivas
• Declarar as variáveis de tipos de classe
• Construir um objeto, usando new, e descrever a inicialização padrão
• Acessar variáveis membro de um objeto usando a notação de pontos
• Descrever o significado de uma variável de referência e indicar as
conseqüências de uma atribuição entre variáveis de tipo de classe

Linguagem de Programação Java 13


Comentários
Três estilos são permitidos para a inserção de comentários:

// Comentário em uma linha


/* comentários em uma ou mais linhas */
/** comentário de documentação */

Os comentários de documentação inseridos imediatamente antes de uma declaração (de uma


variável ou função) indicam que o comentário deve ser incluído em qualquer documentação gerada
automaticamente (os arquivos HTML gerados pelo comando javadoc). Esses comentários servem
como uma descrição do item declarado.

Observação - O formato desses comentários utilizados na ferramenta javadoc são abordados no


diretório docs/tooldocs/solaris ou docs/tooldocs/win32 da documentação de API
referente ao JDK.

Pontos-e-vírgulas, blocos e espaços em branco


Na linguagem Java, as instruções são terminadas com um ponto-e-virgula (;).

Um bloco é marcado com a inclusão de chave ao seu redor - {e}, constituindo uma instrução
composta.

Permite-se um espaço em branco entre os elementos do código fonte e, sempre que for permitido,
você poderá utilizar quantos desejar. O espaço em branco abrange espaços, tabulações e novas
linhas. portanto, poderá ser utilizado para melhorar a aparência do código de origem.

{
int x;
x = 23 * 54;
}

Identificadores
Na linguagem Java, os identificadores iniciam com uma letra, um caracter de sublinhado
( _ ) ou um símbolo de dólar ($). Os caracteres subsequentes também podem conter dígitos. Os
identificadores fazem distinção entre maiúsculas e minúsculas e não têm limite de comprimento.

Identificadores válidos:

• identifier
• username
• User_name
• _sys_var1
• $change

As origens da linguagem Java se encontram efetivamente em Unicode, em vez de em texto ASCII.


Assim, uma letra representa uma definição bem mais abrangente do que apenas A até Z.

Tenha cuidado com caracteres que não sejam ASCII, pois o Unicode pode suportar diferentes
caracteres que possuem a mesma aparência.

Linguagem de Programação Java 14


Os identificadores podem conter palavras-chave, mas não necessariamente. As palavras chave da
linguagem Java são descritos a seguir. Portanto, thisOne é um identificador válido, mas this não
é.

Observação - os identificadores que contém um símbolo de dólar ($) geralmente são raros, embora
linguagens como os shells BASIC e UNIX e também os sistemas VAX VMS os utilizem com
frequência. Como esses identificadores não são comuns, provavelmente é melhor evitá-los, a menos
que haja uma convenção local ou outra razão importante para se incluir esse símbolo no
identificador.

Palavras-chave na linguagem Java


abstract do implements private throw
boolean double import protected throws
break else instanceof public transient
byte extends int return true
case false interface short try
catch final long static void
char finally native super volatile
class float new switch while
continue for null synchronized
default if package this

Observação - Na linguagem Java, os literais true, false e null se encontram em letras


minúsculas e não em maiúsculas como na linguagem C++. Estritamente, não são palavras-chave e
sim literais. No entanto, a distinção é acadêmica.

Observação - Não existem operadores sizeof. o tamanho e a representação de todos os tipos são
fixos e não dependem de implementação.

Observação - goto e const são palavras-chaves, mas não são utilizados na linguagem Java.

Linguagem de Programação Java 15


Tipos básicos da linguagem Java
Java define oito tipos de dados primitivos e um tipo especial que podem ser utilizados como valores
literais ou em variáveis. Os tipos podem ser considerados em quatro categorias: lógica, textual,
integral e ponto flutuante.

Lógica - boolean
Os valores lógicos possuem dois estados. Em geral, são indicados como "ativado" e "desativado",
"verdadeiro" e "falso" ou "sim" e "não". Na linguagem Java, esse valor é representado pelo tipo
boolean. O boolean contém dois valores literais: true e false.

Observação - Não há conversão entre tipos de inteiro e o tipo boolean. Algumas linguagens,
especialmente C e C++, permitem que valores numéricos sejam interpretados como valores lógicos,
normalmente considerando o zero como falso e o valor diferente de zero como verdadeiro. Isso não
é permitido na linguagem Java, pois sempre que um valor boolean for necessário, nenhum outro
poderá ser utilizado.

Textual – char e String

Os caracteres simples são representados através do tipo char. O char representa um caracter
Unicode que utiliza um número de 16 bits sem sinal, em uma faixa de 0 a 2 16 -1. O tipo char deve
ser delimitado por aspas simples. (‘ ‘).

‘a’ a
‘\t’ tab
‘\u????’ um caracter Unicode específico, ???? . Deve ser substituído exatamente por quatro
dígitos hexadecimais.

O tipo String, que não é primitivo, é utilizado para representar a seqüência de caracteres. Os
caracteres baseados em Unicode, e os estilos de seqüência de escape mostrados para o tipo char
também funcionam em uma String. Um literal String é colocado entre aspas duplas, como a
seguir:

“abcdefghijklmnopqrstuvwxyz 1234567890”

Inteiro – byte, short, int e long

Há quatro tipos inteiros na linguagem Java. Cada tipo é declarado através de uma das palavras-
chave byte, short, int ou long. Os literais de um tipo inteiro podem ser representados
através de formas decimais, octais ou hexadecimais, como a seguir:

2 O valor decimal é dois.


077 O zero à esquerda indica um valor octal.
0xBABE O 0x à esquerda indica um valor hexadecimal.

Observação - todos os tipos inteiros na linguagem Java são números com sinais.

Os literais inteiros pertencem ao tipo int, a menos que explicitamente seguidos da letra “L” indica
um valor long. Observe que, na linguagem Java, é válido utilizar o L maiúsculo ou minúsculo. No
entanto, letra minúscula não é recomendável, pois, na maioria das vezes, ela não pode ser
distinguida do dígito um. Estas são versões longas dos literais mostrados acima:

2L O valor decimal, como um long.


077L O zero à esquerda ainda indica um valor octal.

Linguagem de Programação Java 16


O tamanho e a faixa referentes aos tipos inteiros são mostrados a seguir. A representação da faixa é
definida pela especificação da linguagem Java como complemento de dois e independente da
plataforma.

Tamanho do inteiro Nome ou tipo Faixa


8 bits byte -27 ... 27 –1
16 bits short -215 ... 215 –1
32 bits int -231 ... 231 –1
64 bits long -236 ... 263 –1

Ponto flutuante – float e double

Uma variável de ponto flutuante pode ser declarada através da palavra-chave float ou double.
Um literal será um ponto flutuante se incluir um decimal, uma parte exponencial (a letra E) ou for
seguido da letra F ou D.

3.14 Um valor de ponto flutuante simples.


6.02E23 Um valor de ponto flutuante extenso.
2.718F Um valor float simples.
123.4E+306D Um valor duplo extenso com D redundante.

Tamanho do ponto flutuante Nome ou tipo


32 bits float
64 bits double

Observação - os literais de ponto flutuante são double, a menos que explicitamente declarados
como float.

O formato de um número de ponto flutuante é definido pela especificação Java como uma
representação IEEE 754, usando os tamanhos mostrados na tabela acima. Esse formato independe
de plataforma.

Linguagem de Programação Java 17


Variáveis, declarações e atribuições
O programa seguir ilustra como declarar e atribuir valores às variáveis do tipo inteiro, ponto
flutuante, o booleano, caracter e seqüência.

1 public class Assign {


2 public static void main(String args[]) {
3
4 int x,y;
5 float z = 3.414f;
6 double w = 3.1415;
7 boolean truth = true;
8
9 char c;
10 String str;
11 String str1 = “adeus”;
12
13 c = ‘A’;
14 str = “Oi!”;
15
16 x = 6;;
17 y = 1000;
18 ...
19 }
20 }

Linguagem de Programação Java 18


Convenções de codificação da linguagem Java
Classes - os nomes de classes devem ser constituídos de substantivos, em letras maiúsculas e
minúsculas, com a primeira letra de cada palavra em maiúsculas.

Interfaces - os nomes de interface devem ser escritos em letras maiúsculas e minúsculas, como nos
nomes de classe.

Métodos - os nomes de métodos devem ser constituídos de verbos, em maiúsculas e minúsculas,


com a primeira letra minúscula. Em cada nome de método, as letras maiúsculas separam as
palavras. Em geral, você não deve utilizar o caracter de sublinhado em nomes de métodos.

Constantes - as constantes primitivas devem ser descritas totalmente em letras maiúsculas, com as
palavras separadas por caracteres de sublinhado. As constantes de objetos podem usar maiúsculas
e minúsculas.

Variáveis - todas as variáveis globais, de classe e de instância devem ser descritas em maiúsculas e
minúsculas, com a primeira letra minúscula. As palavras são separadas por letras maiúsculas. Em
geral, você não deve utilizar o caracter de sublinhado em nomes de variáveis. Além disso, evite
utilizar o símbolo de dólar ($), pois esse caracter possui um significado especial em inner classes.

Os nomes das variáveis devem ter significado. Esse nome deve indicar a função da variável ao leitor
casual. Você deve evitar utilizar nomes de um único caracter, exceto no caso de variáveis
"descartáveis" temporárias (por exemplo, i, j, k, para uma variável de controle que não é utilizada
fora do loop).

Estruturas de controle - utilize chaves ( { } ) ao redor de todas as declarações, mesmo a simples,


quando elas fizerem parte de uma estrutura de controle como uma declaração if-else ou for.

Espaçamento - coloque apenas uma única instrução em qualquer linha e utilize um recuo de quatro
espaços para que seu código fique legível.

Comentários - Deve-se utilizar comentários para explicar os segmentos de código que não sejam
óbvios. Utilize delimitador de comentários // para comentário normal, de modo que as seções de
código possam ser comentadas através dos delimitadores /*...*/. Utilize o comentário de
documentação /**...*/ para fornecer uma API através do javadoc à pessoa que realizará a
manutenção posteriormente.

Linguagem de Programação Java 19


Objetos
Visão Geral

As linguagens de programação anteriores e os programadores inexperientes tendem a tratar as


variáveis como itens isolados. Por exemplo, se um programa precisar manipular uma data, três
inteiros separados serão declarados.

int dia, mes, ano;

Essa declaração tem duas funções. Ela informa que quando nos referirmos ao dia, mês ou ano,
estaremos manipulando um inteiro, em vez de qualquer outro item da memória. Ela também aloca a
área de armazenamento para esses inteiros.

Embora essa abordagem seja de fácil compreensão, possui duas desvantagens significativas.
Primeiro, se o programa precisar controlar várias datas, mais declarações serão necessárias. Para
armazenar duas datas de aniversário, podemos utilizar:

int meuDia, meuMes, meuAno;


int seuDia, seuMes, seuAno;

Esse método logo se torna o confuso em termos da quantidade de nomes necessários.

Segunda é que cada um desses valores consiste em uma variável independente. Em um nível
conceitual, e existe uma associação entre um dia, um mês e um ano. Eles fazem parte de um
mesmo item, neste caso, uma data.

Tipos agregados de dados

A maioria das linguagens de programação, até mesmo as mais antigas, suporta o conceito de
variáveis como tipos. Isto é, um valor pode ser um inteiro, um número de ponto flutuante ou talvez
um caracter. Embora essas linguagens contenham vários tipos e embutidos, o ideal é haver a
possibilidade de definir novos tipos para representar outras idéias, como uma "data".

Essas dificuldades são abordadas em diversas linguagens, através da inclusão de tipos agregados
de dados. Os tipos agregados de dados são mencionados em algumas linguagens como tipos
estruturados ou tipos de registro.

Os tipos agregados de dados são definidos pelos programadores no programa fonte. Uma vez feita
a definição, o tipo poderá ser utilizado como qualquer outro. Para definir um tipo, o programador
deve descrever os elementos que constituem uma instância na nova variável.

Considere a seguinte declaração:

int dia;

Java sabe que determinada área de armazenamento deve ser alocada e também pode interpretar o
conteúdo desse armazenamento. Portanto, para definir um novo tipo, devemos e especificar a área
de armazenamento necessária e como interpretar o conteúdo. Em geral, isso não é feito com base
nos números de bytes, nem na ordem e no significado dos bits, mas sim com base em outros tipos
que já foram compreendidos.

Por exemplo, para definir um tipo que represente uma data, precisamos de um armazenamento
suficiente para três inteiros, além de tratar o espaço como três inteiros separados. Além disso,
quando os utilizamos, atribuímos o significado do dia, mês e ano, respectivamente a esses inteiros.
Na linguagem Java, isso é feito da seguinte maneira:

Linguagem de Programação Java 20


public class Data {
int dia;
int mes;
int ano;
}

A palavra class faz parte da linguagem Java e deve ser totalmente escrito em letras minúsculas. O
nome Date é escrito com a primeira letra maiúscula em virtude de convenção; não é um requisito da
linguagem.

Observação - Uma classe representa bem mais que um simples tipo de dados agregado. Porém,
abordaremos essas questões adicionais mais adiante.

Agora, uma variável pode ser declarada como sendo do tipo Date, que as partes que constituem o
dia, ou mês e o ano serão indicadas por essa declaração da seguinte maneira:

Data minhaData, suaData;

Utilizando essa declaração, Java permitem que as partes (day, month e year) das variáveis sejam
acessadas através do operador ponto (.), como mostrado a seguir:

minhaData.dia = 26;
minhaData.mes = 11;
minhaData.ano = 1960;

Os tipos agregados de dados fornecem uma maneira de associar as partes componentes do modelo
de uma data, tratando-as não como itens isolados e sim como elementos de um todo maior. Além
disso, reduzem a carga de nomes, já que, em vez de três nomes, apenas um é necessário para a
cada data.

Os elementos que constituem a classe, isto é, as partes de dia, mês e ano, são normalmente
chamados campos, mas na terminologia do orientada a objetos recebem o nome de variáveis
membro. Como a linguagem Java é orientada a objetos, utilizaremos o termo variável membro ou
apenas membro.

Uma instância de um tipo agregado, como Data, é chamada objeto.

Criar um objeto

Quando declaramos variáveis de qualquer tipo primitivo, isto é, seja boolean, byte, short, char,
int, long float ou double, um espaço da memória é alocado como parte da operação. A
declaração de uma variável através de um tipo de classe como String ou de qualquer tipo definido
pelo ou usuário não aloca memória.

Observação - Algumas linguagens alocam memória, mas Java não.

Na verdade, uma variável que declarada com um em tipo de classe não consiste nos dados, e sim
em uma referência aos dados.

Observação – A referência também pode ser considerado como um ponteiro, que é sua verdadeira
função na maioria das implementações. Se esse termo tiver significado para você, ele irá ajudá-lo a
compreender melhor o assunto.

Antes que você possa utilizar a variável, a área de armazenamento deve ser alocada. para isso,
utiliza-se a palavra-chave new como a seguir:

Linguagem de Programação Java 21


Data minhaData;
minhaData = new Data();

A primeira declaração aloca apenas o espaço suficiente para a referência. A segunda declaração
aloca o espaço para os três inteiros utilizados para formar a Data. A atribuição configura a variável
minhaData para se referir corretamente ao novo objeto. Após executar essas duas operações,
você poderá manipular os componentes do tipo data, como descrito anteriormente.

O armazenamento real, alocado através de new Data() é chamado objeto. Com base em uma
definição de classe referente a uma classe arbitrária Xxxx, você pode chamar a new Xxxx() para
criar todos os objetos necessários. Eles são separados uns dos outros e possuem sua própria
referência. Essa referência precisará ser armazenada em uma variável, de modo que você possa
utilizar a combinação “variável-ponto-membro” (como minhaData.dia) para a acessar os membros
individuais de cada um dos objetos.

Alocação de memória e layout

No corpo de um método, a declaração

Date today;
today = new Date();

Aloca armazenamento apenas para uma referência:

today ????

A construção aloca e inicialização o armazenamento.

Date today;
today = new Date();

today ????

day 0
month 0
year 0

Por fim, a atribuição configura variável de referência para que ela se refira corretamente ao objeto
recém-criado.

Date today;
today = new Date();

today 0x01abcdef
day 0
month 0
year 0

Atribuição de variáveis de referência

A linguagem Java trata as variáveis que são declaradas como contendo um tipo de classe, como
referência, o que influencia no significado da atribuição. Considere este fragmento de código:

int x = 7;
int y = x;
String s = “Olá!”;

Linguagem de Programação Java 22


String t = s;

Quatro variáveis são criadas: duas do tipo int e duas referências a String. O valor de x é 7,
sendo copiado para y. Tanto x como y são variáveis independentes e as alterações efetuadas em
uma delas não afeta a outra.

Com as variáveis s e t, e existe apenas um objeto String, que contém o texto “Olá”. Tanto s como
t referem-se a esse único objeto.

Inicialização padrão e o valor de referência null

Quando você executa new a fim de alocar memória para um objeto, a linguagem Java a inicializa os
valores no espaço como zero. Em variáveis numéricas, o valor é realmente zero. Em variáveis
boolean, o valor é definido como false. Em referências, isto é, qualquer variável de um tipo de
objeto, é utilizado um valor null especial.

Na linguagem Java, um valor null indica que a referência não se refere a um objeto. Isso permite
que o sistema de tempo de execução detecte a utilização dessa referência e pare o processo
imediatamente, seja qual for a plataforma, evitando a ocorrência de danos. Mais detalhes sobre esse
comportamento serão apresentados mais adiante.

Observação - A inicialização padrão ocorre em variáveis membro, mas não de variáveis locais ou
automáticas de método. Ocorrerá um erro no tempo de compilação se você tentar utilizar uma
variável automática antes que ela tenha recebido um valor. No entanto, você pode atribuir o valor
null a uma variável.

Os programadores de C e C++ devem tomar cuidado, pois, na linguagem Java, o literal null é
escrito em letras minúsculas, ao contrário de seu equivalente naquelas linguagens.

Linguagem de Programação Java 23


Revisão de terminologia
Alguns termos foram apresentados neste módulo. O objetivo desta seção é revisá-los brevemente.

Tipo agregado de dados

Um nome (um tanto matemático) que um programador utiliza para criar um modelo. O modelo é
construído através da inclusão de variáveis que descrevem os vários aspectos que especificam o
estado da entidade em uma única definição. Uma vez definidas, as variáveis poderão ser criadas por
meio de um nome de tipo. Em vez disso, algumas linguagens utilizam os termos registro ou tipo
estruturado. A linguagem Java utiliza o termo classe.

Classe

Uma versão em linguagens orientadas a objetos de um tipo agregado de dados. Estritamente, uma
classe consiste em um super conjunto da idéia de um tipo agregado de dados. No entanto, os
diversos recursos adicionais que a tornam uma classe, em vez de um tipo agregado de dados, ainda
não foram abordados.

Objeto

Uma instância real de uma classe. A classe pode ser considerada com um gabarito - um modelo do
objeto que você está descrevendo. Um objeto é o que você obtém toda vez que cria uma instância
de uma classe.

Membro

Membro é um dos elementos que constituem um objeto. O termo também é utilizado para os
elementos da classe de definição. Os termos variável membro, instância de variável ou campo
também são utilizados como sinônimos.

Referência

Na linguagem Java, uma variável que é definida como contendo um tipo de classe, na verdade, não
contém os dados do objeto. em vez disso, ela consiste em um meio de identificar um objeto
específico. Esse tipo de variável é chamado referência.

Linguagem de Programação Java 24


Capítulo3

Expressões e controle de fluxo

Objetivos

Após concluir este módulo, você será capaz de:


• Distinguir entre variáveis membro e variáveis automáticas.
• Descrever a inicialização de variáveis membro.
• Reconhecer e corrigir um erro de compilação “Possível referência antes
da atribuição”.
• Reconhecer, descrever e utilizar os operadores da linguagem Java
(exceto o instanceof).
• Distinguir entre atribuições válidas e inválidas de tipos primitivos.
• Reconhecer expressões boolean e determinar o requisito referente a
elas em construções de controle
• Utilizar adequadamente as construções if, switch, for, while, e do
e os formatos rotulados de break e continue.

Linguagem de Programação Java 25


Expressões
Variáveis e vida útil

Você já conhece as duas maneiras como as variáveis podem ser descritas: através de tipos simples
como int e float ou através de tipos de classes definidos pelo programador. Você também já
sabe que duas variáveis locais podem ser declaradas: dentro de um método ou dentro da definição
de classe como um todo.

As variáveis definidas dentro de um método (você verá mais adiante que o método main() não é o
único que pode ser definido) são chamadas variáveis automáticas, locais, temporárias ou de pilha,
dependendo da preferência do usuário.

As variáveis membro são criadas quando o objeto é construído através do método new Xxxx ().
Essas variáveis continuam a existir, enquanto o objeto seja necessário.

Observação – Parte do sistema chamada “garbage collector” destrói os objetos não mais
necessários. Em geral, isso acontece sem a intervenção do programador.

As variáveis automáticas são criadas quando a execução entra no método e são destruídas quando
saem desse método. Esse é o motivo pelo qual, às vezes, elas são chamadas “temporárias”.

Inicialização de variáveis

Nenhuma variável em um programa Java pode ter um valor indefinido. Quando um objeto é criado,
as variáveis membro são inicializadas com os seguintes valores, no momento em que o
armazenamento é alocado:

byte 0
short 0
int 0
long 0l
float 0.0f
double 0.0d
char ‘\u0000’
boolean false
Todos os tipos de referência null

Observação – Uma referência que contém o valor nulo não se refere a nenhum objeto. Uma
tentativa de utilizar o objeto ao qual se refere causará uma exceção. As exceções consistem em
erros que ocorrem em tempo de execução e serão abordadas em um módulo posterior.

As variáveis automáticas devem ser inicializadas antes de serem utilizadas. O compilador estuda o
código para determinar que cada variável seja definitivamente inicializada antes de ser utilizada pela
primeira vez. Se o compilador não puder determinar isso, ocorrerá um erro de compilação.

Linguagem de Programação Java 26


int x = (int) (Math.random() * 100);
int y;
int z;
if (x > 50) {
y = 9;
}
z = y + x; // A possível utilização antes da inicialização
// causa um erro de compilação

Operadores

Os operadores da linguagem Java são bastante semelhantes em estilo e função aos utilizados nas
linguagens C e C++. A tabela a seguir lista os operadores em ordem de precedência (“L até R”
significa associativo da esquerda para a direita; “R até L” significa associativo da direita para a
esquerda):

Separador . [] () ; ,

R até L ++ -- + - ~ ! (cast_operator)
L até R * / %
L até R + -
L até R << >> >>>
L até R < > <= >= instanceof
L até R == !=
L até R &
L até R ^
L até R |
L até R &&
L até R ||
R até L ?:
R até L = *= /= %= += -= <<= >>= >>>= &= |=

Observação – O operador instanceof é exclusivo da linguagem Java e será descrito em um módulo


posterior.

Expressões lógicas

A maioria dos operadores também se encontra em outras linguagens e se comporta de maneira


esperada.

Os operadores relacionais e lógicos retornam um resultado booleano. Não há conversão automática


de int para boolean:

int (i) // gera um erro de compilação


if (i != 0) // Correto

Concatenação de sequências com +

O + operador executa a concatenacão de objetos String, produzindo uma nova String.

String handle = “Prof.”;


String name = “Primeiro Nome “ + “Último Nome”;
String title = handle + name;

Linguagem de Programação Java 27


Observação – se um dos argumentos do operador for um objeto String, o outro será convertido
em uma String. Todos objetos podem ser convertidos automaticamente em uma String, embora
o resultado possa ser um tanto confuso.

Operadores lógicos curtos

Os operadores && e || executam expressões lógicas curtas. Considere este exemplo:

String unset = null;


if ((unset != null) && (unser.lenght() > 5)) {
// executa uma ação com unset
}

A expressão booleana que forma o argumento para a instrução if () é válida e totalmente segura.
Isso ocorre porque a primeira subexpressão (unset != null) é falsa, o que é suficiente para
provar que toda a expressão é falsa. Por isso, o operador && ignora o cálculo desnecessário de
(unset.lenght() > 5). Como isso não é calculado, uma exceção de ponteiro nulo é evitada. Da
mesma forma, se o operador || for utilizado e a primeira expressão retornar verdadeiro, a segunda
não será calculada, pois já será considerada como totalmente verdadeira.

Observação – Para evitar o comportamento de avaliação curta, basta utilizar os operadores & e |

Operadores de deslocamento pra a direita >> e >>>

A linguagem Java fornece dois operadores de deslocamento para a direita.

O operador comum >> executa um deslocamento aritmético para a direita. Em geral, o resultado
desse deslocamento é que o operando é divido pelo dobro do número especificado pelo segundo
operando. Por exemplo:

128 >> 1 fornece 64


256 >> 4 fornece 16
-256 >> 4 fornece –16

Observação – se você compreender a notação complementar de dois, o operador >> resultará no


bit de sinal ser copiado durante o deslocamento.

O operador de deslocamento para a direita >>> lógico ou sem sinal trabalha com o padrão de bits
de um valor, em vez de considerar seu significado aritmético, e sempre inclui zeros nos bits mais
significativos. Por exemplo:

1010 ... >> 2 fornece 111010 ...


1010 ... >>> 2 fornece 001010 ...

Observação – Os operadores de deslocamento reduzem o respectivo módulo 32 do operando


direito referente a um operando esquerdo do tipo int e o módulo 64 referente a um operando do
tipo long. Assim, em qualquer int x, x >>> 32 resultará em um x inalterado, e não em zero
como você pode ter esperado.

Observação – É importante observar que o operador >>> só é permitido em tipos integrais e só terá
efeito em valores int ou long. Se for utilizado em um valor de byte ou curto, o valor será elevado,
com uma extensão de sinal, a um inteiro antes de >>> ser aplicado. Nesse ponto, o deslocamento
sem sinal normalmente se torna um deslocamento com sinal.

Elevação e cast de expressões

Linguagem de Programação Java 28


A linguagem Java não suporta o cast arbitrário de tipos de variáveis. Você deve utilizar um cast
explícito para fazer o cast entre tipos de variáveis. Pode haver o cast de variáveis e expressões para
formas mais amplas, porém não mais restritas. Portanto, uma expressão int pode ser tratada
como long, mas uma expressão longa não pode ser tratada como int sem uma cast explícito.

long bigval = 6; // 6 é um tipo int, OK


int smallval = 99L; // 99L é um long, inválido

float z = 12.414F; //12.414F é float, OK


float z1 = 12.414; //12.414 é double, inválido

Cast

Onde os valores não são compatíveis em termos de atribuição, um cast pode, às vezes, ser utilizado
para persuadir o compilador a reconhecer uma atribuição. Isso pode se feito, por exemplo, para
“comprimir” um valor longo em uma variável do tipo inteiro. O cast explícito é feito da seguinte
maneira:

Long bigvalue = 99L;


Int squashed = (int) (bigvalue);

Observe que o tipo desejado é colocado entre parênteses e utilizado como um prefixo da expressão
que deve ser modificada. Embora não seja necessário, é geralmente aconselhável colocar
parênteses toda a expressão em que haverá o cast, pois, caso contrário, a precedência da operação
de cast poderá causar problemas.

Observação – Lembre-se de que a faixa de short é de –215 até 2 15e a faixa de char é de 0 até 216
-1. Por isso, a atribuição entre short e char sempre necessitará de um cast explícito.

Linguagem de Programação Java 29


Controle de Fluxo
Instruções de desvio

Instruções if, else

Esta é a sintaxe básica de instruções if, else


If (expressão_booleana)
Declaração_ou_bloco
Else
Declaração_ou_bloco

Exemplo

1 int count;
2 count = getCount(); // um método definido no programa
3 if (count < 0) {
4 System.out.println(“Erro: valor de count é negativo!”);
5 }
6 else {
7 System.out.println(“Haverá “ + count +
8 “ pessoas para o almoco hoje.”);
9 }

A linguagem Java difere da C/C++ pelo fato de um método if () utilizar uma expressão boolean
em vez de um valor numérico. Lembre-se de que os tipos boolean e os tipos numéricos não podem
ser convertidos e nem sofrer cast. Portanto:

if (x) // x é inteiro

É inválido, você deve utilizar:

if (x!=0)

Instrução switch

Esta é a sintaxe da instrução switch:

Switch (expr1)
case expr2:
declarações;
break;
case expr3:
declarações;
break;
default:
declarações;
break;
}

Observação – Na declaração switch (expr 1), expr1 deve ser compatível em termos de
atribuição a um tipo int. A elevação ocorre com os tipos type, short ou char. As expressões de
ponto flutuante ou do tipo long não são permitidas.

Observação – Só é possível efetuar switch com os tipos de dados short, int, byte ou char.

Linguagem de Programação Java 30


Exemplo

1 switch (colorNum) {
2 case 0:
3 setBackground(Color.red);
4 break;
5 case 1:
6 setBackground(Color.green);
7 break;
8 default:
9 setBackground(Color.black);
10 break;
11 }

Instruções de loop

Loops for

Esta é a sintaxe do loop for:

For (init_expr; boolean; alter_expr3)


declaração_ou_bloco

Exemplo

For (int i=0; i<10; i++) {


System.out.println(“Você já acabou?”);
}
System.out.println(“Finalmente!”);

Observação – A linguagem Java permite o operador vírgula em for (), mas em nenhum outro
lugar.

Por exemplo: for (i=0, j=0; j<10; i++, j++) é válido.

Loops while

Esta é a sintaxe do loop while:

while (boolean)
declaração_ou_bloco

Exemplo

1 int i=0;
2 while (i<10) {
3 System.out.println(“Você já acabou?”);
4 i++;
5 }
6 System.out.println(“Finalmente”);

Loops do

Esta é a sintaxe do loop do

do
Linguagem de Programação Java 31
declaração_ou_bloco
while (boolean);

Exemplo

1 int i=0;
2 do {
3 System.out.println(“Você já acabou?”);
4 i++;
5 } while (i<10);
6 System.out.println(“Finalmente”);

Controle de loops especiais

As instruções a seguir podem ser utilizadas para controlar melhor as declarações de loop:

• break [label];
• continue [rótulo];
• label declaração; //onde declaração deve ser uma
//instrução for, while ou do.

Exemplos

1 loop: while (true) {


2 for (int i=0; i<100; i++) {
3 switch (c = in.read()) {
4 case –1:
5 case ‘\n';
6 //pula para fora do loop while para a linha #12
7 break loop;
8 ...
9 }
10 } //finaliza for
11 } // finaliza while
12
13 test:for (...) {
14 ...
15 while (...) {
16 if (j<10) {
17 //pula para a próxima iteração do loop for na linha #13
18 continue test;
19 }
20 }
21 }

Linguagem de Programação Java 32


Capítulo 4

Matrizes

Objetivos

Após concluir este módulo, você será capaz de:


• Declarar e criar matrizes de tipos fundamentais, de classe ou de
matriz.
• Determinar a necessidade e ser capaz de inicializar os elementos
de um matriz.
• Determinar o número de elementos em uma matriz.
• Criar um código para copiar matrizes.

Linguagem de Programação Java 33


Declarar matrizes

Você pode declarar matrizes de qualquer tipo, seja fundamental ou de classe:

char s [ ];
point p [ ];

Na linguagem Java, uma matriz é uma classe e, como em outras classes, a declaração não cria o
objeto em si. Portanto, essas declarações não criam matrizes. Apenas fazem referência a variáveis
que podem ser utilizadas para se referir a uma matriz.

O formato mostrado acima, com colchetes após o nome da variável, é o padrão das linguagens C e
C++, sendo também utilizado na linguagem Java. Esse formato gera formas complexas de
declaração, cuja leitura pode ser bem difícil. Por isso, Java permite um formato alternativo com
colchetes à esquerda:

char [ ] s;
point [ ] p;

O resultado é que a declaração passa a ter a parte do tipo à esquerda e o nome da variável à direita.
Os dois formatos serão utilizados, mas você deverá optar por um deles e continuar com ele.

Criar matrizes

Você cria matrizes, como todo os objetos, usando a palavra-chave new,da seguinte maneira :

s = new char [20] ;


p = new Point [100] ;

A primeira linha cria uma matriz de 20 valores char. A segunda linha cria uma matriz de 100
variáveis do tipo Point. No entanto, ela não cria 100 objetos Point. Esses objetos devem ser
criados individualmente.

Observação – Outras maneiras mais elegantes de inicializar matrizes serão apresentadas em


breve.

Inicializar matrizes

Quando você cria uma matriz, todos os elementos são inicializados.


No caso da matriz char s, descrita na página anterior, cada valor é inicializado como o caractere
zero (\0000 – nulo). No caso da matriz p, cada valor foi inicializado como null, indicando que
(ainda) não se refere a um objeto Point. Após a atribuição p [0] = new Point(); o primeiro
elemento do matriz refere-se a um objeto Point real.

Observação – a inicialização de todas as variáveis, incluindo os elementos de matrizes, é essencial


para a segurança do sistema. Nenhum elemento deve ser utilizado em um estado não inicializado,
pois o compilador não consegue verificar o fluxo dos elementos da matriz.

A linguagem Java permite uma abreviação que criará matrizes com valores iniciais:

String names[] = {
“Georgiana”,
“Jean”,
“Silvia”,

Linguagem de Programação Java 34


“Carlos”
};

A linha anterior de código contida no exemplo anterior equivale ao seguinte:

String names[];
names[0] = “Georgiana”;
names[1] = “Jean”;
names[2] = “Silvia”;
names[3] = “Carlos”;

Esta abreviação pode ser utilizada para qualquer tipo de elemento e exige uma referência a cada
elemento em cada posição no bloco de inicialização. Por exemplo:

Color palette[] = {
Color.blue,
Color.red,
Color.white
};

Matrizes multidimensionais

A linguagem Java não fornece matrizes multidimensionais, mas como uma matriz pode ser
declarada e ter qualquer tipo de base, é possível criar matrizes de matrizes (de matrizes, de
matrizes etc.)

int twoDim [][] = new int [5][];


twoDim[0] = new int[5];
twoDim[1] = new int[5];

O objeto criado através de new é simplesmente uma matriz. A matriz contém cinco elementos. Cada
um desses elementos é uma referência null a um elemento do tipo array of int.Portanto,
cada um dos quatro elementos deve ser construído separadamente. Cada um deles é um array
of int.

Observação – Embora o formato da declaração permita colocar os colchetes à esquerda ou à


direita do nome da variável, essa flexibilidade não se aplica a outros aspectos da sintaxe de matriz.
Por exemplo, new int[][4]não é válido.

Em virtude dessa separação, criar matrizes de matrizes não-retangulares. Isto é, os elementos de


twoDim podem ser inicializados da seguinte maneira:

twoDim[0] = new int[2];


twoDim[1] = new int[4];
twoDim[2] = new int[6];
twoDim[3] = new int[8];
twoDim[4] = new int[10];

Como esse tipo de inicialização é tediosa e a matriz de matrizes retangular é o formato mais comum,
a linguagem Java fornece uma abreviação para criar matrizes bidimensionais. Por exemplo,

int matrix[][] = new int[4][5]

pode ser utilizado para criar uma matriz de quatro matrizes, contendo cinco inteiros cada.

Limites da matriz

Linguagem de Programação Java 35


Na linguagem Java, todos os subscritos começam com z ero.

O número de elementos em uma matriz é armazenado como parte do objeto de matriz. O valor
utilizado para executar a verificação de limites de todos os acessos em tempo de execução. Se
houver um acesso fora dos limites, ocorrerá uma exceção. As exceções constituem o tema de um
próximo módulo.

O tamanho de uma matriz pode ser determinado em tempo de execução através da variável
membro length. Por exemplo, para iterar uma matriz, o código deve ser utilizado desta forma:

int list[] = new int[10];


for (int i = 0; i < list.length; i++)
// realiza o trabalho.

Observe que o limite do loop é determinado através da comparação com o list.length, em vez
de com valor imediato 10. Esse método é mais eficiente em face da manutenção do programa.

Copiar matrizes

Uma vez criada, um matriz não poderá ser redimensionada. No entanto, você pode utilizar a mesma
variável de referência para se referir a uma matriz totalmente nova:

int elements[] = new int[6];


elements = new int[10];

Nesse caso, a primeira matriz é efetivamente perdida, a menos que alguma outra referência a ela
seja mantida em outro local.

É possível copiar uma matriz com eficiência tolerável. A linguagem Java fornece um método
especial na classe System: o método arraycopy(), que pode ser utilizado da seguinte maneira:

// matriz original
int elements[] new int { 1, 2, 3, 4, 5, 6 };
:
// nova matriz mais extensa
int hold[] = new int { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 };
// copia tudo da matriz elements para a matriz
// hold
// iniciando com índice de ordem 0
System.arraycopy(elements, 0, hold, 0, elements.lenngth);

Nesse ponto, a matriz hold possui este conteúdo: 1,2,3,4,5,6,4,3,2,1.

Linguagem de Programação Java 36


Capítulo 5

Objetos e classes

Objetivos

Após concluir este módulo, você será capaz de:


• Descrever a noção de um tipo abstrato de dados.
• Escrever código para definir um método.
• Utilizar a palavra-chave this para acessar membros de dados em um
método.
• Escrever código para chamar um método em um determinado objeto.
• Utilizar a palavra-chave this para acessar o objeto atual.
• Descrever o efeito do modificador private em uma variável membro.
• Criar construtores de classes e chamar construtores específicos
utilizando new com argumentos.
• Descrever e utilizar a divisão em subclasses.
• Criar e utilizar coleções heterogêneas.
• Criar e utilizar métodos que aceitam tipos de argumento genéricos.
• Criar métodos subscritores em uma subclasse e descrever o fluxo de
execução ao executar um método sobrescrito.
• Chamar métodos e construtores sobrecarregados.
• Chamar métodos sobrescritos e descrever como o método é escolhido.
• Compreender a utilização de declarações de importação para o acesso a
bibliotecas.

Linguagem de Programação Java 37


Tipos abstratos de dados
Quando os itens de dados constituem tipos agregados de dados, como uma data, você pode definir
vários bits de programa que operam especificamente nesse tipo de dados. Isso não é um novo
conceito. Quando uma linguagem de programação define um conceito primitivo, como um inteiro,
também define diversas operações que ele pode executar em instancias desse tipo, como adição,
subtração, multiplicação e divisão.

Em várias linguagens de programação, uma vez definido um tipo agregado de dados, o programador
simplesmente define funções utilitárias para operar em variáveis desse tipo, sem qualquer
associação específica entre o código agregado, exceto talvez na convenção de atribuição de nome.
Portanto, pode ser que você encontre uma função apenas chamada tomorrow(), que deve conter
um argumento e uma variável do tipo Date.

Algumas linguagens de programação, incluindo Java, permitem uma associação mais segura entre
a declaração de um tipo de dados e a declaração do código que deve operar em variáveis desse
tipo. Em geral, essa associação é descrita como um tipo abstrato de dados.

Na linguagem Java, você pode criar uma associação entre o tipo date e a operação tomorrow da
seguinte maneira:

public class Data {


private int dia, mes, ano;
public void amanha() {
// código para incrementar o dia
}
}

Na linguagem Java, você pode se referir ao código denominado tomorrow como um método,
embora também possa encontrar termos função membro e função.

Observação – Não se preocupe sobre o significado da palavra private nessa declaração, pois ela
será descrita mais adiante.

Nas linguagens que não suportam a associação entre dados e código, você poderá observar que a
declaração do método tomorrow especifica a data especifica que deve ser aumentada em um dia.
Por exemplo:

void amanha(Data d);

As linguagens, como Java, que suportam tipos de dados criam uma associação mais segura entre
os dados e o código. Não descrevemos um método de acordo com sua operação em um fragmento
de dados.
Em vez disso, preferimos considerá-lo como se ele soubesse como se auto-modificar. Em seguida,
solicitamos que os dados executem uma operação em si próprios, da seguinte maneira:

Data d = ?????;// Um objeto de data inicializado


d.amanha();

Essa notação reflete a idéia de que o comportamento é realizado pelos dados e não de outra
maneira. Lembre-se de que você pode se referir aos campos da classe Data utilizando a notação de
pontos:

d.dia

Linguagem de Programação Java 38


Isso significa que “o campo de dia do objeto Data continha uma referência feita pela variável d”.
Assim, este é o exemplo anterior: “o comportamento tomorrow do objeto Data com uma referência
feita pela variável d”. Em outras palavras, execute a operação tomorrow no objeto d.

A idéia é de que os métodos são uma propriedade dos dados em vez de construir a etapa principal
para a construção de um sistema orientado a objetos. Às vezes, você ouvirá o termo passagem de
mensagens. É utilizado em algumas linguagens para passar a noção de instruir um item de dados
para executar alguma ação em si próprio. Na verdade, em linguagens que utilizam
convencionalmente essa terminologia, ele geralmente também reflete a natureza da implementação.

Definir métodos

Na linguagem Java, os métodos são definidos através de uma abordagem bastante semelhante à
utilizada em outras linguagens, especialmente C e C++. A declaração assume a seguinte forma:

<modifiers> <return_type> <name> ( <argument_list> ) <block>

O <name> pode ser qualquer identificador válido, com algumas restrições baseadas nos nomes que
já estão sendo utilizados.
O <return_type> indica o tipo de valor retornado pelo método. Se o método não retornar um
valor, ele deve ser declarado como void. A linguagem Java é rigorosa com relação aos valores
retornados. Se a declaração indicar que o método retorna, por exemplo, um inteiro,ele deverá fazê-
lo a partir de todos os caminhos de retorno possíveis.

0 segmento <modifiers> pode conter diversos modificadores, incluindo o public, protected


e private. O modificador de acesso public indica que o método pode ser chamado em qualquer
outro código, ao passo que private indica que um método só pode ser chamado por outros
métodos da classe. Abordaremos mais adiante o modificador protected.

O <argument_list> permite que os valores de um argumento sejam passados para um método.


Os elementos da lista são separados por virgulas, sendo que cada um deles é constituído de um tipo
e um identificador. Por exemplo,

public void addDays(int days)

Instrui o corpo do método a receber um argumento que indica o número de dias a sem adicionados
à data atual. No método, é feita uma referência ao valor através do identificador days.

Linguagem de Programação Java 39


Passar por valor

A linguagem Java só passa argumentos “por valor”; isto é, o argumento não pode ser alterado pelo
método chamado. Quando uma instância de objeto é passada como um argumento para um objeto,
o conteúdo do objeto pode ser alterado no método chamado, mas a referência ao objeto nunca é
alterada.

O seguinte exemplo de código ilustra esse ponto:

1 public class PassTest


2
3 float ptValue
4
5 public static void main (String args[]) {
6
7 String str;
8 int val;
9
10 // cria uma instância da classe
11 PassTest pt = new PassTest ();
12
13 // Atribui o valor int
14 val = 11
15
16 // Tenta alterá-lo
17 pt.changeInt (val)
18
19 // Qual é o valor atual?
20 System.out.println (“Valor Int é:” + val);
21
22 // Atribui a seqüência
23 str = new String (“olá”);
24
25 // Tente alterá-la
26 pt.changeStr
27
28 // Qual é o valor atual?
29 System.out.println (“Valor str e:” + str);
30 // Agora define o ptValue
31 pt.ptValue = 101f;
32
33 // Agora altera o valor do ponto flutuante
34 // Através da referência ao objeto
35 pt.changeObjValue (pt);
36

37 // Qual é o valor atual?


38 System.out.println (“ptValue atual é:” + pt.ptValue);
39
40 }
41
42 // Métodos para alterar os valores atuais
43 public void changeInt (int value) {
44 value = 55;
45 }
46
47 public void changeStr (String value) {
48 value = new String (“diferente”);

Linguagem de Programação Java 40


49 }
50
51 public void changeObjValue (PassTest ref) {
52 ref.ptValue = 99f;
53 }
54}

Esta é a aparência da saída:

% Java PassTest
Valor Int é: 11
Valor Str: olá
PtValue atual é: 99.0

O objeto String não é alterado por changeStr(), mas o conteúdo do objeto PassTest pode ser
alterado.

A referência this

Em uma função tradicional, é feita referência aos dados a serem manipulados, através do
argumento com nome contido na função. Na linguagem Java, não há argumentos com nome. Em
vez disso, você pode fazer referência ao objeto-alvo da operação, utilizando a palavra chave this.
Por exemplo:

public class Data {


private int dia, mes, ano;
public void amanha() {
this.dia = this.dia + 1;
// prosseguir com o código...
}
}

A linguagem Java associa automaticamente todas as referências a variáveis e métodos à palavra-


chave this. Portanto, a utilização da palavra-chave é redundante em circunstâncias como essa. O
código seguinte equivale a:

public class Data {


private int dia, mes, ano;
public void amanha() {
dia = dia + 1; // não há ‘this.’ Antes de ‘day’
// prosseguir com o código...
}
}

Há situações em que a palavra-chave this não é redundante. Por exemplo, convém chamar um
método em uma classe totalmente separada e passar a instância do objeto como um argumento.
Por exemplo:

Aniversario bDia = new Aniversario (this);

Ocultar dados

A utilização da palavra-chave private na declaração de dia, mes e ano na classe Data torna
impossível acessar esses membros em qualquer código, exceto nos métodos da própria classe
Data. Assim, com base nessa declaração da classe Data, o seguinte código é inválido:

Linguagem de Programação Java 41


public class DataUser {
public static void main (String args[]) {
Data minhadata = new Data();
minhadata.dia = 21; // ilegal!
}
}

Pode parecer que impedir o acesso direto a essas variáveis de dados seja uma ação peculiar para
ser executada deliberadamente. Porém, na verdade, traz várias vantagens para a qualidade do
programa que utiliza a classe Data. Como os itens individuais dos dados são inacessíveis, a única
maneira de ler e gravar neles é através de métodos. Por isso, se seu programa exigir uma
consistência interna dos membros da classe, isso poderá ser gerenciado pelos métodos da própria
classe.

Considere uma classe Data que permita um acesso externo arbitrário a seus membros. Será muito
mais fácil para o código executar qualquer uma destas ações:

Data d = new Data();


d.dia = 32;
d.mes = 2; d.dia = 30; // plausível mas, incorreto
d.mes = d.mes + 1; // omitir a variação para prosseguir

Se os membros de dados de uma classe não forem expostos, o usuário da classe será forçado a
modificar as variáveis membro por meio de métodos. Como esses métodos constituem código real,
podem executar verificações de validade. Considere o seguinte método como parte da classe Data:

public void setDia(int novoDia) {


if (novoDia > this.diasEmMes()) {
System.out.println (“dia inválido” + novoDia);
}
else {
this.dia = novoDia;
}
}

O método verifica se o dia que ele deve definir é, de fato, válido. Se não for, ele ignorará a
solicitação e imprimirá uma mensagem. Até agora, no entanto, você pode ver que essa classe Date
é efetivamente protegida contra datas com valores excessivos.

Observação – No exemplo, presume-se que diasEmMes(), seja um método da classe Date.


Conseqüentemente, ele possui um valor this do qual pode extrair o mes e ano que são necessários
para responder à consulta. Como nas variáveis membro, a utilização desse procedimento para
qualificar o método diasEmMes() é redundante nesse caso.

As regras que determinam como um método pode ser chamado adequadamente (como,”o valor do
argumento deve se encontrar na faixa válida de números de dias referente ao mês do objeto”) são
denominados pré-condições. A utilização cuidadosa de testes de pré-condição pode facilitar
bastante a reutilização de uma classe, além de torna-la bem mais confiável para tal, pois, se algum
método tiver sido utilizado incorretamente, o programador que estiver trabalhando com a classe
saberá disso imediatamente.

Encapsulamento

Além de proteger os dados de um objeto contra qualquer modificação imprópria, fazer com que o
usuário acesse os dados através de um método permitirá que uma classe seja reutilizada com mais

Linguagem de Programação Java 42


simplicidade, garantindo que os efeitos colaterais necessários sejam tratados adequadamente. No
caso da classe Data, por exemplo, considere como um método amanha deve ser construído.

Se os dados estiverem totalmente acessíveis, cada usuário da classe terá que incrementar o valor
do dia, compara-lo com o número de dias do mês atual e tratar as condições de fim de mês (e
possivelmente fim de ano), em que o aumento de um dia significa também haver alteração no mês
e/ou o ano. Esse procedimento é tedioso e suscetível a erros. Embora essas necessidades possam
ser bem compreendidas com relação a datas, pode ser que outros tipos de dados tenham restrições
semelhantes que não sejam tão conhecidas. Ao forçar o usuário da classe a utilizar o método
amanha() fornecido, todos poderão ter certeza de que os efeitos colaterais necessários sempre
serão tratados de forma consistente e correta.
Esse aspecto da ocultação de dados é geralmente chamado encapsulamento.

Sobrecarregar nome de métodos


Em algumas circunstâncias, convém criar vários métodos na mesma classe, cujo processo é
basicamente o mesmo em diferentes argumentos. Considere um método simples que se destina a
apresentar a saída de uma representação textual do respectivo argumento. Esse método pode se
chamar print().

Agora, suponha que você precise de um método diferente para imprimir os tipos int, float e
String. Isso é razoável , já que os vários tipos de dados necessitarão de uma formatação diferente
e, provavelmente, de um tratamento diferente. Você pode criar três métodos, denominados
printint(), printfloat() e printString(), respectivamente. No entanto, isso é tedioso.

A linguagem Java, da mesma forma que várias outras linguagens, permite que você reutilize um
nome de método em mais de um método. Obviamente, isso só poderá funcionar se, nas
circunstâncias em que a chamada for feita, houver um meio para distinguir qual método é realmente
necessário. No caso dos três métodos de impressão, é possível fazer essa condição com base nos
argumentos. Ao reutilizar o nome do método, chegamos a estes três métodos:

public void print(int i)


public void print(float f)
public void print(String s)

Quando você cria um código para chamar um desses três métodos, o que for apropriado será
escolhido de acordo com o tipo de argumento fornecido.

Duas regras se aplicam aos métodos sobrecarregados:


- A lista de argumentos da instrução de chamada devem ser diferentes o bastante para
permitir a determinação clara do método de chamada adequado. Elevações normais de
ampliação(por exemplo, de float para double) poderão ser aplicadas, causando confusão sob
algumas condições.
- O tipo de retorno dos métodos pode ser diferente, porém não consistirá na única diferença.
As listas de argumentos das varias sobrecargas precisam ser diferentes.

Linguagem de Programação Java 43


Construir e inicializar objetos
Já vimos como você deve executar uma chamada ao método new xxx() a fim de alocar espaço
para um novo objeto. Além disso, vimos que, às vezes, os argumentos podem ser colocados entre
parênteses, por exemplo, new Button(“Pressione”). Se você chamar o construtor, poderão
ocorrer as seguintes situações:

1. Primeiro, o espaço para o objeto é alocado e o padrão é inicializado. Na linguagem Java,


essa fase é indivisível para garantir que não haja objeto contendo valores aleatórios.
2. Em seguida, qualquer inicialização explícita é executada.
3. Por fim, um construtor, bastante semelhante a um método especial, é executado.

Esta seção analisa essas duas ultimas fases.

Inicialização explícita de membro

Se você inclui expressões simples de atribuição nas declarações membro, poderá executar uma
inicialização explícita de membro durante a construção de seu objeto.

public class Initialized {


private int x = 5;
private String name = “Fred”;
private Date created = new Date();
// Os métodos de acesso entram aqui
...
}

Construtores

O mecanismo de inicialização explícita descrita fornece uma maneira simples de definir os valores
iniciais de campos em um objeto. Ás vezes, no entanto, você realmente precisa executar um método
para realizar a inicialização. Pode ser que você precise tratar exceções possíveis com try/catch.
Convém utilizar loops ou condicionais para determinar a inicialização. Também é aconselhável
passar argumentos para o processo de construção, deforma que o código que solicita a construção
do novo objeto possa controlar o objeto criado. Objetos como botões são exemplos, pois precisam
utilizar uma seqüência de texto como rótulo.

Na verdade, o terceiro estágio da construção de um novo objeto consiste em chamar um método de


inicialização. Esse método é convencionalmente chamado construtor, embora a construção já tenha
sido executada.

Para criar um método que se chame construtor, você deve seguir estas duas regras:
1. O nome do método deve corresponder exatamente ao nome da classe.
2. Não deve haver um tipo de retorno declarado para o método.

Por exemplo:

public class Xyz {


// variáveis membro
public Xyz() {
// configura o objeto
}
public Xyz(int x) {
// configura o objeto com um parâmetro
}
}

Linguagem de Programação Java 44


Observação – Como os métodos, você pode sobrecarregar o nome do construtor fornecendo vários
construtores com diferentes listas de argumentos. Quando você emite uma nova chamada
Xyz(argument_list), a lista de argumentos determina o construtor que deve ser utilizado.

O construtor padrão

Dissemos que toda classe possui pelo menos um construtor, mas antes desta seção, não criamos
construtores para nenhuma de nossas classes.
Na verdade, se você não criar nenhum construtor, a linguagem Java lhe fornecerá um. Esse
construtor não contém argumentos e possui um corpo vazio.

O construtor padrão permite que você crie instâncias de objetos com new Xxx(). Caso contrário,
você terá de fornecer um construtor para cada classe.

Observação – É importante saber que, se adicionar uma declaração de construtor com argumentos
a uma classe que não continha construtores explícitos, você perderá o construtor padrão. Nesse
ponto, se você chamar o método new Xxx(), causará erros de compilação.

Linguagem de Programação Java 45


Divisão em subclasses / Herança
A relação “é um”

Em programação, você normalmente cria um modelo de algo, por exemplo, um funcionário e, em


seguida, precisa de uma versão mais especializada desse modelo original. Por exemplo, vamos
supor que desejamos criar um modelo para um gerente. Na verdade, um gerente é um funcionário,
porém com recursos adicionais.

Considere os seguintes exemplos de declarações de classe que demonstram isso:

public class Employee {


private String name;
private Date hireDate;
private Date dateOfBirth;
private String jobTitle;
private int grade;
...
}
public class Manager {
private String name;
private Date hireDate;
private Date dateOfBirth;
private String jobTitle;
private int grade;
private String departament;
private Employee [] subordinates;
...
}

Esse exemplo ilustra a duplicação entre a classe Manager e a classe Employee. Na realidade, há
diversos métodos que podem ser aplicáveis ao Employee e, provavelmente, um conjunto deles
também seria necessário ao Manager.

Portanto, o que precisamos é de uma maneira de criar uma nova classe a partir de uma existente.
Isso é chamado divisão em subclasses ou herança.

A palavra-chave extends

Em linguagens orientadas a objeto, são fornecidos mecanismos especiais para permitir que o
programador defina uma classe em termos de uma classe anteriormente definida. Na linguagem
Java, isso é feito através da palavra-chave extends, da seguinte maneira:

public class Employee {


private String name;
private Date hireDate;
private Date dateOfBirth;
private String jobTitle;
private in grade;
private String departament;
private Employee [] subordinates;
...
}
public class Manager extends Employee {
private String Departament;
private Employee [] subordinates;
...

Linguagem de Programação Java 46


}

Nessa organização, a classe Manager é definida para ter todas as variáveis e métodos contidos em
um Employee. Todas essas variáveis e métodos são herdados da definição classe pai. Tudo o que
o programador precisa fazer é definir os recursos adicionais ou como veremos brevemente,
especificar as alterações que devem ser aplicadas.

Observação – Esse método representa uma grande melhoria em termos de manutenção e


confiabilidade. Se uma correção for efetuada na classe Employee, a classe Manager será corrigida
sem intervenção do programador.

Herança única

A linguagem Java permite que uma classe origine apenas uma outra. Essa restrição é chamada
herança única. O debate sobre os méritos relativos da herança única e múltipla constitui o assunto
de extensas discussões entre programadores de linguagens orientadas a objetos. A linguagem Java
impõe a restrição de uma única herança porque se considera que ela torne o código resultante mais
confiável, embora, às vezes, à custa de dificultar um pouco o trabalho dos programadores.

Os construtores não são herdados

É importante saber que, embora uma subclasse herde todos os métodos e variáveis de uma classe
pai, ela não herda os construtores.

Só há duas maneiras para que uma classe possa obter um construtor:


Você pode criá-lo ou, se você não criar, a classe terá um único construtor padrão.

Polimorfismo

A descrição de uma classe Manager como a classe Employee não é apenas uma maneira fácil de
descrever a relação entre duas classes. Lembre-se que dissemos que a classe Manager obtém
todos os atributos, tanto membros como métodos, da classe pai Employee. Isso significa que
qualquer operação que seja legítima em uma classe Employee também será em uma classe
Manager. Se a Employee tiver os métodos raiseSalary()and fire(), a classe Manager
também os terá.
Isso leva à idéia de que os objetos são poliformicos, ou seja, possuem “várias formas”. Um objeto
específico pode ter a forma de uma classe Manager, mas também tem a forma de uma classe
Employee. Acontece que, na linguagem Java, existe uma classe que é a classe pai de todas as
outras. Essa é a classe java.lang.Object. Assim, na realidade, as definições anteriores eram
abreviações de:

public class Employee extends Object

public class Manager extends Employee

A classe Object define vários métodos úteis, incluindo toString(). É por isso que todos os
elementos da linguagem Java podem ser convertidos em uma representação de seqüência, mesmo
que o resultado seja de uso limitado.

Java, como a maioria das linguagens orientadas a objeto, realmente permite que você se refira a um
objeto com uma variável cujo tipo seja um dos da classe pai. Assim, é válido dizer que:

Employee emp = new Manager();

Linguagem de Programação Java 47


Utilizando a variável emp no estado em que está, você só pode acessar as partes do objeto que
pertençam à classe Employee. As partes específicas da classe Manager ficam ocultas. Isso
acontece porque, no que diz respeito ao compilador, emp pertence à classe Employee e não a
Manager.

Argumentos de método e coleções heterogêneas

Pode parecer irreal criar uma classe Manager e deliberadamente atribuir a referência a ela em uma
variável do tipo Employee. Isso é verdadeiro, mas há razões para que você queira atingir o mesmo
efeito.

Com esse procedimento, pode criar métodos que aceitem um objeto “genérico” , nesse caso, uma
classe Employee e trabalhar adequadamente em qualquer uma das subclasses dela. Portanto, você
pode produzir um método em uma classe de aplicativo que compare um funcionário e determinado
salário limite, para determinar os prováveis impostos relativos a esse salário. Utilizando os recursos
polimórficos, você pode tornar essa tarefa bastante simples:

public TaxRate findTaxRate(Employee emp) {


// efetua os cálculos e retorna uma taxa para emp
}
// Enquanto isso, em outro local na classe de aplicativo
Manager m = new Manager();
:
TaxRate t = findTaxRate(m);

Isso é válido, pois a classe Manager é uma classe Employee.


Uma coleção heterogênea contém elementos distintos. Em linguagens orientadas a objetos, você
pode criar coleções de vários elementos que possuam uma classe antecessora comum. Assim,
podemos criar o seguinte:

Employee[] staff = new Employee[1024];


staff[0] = new Manager();
staff[1] = new Employee();

e assim por diante. Podemos até criar um método de classificação que coloque os funcionários na
ordem de idade ou salário, sem termos de nos preocupar com o fato de alguns deles serem
realmente gerentes.

Observação – Como na linguagem Java toda classe é uma subclasse de Object, você pode
utilizar um array de Object como recipiente de quaisquer objetos. Você só não pode adicionar
variáveis primitivas a essa matriz. No entanto, melhor que uma matriz de Object é a classe
Vector, que foi criada para armazenar coleções heterogêneas de objetos.

O operador instanceof

Como você pode deslocar objetos utilizando referências às respectivas classes pai, ás vezes
convém saber o que você realmente conseguiu obter. Essa é a finalidade do operador instanceof.
Suponha que nossa hierarquia de classes seja ampliada da seguinte maneira:

public class Employee extends Object


public class Manager extends Employee
public class contractor extends Employee

Linguagem de Programação Java 48


Observação – Lembre-se de que, embora totalmente válido extends Object é, na verdade,
redundante. Esse por exemplo só é mostrado como lembrete.

Se você receber um objeto através de uma referência do tipo Employee, pode ser que ele venha a
ser ou não um Manager ou um Contractor. Se desejar, você poderá testá-lo utilizando o operador
instanceof, da seguinte maneira:

public void method(Employee emp) {


if (emp instanceof Manager) {
// chamá-lo ‘Sir’
}
else if (e instanceof Contractor) {
// manter as informações sigilosas da empresa
}
// funcionário; pode-se falar livremente após um drinque
}
}

Efetuar coerção (cast) em objetos

Em circunstâncias em que for feita referência a uma classe pai e for determinado que o objeto seja
realmente uma subclasse específica por meio do operador instanceof, você poderá restaurar a
funcionalidade total do objeto ao efetuar um cast na referência.

public void method(Employee e) {


if (e instanceof Manager) {
Manager m = (Manager) e;
System.out.println(“este é o gerente de “ + m.departament)
}
// resto da operação
}

Se você não conseguir executar o cast, também não conseguirá fazer referência à
e.departament, já que o compilador não conhece nenhum membro chamado departament
pertencente à classe Employee.

Se você não realizar o teste utilizando o operador instanceof, pode ser que ocorra uma falha no
cast. Em geral, qualquer tentativa de efetuar coerção na referência de um objeto estará sujeita a
varias verificações:

- os casts “para cima” na hierarquia de classes são sempre válidas e, na verdade, não exigem
o operador de cast, podendo ser efetuadas através de uma simples atribuição.
- Para casts “para baixo, o compilador precisa estar certo de que o cast seja, pelo menos,
possível. Por exemplo, qualquer tentativa de efetuar coerção em uma referência de Manager
para uma referência de Contractor é definitivamente inválida, já que a classe
Contractor não é uma classe Manager. A classe para a qual a coerção está sendo
efetuada deve ser subclasse do tipo de referência atual”.
- Se o compilador permitiu a coerção, o tipo de referência será verificado no tempo de
execução . se em virtude de a verificação de instaceof ter sido omitida da origem, o objeto
que está sofrendo a coerção não é de fato um objeto do tipo da classe que irá recebê-lo,
ocorrerá uma Exceção. As exceções são uma espécie de erro execução e serão abordadas
em um próximo módulo.

Linguagem de Programação Java 49


Sobrescrever métodos
Da mesma forma que se produz uma nova classe com base em uma já existente, apenas
adicionando outros recursos, também é possível modificar um comportamento existente da classe
pai.

Se um método for definido em uma nova classe de modo que o nome, o tipo de retorno e a lista de
argumentos correspondam exatamente aos contidos em um método de uma classe pai, significa que
o novo método terá sobrescrito o antigo.

Observação – Lembre-se de que se houver o mesmo nome e uma lista de argumentos diferente,
ocorrerá simplesmente uma sobrecarga, e o compilador terá apenas de analisar os argumentos
fornecidos para decidir qual método chamar.

Considere estes exemplos de métodos nas classes Employee e Manager:

public class Employee {


String name;
int salary;

public String getDetails() {


return “Nome: “ + “\n” +
“Salário:” + salary;
}
}

public class Manager {


String departament;

public String getDetails() {


return “Nome:” + “\n” +
“Gerente de” + departament;
}
}

Anteriormente falamos que a classe Manager possui um método getDetails(), por definição,
pois ela o herda da classe Employee. Agora, substituímos ou sobrescrevemos esse método original
e fornecemos outro em seu lugar.

Com base no exemplo anterior e no seguinte cenário:

Employee e = new Employee();


Manager m = new Manager();

Se você solicitar os métodos e.getDetails() e m.getDetails(), chamaremos diferentes


comportamentos. O objeto Employee executará a versão de getDetails() associada à classe
Employee, ao passo que o objeto Manager executará a versão de getDetails() associada à
classe Manager.

O que é menos óbvio é o que acontecerá se houver:

Employee e = new Manager();

Ou algum efeito semelhante, como um argumento de método geral ou um item derivado de uma
coleção heterogênea.

Linguagem de Programação Java 50


Na realidade, e esse é um recurso importante de linguagens orientadas a objetos, você obtém o
comportamento associado ao tipo real (tipo de execução ) do objeto e não tipo da referência (o tipo
de tempo de compilação). Esse é outro recurso importante do poliformismo normalmente
denominado chamada de método virtual.

No exemplo acima, o método e.getDetail() retornará os resultados da classe Manager, que é o


tipo real.

Comparação da Java com outras linguagens

Observação – se você for programador de C++, haverá uma importante distinção a ser delineada
entre a linguagem Java e a C++. Em C++, você só obterá esse comportamento se marcar o método
como virtual na origem. No entanto, nas linguagens orientadas a objetos “puras”, isso não é normal.
É evidente que a C++ faz isso para aumentar a velocidade de execução.

Às vezes, você ouvirá praticantes da programação orientada a objetos falando sobre “passagem de
mensagens”. Uma maneira fácil de pensar sobre a chamada de um método virtual consiste em
imaginar que o método é uma instrução passada, quase verbalmente, para o objeto. Portanto, em
vez de informar a CPU para executar um bit de código, você instruirá um objeto: faça xxx para mim”.
Esse modelo torna fácil compreender que o comportamento sempre será adequado se associado ao
objeto real. Na verdade, isso também descreve muito bem a maneira como o sistema é
implementado em algumas outras linguagens.

Linguagem de Programação Java 51


Agrupar classes
Pacotes (Package)

A linguagem Java fornece o mecanismo de pacote como uma maneira de agrupar classes
relacionadas. Até agora, todos os exemplos pertenciam ao pacote padrão ou sem nome.

Você pode indicar que as classes de um arquivo fonte pertencem a um pacote específico, utilizando
a declaração package.

// classe Employee do departamento financeiro da empresa ABC


package abc.financeDept;

public class Employee {


...
}

A declaração de pacote, se houver, deve se encontrar no inicio do arquivo fonte. Antes dele, você
pode incluir um espaço em branco e comentários, mas nada além disso. Apenas uma declaração de
pacote será permitida e controlará todo o arquivo de origem.

Os nomes de pacotes são hierárquicos e separados por pontos. É normal que os elementos do
nome do pacote se encontrem totalmente em letras minúsculas. A classe, no entanto, geralmente
começa com uma letra maiúscula e não pode utilizar outras maiúsculas para separar as palavras no
nome da classe.

Quando você utilizar uma declaração de pacote, não precisa importar o mesmo pacote ou qualquer
elemento dele. Lembre-se de que a declaração import é utilizada para trazer as classes de outros
pacotes para o espaço de nomes atual. O pacote atual seja explícito ou implícito, sempre fará parte
do espaço de nomes atual.

A declaração import

Na linguagem Java, para trabalhar com recursos de pacotes, você deve utilizar a declaração
import para informa ao compilador onde encontrar as classes utilizadas. Na verdade, o nome do
pacote(por exemplo, abc.FinanceDept) forma parte do nome das classes contidas no pacote.
Você pode se referir à classe Employee como abc.FinanceDept.Employee, mas para
economizar tempo, se você utilizar apenas o nome da classe básica Employee:

import abc.FinanceDept.*;

public class Manager estends Employee {


String departament;
Employee[] subordinates;
}

Observação – As declarações import devem preceder todas as declarações de classe.

Linguagem de Programação Java 52


Layout do diretório e a variável de ambiente CLASSPATH

Os pacotes são “armazenados” em um diretório formado pelo nome do pacote. Por exemplo, o
arquivo Employee.class, descrito na página anterior, existiria no seguinte diretório quando
compilado:

path/abc/FinanceDept

O diretório abc que forma a base dessa hierarquia deve estar localizado em alguma parte de
CLASSPATH.

A variável CLASSPATH não foi utilizada antes, pois, como ainda não foi definida, o comportamento
padrão das ferramentas inclui automaticamente o local padrão das classes de distribuição e o
diretório de trabalho atual. Para acessar os pacotes que se encontram em outros locais, defina
explicitamente a variável CLASSPATH.

O compilador de Java criará o diretório package e moverá o arquivo de classe compilado quando a
opção –d for utilizada.

% javac –d /home/anton/mypackages Employee.java

A variável de ambiente CLASSPATH deve incluir o caminho para o pacote:

CLASSPATH = /home/anton/mypackages:.

Para que o compilador localize a classe abc.FinanceDept.Employee no arquivo


Manager.Java, na página anterior.

Linguagem de Programação Java 53


Linguagem de Programação Java 54
Capítulo 6

Recursos Avançados da Linguagem

Objetivos

Após concluir este módulo, você será capaz de:


• Chamar métodos sobrescritos.
• Chamar construtores sobrescritos.
• Controlar a chamada de construtores de classes pai.
• Declarar e utilizar corretamente variáveis e métodos static.
• Declarar e utilizar corretamente classes, métodos e variáveis final.
• Declarar e utilizar corretamente métodos e interfaces abstratos.
• Descrever e utilizar o controle de acesso padrão e protected.
• Utilizar o sinalizador -deprecation do compilador Java para
determinar os métodos apropriados do JDK a serem utilizados.
• Descrever e utilizar classes internas.

Linguagem de Programação Java 55


Chamar métodos sobrescritos

Em geral, quando você sobrescreve um método, o objetivo real não é substituir o comportamento
existente e sim ampliar esse comportamento de alguma maneira. Para isso, basta utilizar a palavra
chave super.

public class Employee {


String name;
int salary;

public String getDetails() {


return "Nome: " + name + "\nSalário: “ + salary;
}
}

public class Manager extends Employee


String department;
public String getDetails() {
return super.getDetails() + "\nDepartamento: " + department;
}
}

Observe que, se você chamar o formato super.method (), todo o comportamento será ativado,
juntamente com qualquer efeito colateral do método que teria sido chamado se o objeto a que foi
feita referência realmente pertencesse à classe pai. 0 método não precisa ser definido nessa classe
pai. Ele pode ser herdado de uma classe localizada mais acima na hierarquia.

Há duas regras importantes sobre métodos sobrescritos:

• Um método sobrescritor não pode ser menos acessível que o método que ele sobrescreve.
• Um método sobrescritor não pode lançar mais exceções que o método que ele sobrescreve.

Essas duas regras resultam da natureza do polimorfismo combinada com a necessidade de Java ser
"segura em termos de tipo". Considere este cenário inválido:

public class Pai {


public void metodo() {
}
}

public class Filha extends Parent {


private void metodo() {
}

public class UsaAmbas {


public void outroMetodo() {
Pai p1 = new Pai();
Pai p2 = new Filha()
pl.metodo();
p2.metodo();

Se for permitido que o metodo() contido na classe Filha seja privado e o método sobrescrito
contido na classe Pai seja público, o código irá executar. No entanto, na execução, surgira um

Linguagem de Programação Java 56


problema, pois o metodo() executado no contexto de p2 é uma versão Filha de metoodo(), mas
como ele foi declarado como privado, o acesso não será permitido.
Uma lógica semelhante é aplicada no contexto da regra "declarar ou tratar" com relação à proibição
de se lançar mais exceções a partir de um método sobrescritor.

Chamar construtores sobrecarregados


Se houver uma classe com vários construtores, convém duplicar o efeito de um dentro de outro.
Para isso, basta utilizar a palavra-chave this como uma chamada de método:

public class Employee {


String name;
int salary;

public Employee(String n, int s) {


name = n;
salary = s;
}

public Employee(String n) {
this (n, 0) ;
}

public Employee() {
this("Unknown");
}
}

No terceiro construtor, que não possui argumentos, a chamada this ("Unknown") realmente
passa o controle para a versão do construtor que utiliza um argumento String e fornece a String.

Chamar construtores da classe pai


Java é muito organizada com relação à inicialização de objetos, com o intuito de garantir a
segurança. Quando uma instância de um determinado objeto é criada, ocorre a seguinte seqüência
de ações:

1. O espaço de memória é alocado e inicializado com valores "zero".


2. Para cada classe na hierarquia, começando pelo top

a. Uma inicialização explícita é executada.


b. Um construtor é chamado.

O modelo de segurança da linguagem Java exige que os aspectos de um objeto que descreva a
classe pai sejam inicializados antes que a classe filho execute qualquer ação. Em várias
circunstâncias, o construtor padrão (isto é, o construtor sem argumentos) é utilizado para inicializar o
objeto pai.

Você pode controlar a maneira como a classe pai é construída, se necessário.

public class Employee {


String name;
public Employee(String n) {
name = n;
}
}

Linguagem de Programação Java 57


public class Manager extends Employee {
String department;
public Manager(String s, String d) {
super(s);
department = d;
}
}

Observação - A chamada ao super() pode conter qualquer número de argumentos apropriado


para os vários construtores disponíveis na classe pai. No entanto, deve constituir a primeira
declaração no construtor.

Variáveis de classe
Às vezes, é desejável haver uma variável que seja compartilhada entre todas as instâncias de uma
classe. Por exemplo, isso pode ser utilizado como a base para a comunicação entre instâncias ou
para controlar o número de instâncias que foram criadas.

Você pode atingir esse efeito marcando a variável com a palavra-chave static. Essa variável é, às
vezes, chamada variável de classe para que seja diferenciada de uma variável membro ou de
instância.

public class Count {


private int serialnumber;
static int counter = 0;
public Count() {
counter++;
serialnumber = counter;
}
}

Nesse exemplo, todos os objetos criados obtêm um número de série exclusivo, começando em um e
progredindo na ordem crescente. A contagem da variável é compartilhada por todas as instâncias.
Portanto, quando o construtor de um objeto a incrementa, o próximo objeto a ser criado vê o valor
incrementado.

Uma variável estática é semelhante, de alguma forma, a uma variável global contida em outras
linguagens. Java não contém variáveis globais desse tipo, mas uma variável estática é uma única
variável acessível a partir de qualquer instância da classe.

Se uma variável estática for marcada como pública, poderá ser acessada de fora da classe. Para
isso, não é necessário haver uma instância da classe. Você pode fazer referência a ela através do
nome da classe.

public class StaticVar {


public static int number;
}

public class OtherClass {


public void method() {
int x = StaticVar.number;
}
}

Linguagem de Programação Java 58


Métodos de classe
Às vezes, você precisa acessar o código do programa quando uma instância de um objeto
especffico não estiver disponível. Um método que é marcado por meio da palavra-chave static pode
ser utilizado dessa maneira e, às vezes, é chamado método de classe.

public class GeneralFunction {


public static int addUp(int x, int y) {
return x + y;
}
}

public class UseGeneral {


public void method() {
int a = 9;
int b = 10;
int c = GeneralFunction.addUp(a, b);
System.out.println(“addUp() fornece " + c);
}
}

Como um método static pode ser chamado sem qualquer instância da classe à qual pertence, não
existe o valor this. Conseqüentemente, um método static não pode acessar variáveis a não ser
os próprios argumentos e variáveis static. A tentativa de acessar variáveis nãoestáticas causará
um erro de compilação.

public class Wrong {


int x;
public static void main(String args[]) {
x = 9; // ERRO DE COMPILAÇÃO!
}
}

A palavra-chave final
Classes finais

A linguagem Java permite que a palavra-chave final seja aplicada a classes. Se isso ocorrer, a
classe não poderá ser dividida em subclasses. A classe java.lang.String, por exemplo, é uma
classe final. Isso pode ser feito por razões de segurança, pois garante que, se um método
contiver uma referência a uma String, ela será definitivamente uma String, em vez de uma subclasse
que possa ter sido adicionada maliciosamente com um comportamento modificado.

Métodos finais

Os métodos individuais também podem ser marcados como final. Se forem marcados, não será
possível sobrescrever os mesmos. Mais uma vez, isso é feito por razões de segurança, de modo
que a pessoa que chamar um método possa saber que o comportamento resultante será o original e
adequado, e não um comportamento alternativo.

Os métodos declarados como final são eventualmente utilizados para otimização, já que o
compilador pode gerar um código que cause uma chamada direta ao método, em vez de utilizar a
chamada de método comum virtual que envolve uma pesquisa de tempo de execução para decidir
qual método chamar.

Linguagem de Programação Java 59


Os métodos marcados como static ou private são automaticamente final, já que, em nenhum
dos casos, a vinculação dinâmica pode ser aplicada.

Variáveis finais

Se uma variável for marcada como final, ela se tornará uma constante. Qualquer tentativa de
alterar o valor de uma variável final causará um erro de compilação.

Observação - Se você marcar uma variável do tipo referência, que é de qualquer tipo de classe,
como final, essa variável não conseguira fazer referencia a qualquer outro objeto. No entanto, ainda
será possível alterar o conteúdo do objeto.

Classes abstratas
Às vezes, no desenvolvimento de bibliotecas, convém criar uma classe que represente um
comportamento fundamental e definir métodos para essa classe. Contudo, você não poderá
implementar o comportamento nela. Em vez disso, é sua intenção implementar os métodos em
subclasses.

Por exemplo, considere uma classe Drawing. A classe deve representar métodos referentes a
diversos recursos de desenho, mas eles devem ser implementados sem se considerar a plataforma.
Obviamente, não será possível acessar o hardware de vídeo de uma máquina e continuar a ser
independente de plataforma. A intenção é que a classe de desenho defina os métodos que devem
existir, mas subclasses especiais dependentes de plataforma realmente implementarão o
comportamento.

Uma classe como a Drawing, que declara a existência de métodos mas não a implementação, é
comumente denominada classe abstrata. Você pode declarar uma classe abstrata em Java,
marcando-a com a palavra-chave abstract. Cada método que não estiver sendo definido também
deve ser marcado como abstract.

public abstract class Drawing {


public abstract void drawDot(int x, int y);
public void drawLine(int xl, int yl, int x2, int y2) {
// desenhar utilizando o método drawDot() repetidamente.
}
}

Observe que uma classe abstract pode conter métodos não-abstratos e variáveis.

Você não pode construir uma instância de uma classe abstract, exceto indiretamente, construindo
uma instância de uma subclasse dela.

As subclasses da classe abstract devem fornecer implementações para todos os métodos abstratos
nas classes pai. Caso contrário, elas também serão classes abstratas.

É possível declarar variáveis de referência do tipo da classe abstract.

Linguagem de Programação Java 60


Interfaces
Uma interface é uma variação da idéia de uma classe abstract. Em uma interface todos os
métodos são abstract. Nenhum deles pode possuir corpo. Uma interface também não pode
declarar variáveis membro.

A vantagem de uma interface é que ela pode ser utilizada para violar a regra de herança única
da linguagem Java. Embora uma definição de classe possa apenas herdar de uma única classe, ela
pode implementar todas as interfaces necessárias.

A implementação de uma interface é semelhante à divisão em subclasses, exceto pelo fato de a


classe de implementação não herdar o comportamento da definição interface. A ação da classe
resume-se em implementar uma interface específica. Isso significa que é possível chamar
chamar métodos que sejam membros da interface em qualquer instância de um objeto que
implemente essa interface.

Como em classes abstract, é possível utilizar um nome interface como o tipo de uma variável de
referência. Conseqüentemente, a vinculação dinâmica comum ocorrerá. Pode haver cast de
referências para tipos de interface e a partir destes, e o operador instanceof pode ser utilizado
para determinar se um objeto implementa uma interface.

Você utilizará interfaces no tratamento de eventos de AWT.

public class ReactToButton implements ActionListener {


public void actionPerformed(ActionEvent e) {
// reage ao evento
}
}

Controle de acesso avançado


Além de private e public, dois níveis de controle de acesso adicionais são definidos na
linguagem Java para serem utilizados em variáveis membro e métodos. Esses são níveis de
controle de acesso padrão e um nível chamado protected.

Uma variável ou um método terá acessibilidade padrão se não tiver um modificador explícito como
parte da declaração. Tal acessibilidade significa que o acesso é permitido a partir de qualquer
método em qualquer classe que seja um membro do mesmo package.

Uma variável de método marcada com o modificador protected é, na verdade, mais acessível que
um controle de acesso padrão. Um método ou uma variável protected é acessível a partir de
qualquer método em qualquer classe que seja um membro do mesmo pacote. Ele também será
acessível a partir de qualquer método em qualquer subclasse.

Deprecação
Entre o JDK versão 1.0 e o JDK versão 1.1, foi feito um grande esforço para se padronizar os nomes
dos métodos. Como resultado, um número significativo de construtores de classe e chamadas de
método tornaram-se obsoletos. Foram substituídos por nomes de métodos que seguem uma
abordagem mais orientada a objetos e, em geral, facilitam o trabalho do programador.

Por exemplo, no JDK versão 1.0 da classe java.awt.component:

• Alterar/obter o tamanho de um componente:

Linguagem de Programação Java 61


o Resize() e size()

• alterar/obter a caixa delimitadora de um componente:


o reshape () e bounds ()

No JDK versão 1.1 e posteriores do java.awt.Component, os métodos acima são deprecados e


substituídos por métodos que utilizam as palavras-chave set e get para indicar a operação primária
do método:

• setSize() e getSize()
• setBounds () e getBounds()

Atualmente no JDK 1.1.1, os métodos deprecados existem juntamente com novos métodos, mas
essa situação mudará futuramente. Sempre que você estiver movendo códigos do JDK 1.0 para o
JDK 1.1, 1.2 e 1.3 ou mesmo se você estiver utilizando os códigos que funcionavam anteriormente
com o JDK 1.0, convém compilar o código com o sinalizador -deprecation.

C:\> javac -deprecation MyFile.java

O indicador -deprecation relatará quaisquer métodos da classe utilizada que sejam deprecados.
Por exemplo, há uma classe de utilitário chamada DateConverter, que é utilizada para converter
uma data no formato mm/dd/yy no dia da semana:

1 package myutilities;
2 import java.util.*;
3 public final class DateConverter
4
5 private static String day_of_the_week = {"Domingo",
"Segunda", "Terça", "Quarta", "Quinta", "Sexta", "Sábado");
6
7 public static String getDayOfWeek (String theDate) {
8 int month, day, year;
9
10 StringTokenizer st =
11 new StringTokenizer (thedate, "/");
12 month = Integer.parseInt(st.nextToken ());
13 day = Integer.parseInt(st.nextTokeno);
14 year = Integer.parseInt(st.nextTokeno);
15 Date d = new Date (year, month, day);
16
17 return (day_of_the-week[d.getDay()]);
18 }
19 }

Quando compilada no JDK 1.1 com o sinalizador -deprecation:

C:\> javac -deprecation DateConverter.java


DateConverter.java:20: Note: The constructor java.util.Date(int,int,int)
has been deprecated.
Date d = new Date (year, month, day);

DateConverter.java:23: Note: The method int getDay() in class


java.util.Date has been deprecated.
return (day_of_the-week[d.getDayo]);

Note: DateConverter.java uses a deprecated API. Please consult the


documentation for a better alternative.
Linguagem de Programação Java 62
3 warnings

A classe DateConverter recriada tem a seguinte aparência:

1 package myutilities;
2 import java.util.*;
3 public final class DateConverter {
4
5 private static String day__of-the week [] = {"Domingo", "Segunda",
"Terça", "Quarta", "Quinta","Sexta", "Sábado");
6
7 public static String getDayOfWeek (String thedate){
8 Date d = null;
9 SimpleDateFormat sdf =
10 new SimpleDateFormat ("MM/dd/yy");
11 try {
12 d = sdf.parse (theDate);
13 } catch (ParseException e) {
14 System.out.println (e);
15 e.printStackTrace();
16 }
17 //Criar um objeto GregorianCalendar
18 Calendar c =
19 new GregorianCalendar (TimeZone.getTimeZone("EST"), Locale.US);
20 c.settime (d);
21 return (day-of-the-week[(c.get(Calendar.DAY_OF_WEEK)-1)]);
22 }
23}

Nesse caso, a versão 1.1 utiliza duas novas classes: SimpleDateFormat, que é uma classe
utilizada para transformar qualquer formato de String em um objeto Date e a classe
GregorianCalendar, que é utilizada para criar um calendário com o fuso horário local e a
localidade.

Classes internas / Inner Classes


As classes internas, também chamadas classes aninhadas, constituem um novo recurso do IDK 1.1.
Em versões anteriores do JDK, apenas classes de nível superior eram permitidas e um arquivo Java
só podia conter classes que fossem membros do pacote declarado.

Agora, o JDK 1.1 suporta classes que são membros de outras classes definidas localmente com um
bloco de instruções ou anonimamente em uma expressão. As classes internas possuem as
seguintes propriedades:

• O nome da classe só pode ser utilizado no escopo definido, exceto quando utilizado em um
nome qualificado. O nome da classe interna deve diferir da classe delimitadora.

• A classe interna pode utilizar variáveis de classe e de instância de classes delimitadores e


variáveis locais de blocos delimitadores.

• A classe interna também pode ser definida como abstract.

• Ela também pode ser urna interface implementada por outra classe interna

Linguagem de Programação Java 63


• As classes internas podem ser declaradas como private ou protected. A proteção ao
acesso não impede que a classe interna utilize qualquer membro de outra classe, desde que
uma contenha a outra.

• As classes internas declaradas como static tornam-se automaticamente classes de nível


superior. Elas perdem a capacidade de utilizar dados ou variáveis no escopo local e em
outras classes internas.

• As classes internas não podem declarar membros como static. Esse procedimento só
pode ser efetuado por classes de nível superior. Para que uma classe interna utilize um
membro static, devera declará-lo na classe de nível superior.

As classes internas são melhor utilizadas como um recurso útil para a criação de adaptadores de
eventos ou em classes que compartilham dados. Será visto em eventos a recriação da classe
TwoListen, descrita no capítulo sobre Eventos, para incluir duas classes de manipuladores na
classe TwoListen.

1 import java.awt.*;
2 import java.awt.event.*;
3
4 public class TwoListenInner {
5 private Frame f;
6 private TextField tf;
7
8 public static void main(String args[]) {
9 TwoListen that = new TwoListen();
10 that.go();
11 }
12
13 public void go() {
14 f = new Frame("Exemplo de Dois Ouvidores");
15 f.add ("North", new Label ("Clique e arraste o mouse"));
16 tf = new TextField (30);
17 f.add ("South", tf);
18
19 f.addMouseMotionListener (new MouseMotionHandler());
20 f.addMouseListener (new MouseEventHandler()o);
21 f.setSize(300, 200);
22 f.setVisible(true);
23 }
24 MouseMotionHandler é uma classe interna
25 public class MouseMotionHandler extends MouseMotionAdapter {
26
27 public void mouseDragged (MouseEvent e) {
28 String s =
29 "Mouse arrastado: X = " + e.getX() + " Y = " + e. gety () ;
30 tf.settext (s);
31
32
33
34 //MouseEventHandler
35 public class MouseEventHandler extends MouseAdapter {
36
37 public void mouseEntered (MouseEvent e) {
38 String s = "O mouse entrou";
39 tf.settext (s);
40 }

Linguagem de Programação Java 64


41
42 public void mouseexited (MouseEvent e) {
43 String s = "O mouse saiu da construção “;
44 tf.settext (s);
45 }
46 }
47 }

As vantagens desse procedimento consistem em que as classes MouseMotionHandler e


MouseEventHandler são incluídas no arquivo TwoListenInner.class e o tamanho global do
programa é menor. TwoListenInner e uma classe com 1.362 bytes.

Se o código acima for recriado com duas classes de nível superior adicionais para o
MouseMotionHandler e o MouseEventHandler, três classes separadas serão criadas e o
tamanho global do aplicativo será 2.864 bytes.

Classes anônimas

Também é possível incluir toda a definição de uma classe no escopo de uma expressão. Esse
procedimento define a classe anonymous e cria a instância de uma só vez. Por exemplo, é possível
recriar parte de TwoListenInner para utilizar uma classe anônima desta maneira:

20 public void go() {


21 f = new Frame("Exemplo de Dois Ouvidores");
22 f.add ("North", new Label ("Cliclue e arraste o mouse"));
23 tf = new TextField (30);
24 f.add ("South", tf);
25
26 f.addMouseMotionListener (new MouseMotionAdapter() {
27 public void mousedragged (MouseEvent e) {
28 String s =
29 Mouse arrastado: x = 11 + e.getX()+ “Y = “
30 + e. getY () ;
31 tf.settext (s);
32 }
33 } ); // <- observe as chaves de fechamento)
34
35 f.addMouseListener (new MouseEventHandlero);
36 f.setSize(300, 200);
37 f.setVisible(true);
38 }

Embora as classes anônimas forneçam uma redução adicional de arquivos de classe


(TwoListenInner com os dois adaptadores utilizando classes anônimas em apenas 1.291 bytes),
você pode observar que o código se torna cada vez mais obscuro. As classes anônimas podem
conter inicializadores, mas não um construtor. A lista de argumentos da nova expressão
(normalmente vazia) é passada para um construtor da superclasse.

Como funcionam as classes internas?


As classes internas têm acesso ao escopo das respectivas classes delimitadoras, de modo que o
compilador possa adicionar uma variável extra de instância privada à classe interna e a um
construtor. Além disso, o compilador aplica automaticamente o escopo das classes delimitadoras à
classe interna, substituindo a notação de pontos padrão "." por um "$". Portanto, o código fonte
transformado é visto da seguinte maneira pelo compilador:

Linguagem de Programação Java 65


1 import java.awt.*;
2 import java.awt.event.*;
3 public class TwoListenInner
4 // Outros métodos permanecem os mesmos
5
6 public void go() {
7 ...
8 f.addMouseMotionListener
9 (new TwoListenInner$MouseMotionHandler (this));
10 ...
11 }
12
13 class TwoListenInner$MouseMotionHandler extends MouseMotionAdapter {
14 // cópia salva de TwoListenInner.this
is private TwoListenInner this$0;
16
17 // o compilador gerou um constr-utor
18 TwoListenerInner$MouseMotionHandler (TwoListenInner this$0) {
19 this.this$0 = this$0;
20 }
21
22 public void mouseDragged (MouseEvent e) {
23 String s =
24 "Mouse arrastado: X = " + e.getX() + " Y = " + e. getY() ;
25 this$0.tf.setText (s);
26 }
27 }

A classe Vector
A classe Vector fornece métodos que permitem trabalhar com matrizes dinâmicas de variados tipos
de elementos.

java.lang.object
|
|
|
|
--|---------- java.util.Vector

Declaração

public class Vector extends Object implements Cloneable

Cada vector mantém uma capacity e capacityIncrement. Como elementos são adicionados a
um vector, o armazenamento deste aumenta em partes do tamanho da variável
capacityIncrement, A capacidade de um vector é sempre, pelo menos, correspondente ao
tamanho do vector (normalmente maior).

Construtores

Estes são os construtores da classe vector:

• public Vector () - Constrói um vector vazio.

Linguagem de Programação Java 66


• public Vector (int initialCapacity) - Constrói um vector vazio com a capacidade
de armazenamento especificada.

• public Vector(int initialCapacity, int capacityIncrernent) - Constrói um


vector vazio com a capacidade de armazenamento especificada e o capacityIncrement
especificado.

Variáveis

A classe Vector contém as seguintes variáveis de instância:

• protected int capacityIncrement -O tamanho do incremento. (Se for 0, o tamanho


do buffer será duplicado toda vez que precisar crescer.)
• protected int elementCount – O número de elementos no buffer.
• protected Object elementData [ ] - O buffer em que os elementos estão
armazenados.

Métodos

Veja a seguir alguns dos métodos da classe vector. Consulte a API da linguagem Java para obter
uma descrição de todos os métodos dessa classe.

public final int size () - Retorna o número de elementos no vector. (Não é o mesmo que a
capacidade do vector.)

public final boolean contains (Object elem) - Retorna verdadeiro quando o objeto
especificado e um valor da coleção.

public final int indexOf (Object elem) - Procura o objeto especificado, a partir da
primeira posição, e retorna um índice para ele (ou -1 se o elemento não for encontrado). Utiliza o
método equals() do objeto, de modo que se esse objeto não sobrescrever o método equals() de
Object, apenas comparará referências a objetos e não o conteúdo destes.

public final synchronized Object elementAt (int index) - Retorna o elemento no


índice especificado.

Lança ArrayIndexOutOfBoundsException quando index é inválido.

public final synchronized void setElementAt (Object obj, int index) -


Substitui o elemento, no índice especificado, pelo objeto especificado.

Lança ArrayIndexOutOfBoundsException quando index é inválido.

public final synchronized void removeElementAt (int index) - Exclui o elemento


no índice especificado.

Lança ArrayIndexOutOfBoundsException quando index é inválido.

public final synchronized void addElement (Object obj) - Adiciona o objeto


especificado como o último elemento do vector.

public final synchronized void insertElemnentAt (Object obj, int index) -


Insere o objeto especificado como um elemento no índice especificado, movendo para cima todos os
elementos com índice igual ou maior.

Linguagem de Programação Java 67


Lança ArrayIndexOutOfBoundsException quando index é inválido.

Exemplo de Vector

O exemplo a seguir pode ser utilizado para adicionar diferentes tipos de elementos a um vector e
imprimir elementos de Vector.

Observação - O programa utiliza métodos das classes descritas anteriormente neste módulo.

1 import java.util.*;
2
3 public class MyVector extends Vector
4 public MyVector {
5 super(1,1); // capacidade de armazenamento e capacityIncrement
6 }
7
8 public void addInt(int i) {
9 addElement(new Integer(i)); // addelement necessita de Object
arg
10 }
11
12 public void addFloat(float f) {
13 addElement(new Float(f));
14 }
is
16 public void addString(String s) {
17 addElement(s);
18 }
19
20 public void addCharArray(char a[]) {
21 addElement (a);
22 }
23
24 public void printVector() {
25 Object o;
26 int length = size();
27
28 System.out.println("Número de elementos de vector é "
29 + length + “ e eles são:");
29 for (int i = 0; i < length; i++) {
30 o = elementAt(i);
31 if (o instanceof char[])
32 //System.out.println(o.toString()); imprime ref
33 System.out.println(String.copyValueof((char[]) o));
34
35 else
36 System.out.println(o.toString());
37 }
38 }
39 public static void main(String args[]) {
40 MyVector v = new MyVector();
41 int digit = 5;
42 float real = 3.14f;
43 char letters[] = {'a', 'b', 'c', 'd'};
44 String s = new String ("Olá!");
45
46 v.addInt(digit);

Linguagem de Programação Java 68


47 v.addFloat(real);
48 v.addString(s);
49 v.addCharArray(letters);
50
51 v.printVector();
52 }
53 }

Esse programa produz a seguinte saída:

C:\> java MyVector

Número de elementos de vector é 4 e eles são:


5
3.14
olá!
abcd

Linguagem de Programação Java 69


Linguagem de Programação Java 70
Capítulo 7

Construção de GUIs Java

Objetivos

Após concluir este módulo, você será capaz de:


• Descrever o pacote AWT a os respectivos componentes.
• Explicar containers, componentes e gerenciadores de layout e como eles
trabalham juntos para construir uma interface gráfica com o usuário (GUI).
• Descrever a utilizar os gerenciadores de layout de fluxo, borda, grade e
cartão, para atingir um layout dinâmico desejado.
• Utilizar apropriadamente os containers de panels e de frames.
• Colocar painéis dentro de outros containers para construir layouts
complexos.

Linguagem de Programação Java 71


O pacote java.awt
O pacote java.awt contém classes para gerar dispositivos e componentes da GUI. Uma visão
geral deste pacote é mostrada no diagrama abaixo. As classes mostradas em negrito destacam os
principais tópicos deste módulo, existindo mais classes no package java.awt.

java.lang.Object BorderLayout
CardLayout
CheckBoxGroup
Color
Dimension
Event
Font
FlowLayout
FontMetrics
Graphics
GridBagLayout
GridLayout
Image
Insets
Point
Polygon
Rectangle
Toolkit MenuBar Menu
MenuComponent MenuItem CheckBoxMenuItem
Component

Button
Canvas
CheckBox Applet
Choice Panel
Container Window
Label Dialog
List Frame
Scrollbar
TextComponent TextArea
Textfield

Construir interfaces gráficas com o usuário (GUIs)


Containers e componentes

Os containers e componentes são fundamentais para o AWT da linguagem Java.


Os componentes constituem o aspecto geralmente visível de uma GUI, como um botão ou um label.

Para que esses componentes sejam exibidos, devem ser "adicionados" a um container. Em geral, um
recipiente pode conter um ou mais componentes a se, for o caso, pode conter outros recipientes.

Observação - O fato de um recipiente poder armazenar não apenas componentes, mas também
recipientes é extremamente importante para a construção de layouts de complexidade realística.

Linguagem de Programação Java 72


Posicionar componentes

A posição de um componente em um recipiente é determinada por um gerenciador de layout. Um


recipiente mantém uma referência a uma instância especifica de um LayoutManager. Quando o
recipiente precisa posicionar um componente, ele chama o gerenciador de layout para fazer isso. A
mesma delegação ocorre quando se decide o tamanho de um componente.

Dimensionar componentes

Como o gerenciador de layout é geralmente responsável pelo tamanho e posição de componentes


no recipiente, você normalmente não deve tentar definir essas propriedades com relação a esses
componentes. Se você tentar fazê-lo (usando qualquer um dos métodos setLocation (),
setSize() ou setBounds()), o gerenciador de layout sobreporá sua ação.

Se você tiver de controlar o tamanho ou a posição de componentes de outra maneira que não seja
utilizando os gerenciadores de layout padrão, será possível desativar esse gerenciador, chamando
este método no containers :

setLayout(null);

Após essa etapa, você deve utilizar o método setLocation(), setSize() ou setBounds() nos
componentes para localizá-los no container.

Saiba que esse procedimento resulta em layouts que dependem de plataforma, devido as diferenças
entre os sistemas de janelas a os tamanhos de fonte. Um melhor procedimento consiste em criar
uma nova classe de LayoutManager.

Frames (moldura)

Frame é uma subclasse de Window. É uma Window com um titulo e cantos de redimensionamento.

Criar um Frame simples

O construtor Frame (String) da classe Frame cria um novo objeto Frame invisível com o titulo
especificado por String. Um Frame pode ser redimensionado através do método setSize ()
herdado da classe Component.

Observação - Os métodos setVisible() e setSize() devem ser chamados para que Frame se
torne visível.

O programa abaixo cria uma moldura simples com um título, tamanho e cor de fundo específicos:

1 import java.awt.*;
2 public class MyFrame extends Frame f {
3 public static void main (String args[]) {
4 MyFrame fr = new MyFrame("Ola ai fora!");
5 // método de componente setSize()
6 fr.setSize(500,500);
7 fr.setBackground(Color.blue);
8 fr.setVisible(true); // método de componente show ()
9 }
10 public MyFrame (String titulo) {
11 super(titulo);
12 }
13 ...

Linguagem de Programação Java 73


14 }

Executar o programa

C:\> javac MyFrame.java


C:\> java MyFrame

Panels (painéis)
Os painéis, como as molduras, fornecem o espaço para que você anexe qualquer componente da
GUI, incluindo outros painéis.

Criar painéis

Os painéis são criados através do construtor Panel (). Uma vez criado um objeto Panel, ele
deverá ser adicionado a um objeto Window ou Frame para que fique visível. Isso é feito através do
método add () da classe Container.

O programa abaixo cria um pequeno painel amarelo a adicioná-lo a um objeto Frame:

1 import java.awt.*;
2 public class FrameWithPanel extends Frame {
3
4 // Construtor
5 public FrameWithPanel (String str) {
6 super (str);
7 }
8
9 public static void main (String args[]) {
10 FrameWithPanel fr =
11 new FrameWithPanel ("Moldura com Painel");
12 Panel pan = new Panel();
13
14 fr.setSize(200,200);
15 fr.setBackground(Color.blue);
16 fr.setLayout(null); //passa por cima do gerenciador de layout
padrao
17 pan.setSize (100,100);
18 pan.setBackground(Color.yellow);
19
20 fr.add(pan);
21 fr.setVisible(true);
22 }

Linguagem de Programação Java 74


23 ...
24 }

Resultado:

Layouts de containers
O layout de componentes em um container pode ser controlado por um gerenciador de layout. Cada
container, como um painel ou uma moldura, tem um gerenciador de layout padrão associado, que
pode ser alterado pelo desenvolvedor de Java ao criar uma instância desse container.

Gerenciadores de layout

Estes gerenciadores de layout estão incluídos na linguagem Java:

FlowLayout - O gerenciador de layout padrão de Panels e Applets.


BorderLayout - O gerenciador de layout padrão de Windows, Dialogs a Frames.
GridLayout
CardLayout
GridBagLayout

O gerenciador GridBagLayout não será descrito neste módulo.

Um exemplo simples
Este exemplo simples de código demonstra vários pontos importantes, que serão abordados nas
próximas seções:

import java.awt.*;

public class ExGui private Frame f;


private Button bl;
private Button b2;

public static void main(String args[]) {


ExGui that = new ExGui();
that.go();
}

Linguagem de Programação Java 75


public void go() {
f = new Frame ("Exemplo GUI");
f.setLayout(new F1owLayout());
b1 = new Button ("Pressione");
b2 = new Button ("não pressione");
f.add(b1);
f.add(b2);
f.pack();
f.setVisible(true);
}
}

O método main ( )

O método main () desse exemplo tem duas funções. A primeira a criar uma instância do objeto
ExGui. Lembre-se de que até que uma instância seja criada, não haverá itens de dados reais
chamados f, bl e b2 para serem utilizados.

Após a criação do espaço de dados, o método main () chamará o método da instância go () no


contexto dessa instância. Em go () , ocorre a ação real.

new Frame ("Exemplo de GUI")

Esse procedimento cria uma instância da classe java.awt.Frame. Um Frame na linguagem Java
é uma janela de nível superior que contém uma barra de título, nesse caso definida pelo argumento
de construtor "Exemplo de GUI", além de uma borda redimensionada e outros acessórios, de acordo
com as convenções locais. O tamanho da moldura equivale a zero a não esta visível no momento.

f.setLayout(new FlowLayout())

Isso cria uma instância do gerenciador de layout de fluxo e a instala na moldura. Há um gerenciador
de layout padrão para cada moldura, mas ele não se aplica à finalidade desse exemplo. O
gerenciador de layout de fluxo é o mais simples do AWT a posiciona os componentes como se
fossem palavras em uma página, linha por linha. Observe que, por padrao, o layout de fluxo
centraliza cada linha.

new Button ("Pressione")

Isso cria uma instância da classe java.awt.Button. Um botão é o padrão extraído do kit de
ferramentas do sistemas de janelas local. O rótulo do botão é definido pelo argumento da string para
o construtor.

f.add(b1)

Isso informa à moldura f (que é um container) que ela deve conter o componente b1. Os botões são
exemplos de componentes da linguagem Java. O tamanho e a posição de b1 estão sob o controle
do gerenciador de layout da moldura, deste ponto em diante.

f.pack()

Este método informa à moldura para definir um tamanho que "acomode adequadamente" os
componentes que ela contém. Para tomar essa decisão, a moldura chama o gerenciador de layout,
que é o responsável pelo tamanho a posição de todos os elementos contidos nela.

f.setVisible(true)

Este método faz com que a moldura e todos os componentes dela se tornem visíveis ao usuário.

Linguagem de Programação Java 76


Em um sistema Windows 95, o resultado final desse código tem esta aparência:

Gerenciadores de layout
Gerenciador de layout de fluxo

O layout de fluxo utilizado no primeiro exemplo posiciona os componentes linha por linha. Toda vez
que uma linha é preenchida, uma nova linha é iniciada.

Ao contrário de outros gerenciadores de layout, o layout de fluxo não limita o tamanho dos
componentes gerenciados, mas, em vez disso, permite que eles tenham o tamanho preferencial.

Observação - Todos os componentes possuem um método chamado getPreferredSize (), que


é utilizado pelos gerenciadores de layout para saber qual será o tamanho ideal para o componente.

As opções em um layout de fluxo permitem alinhar os componentes à esquerda ou à direita se você


preferir utilizar essa organização em vez da centralização padrão.

Você poderá especificar inserções se quiser criar uma área de borda maior entre cada componente.

Quando a área que esta sendo gerenciada por um layout de fluxo é redimensionada, pode ser que o
layout seja alterado.

Após o
redimensionamento

Veja a seguir alguns exemplos de como implementar o FlowLayout, utilizando o método


setLayout () herdado de Component:

setLayout(new FlowLayout(FlowLayout.RIGHT,20,40));
setLayout(new FlowLayout(FlowLayout.LEFT));
setLayout(new FlowLayout());

O exemplo abaixo adiciona vários botões a um layout de fluxo em uma moldura.

1 import java.awt.*;
2
3 public class MyFlow f
4 private Frame f;
5 private Button buttonl, button2, button3;
6
Linguagem de Programação Java 77
7 public static void main (String args[]) {
8 MyFlow mflow = new MyFlow ();
9 mflow.go();
10 }
11
12 public void go() {
13 f = new Frame (" Layout de Fluxo");
14 f.setLayout(new FlowLayout());
15 button1 = new Button("Ok");
16 button2 = new Button("Abrir");
17 button3 = new Button("Fechar");
18 f.add(buttonl);
19 f.add(button2);
20 f.add(button3);
21 f.setSize (100,100);
22 f.setVisible(true);
23 }
24 }

Linguagem de Programação Java 78


Gerenciador de layout de borda

O gerenciador BorderLayout fornece um esquema mais complexo para posicionar os


componentes em um painel ou janela. O BorderLayout contém cinco áreas distintas: North,
South, East, West e Center.

A área North ocupa a parte superior de um painel, a East ocupa o lado direito a assim por diante.
A área Center representa todo o restante, uma vez preenchidas as áreas North, South, East
e West.

O BorderLayout é o gerenciador padrão para Caixas de Diálogo e Molduras. Veja na próxima


página o código referente ao exemplo mostrado abaixo:

Após o redimensionamento

Observação – Quando a janela é redimensionada, os tamanhos dos botões são alterados, mas não
suas posições relativas.

Linguagem de Programação Java 79


O código a seguir consiste em uma modificação do exemplo anterior e demonstra o comportamento
do gerenciador de layout de borda:

1 import java.awt.*;
2
3 public class ExGui2 {
4 private Frame f;
5 private Button bn, bs, bw, be, bc;
6
7 public static void main(String args[]) {
8 ExGui2 that = new ExGui2();
10 )
11
12 public ExGui2() {
13 f = new Frame("Layout de Borda ");
14 bn = new Button("B1");
15 bs = new Button("B2");
16 bw = new Button("B3");
17 be = new Button("B4");
18 be = new Button("B5");
19
20 f.add(bn, "North");
21 f.add(bs, "South");
22 f.add(bw, "West");
23 f.add(be, "East");
24 f.add(bc, "Center");
25
26 f.setSize (200, 200);
27 f.setVisible(true);
28 }
29 }

Os componentes devem ser adicionados a regiões com nome no gerenciador de layout de borda;
caso contrário, não ficarão visíveis. Tome cuidado para digitar corretamente os nomes de regiões,
pois a ortografia e a utilização de maiúsculas são cruciais.

Você pode utilizar urn gerenciador de layout de borda para produzir layouts com elementos que se
expandam em uma direção ou em outra ou em ambas as direções, mediante redimensionamento.

Se você não utilizar uma região de um layout de borda, ela se comportará como se o tamanho
preferencial fosse zero por zero. Assim, a região central ainda aparecerá como fundo, mesmo que
ela não contenha componentes, mas as quatro regiões periféricas serão reduzidas a uma linha de
espessura zero e desaparecerão.

Você só deve adicionar um único componente a cada uma das cinco regiões do gerenciador de
layout de borda. Se você tentar adicionar mais de um, apenas um ficará visível. Voce verá, mais
adiante, como é possível utilizar recipientes intermediários para permitir que mais de um
componente seja incluído no espaço de uma única região do gerenciador de layout de borda.

Linguagem de Programação Java 80


Gerenciador de layout de grade (GridLayout)

O gerenciador grid layout fornece flexibilidade para o posicionamento de componentes. Você cria o
gerenciador com um número determinado de linhas a colunas. Em seguida, os componentes
preenchem as células definidas pelo gerenciador. Por exemplo, um layout degrade com tres linhas e
duas colunas criadas pela declaração new GridLayout(3,2) criaria seis células, como mostrado
abaixo.

Após o
redimensionamento

Como no gerenciador BorderLayout, a posição relativa de componentes não é alterada quando a


área é redimensionada. Apenas os tamanhos dos componentes são alterados.

Observe que a largura de todas as células é idêntica e é determinada como uma divisão simples da
largura disponível pelo número de células. Da mesma forma, a altura de todas as células é
simplesmente determinada pela altura disponível dividida pelo número de linhas.

A ordem em que os componentes são adicionados à grade determina a célula que eles ocupam. As
linhas de células são preenchidas da esquerda para a direita como texto, e a "página" é preenchida
com linhas de cima para baixo.

Veja a seguir o código do aplicativo mostrado na página anterior.

1 import java.awt.*;
2 public class GridEx {
3 private Frame f;
4 private Button bl, b2, b3, b4, b5, b6;
5
6 public static void main(String args[]) {
7 GridEx grid = new GridEx();
8 grid.go();
9 }
10
11 public void go() {
12 f = new Frame("Exemplo de Grade");
13
14 f.setLayout (new GridLayout (3, 2));
15 b1 = new Button("1");
16 b2 = new Button("2");
17 b3 = new Button("3");
18 b4 = new Button("4");
19 b5 = new Button("5");
20 b6 = new Button("6");
21
22 f.add(bl);
23 f.add(b2);
24 f.add(b3);

Linguagem de Programação Java 81


25 f.add(b4);
26 f.add(b5);
27 f.add(b6);
28
29 f.pack();
30 f.setVisible(true);
31 }
32 }

CardLayout

O Gerenciador CardLayout permite tratar a interface como uma série de cartões, sendo que é
possível visualizar um de cada vez. Veja a seguir um exemplo de um Frame com cinco painéis. Se
você clicar no Panel do Frame, mudará para o próximo cartão.

Se você clicar neste painel, mudará para este outro e assim por diante.

Um fragmento do código é mostrado a seguir:

1 import java.awt.*;
2 import java.awt.event.*;
3
4 public class CardTest implements MouseListener {
5
6 Panel p1 p2, p3, p4, p5;
7 Label 11, 12, 13, 14, 15;
8
9 // Declara um objeto CardLayout,
10 // de modo que seja possível chamar os respectivos métodos
11 CardLayout mycard;
12 Frame f;
13
14 public static void main (String args[]){
15 CardTest ct = new CardTest ();
16 ct.init();
17 }
18
19 public void init {
20 f = new Frame (" Teste de Cartão");
21 mycard = new CardLayout();

Linguagem de Programação Java 82


22 f.setLayout(myCard);
23
24 // cria os painéis a serem utilizados
25 // como cartões
26 p1 = new Panel();
27 p2 = new Panel();
28 p3 = new Panel();
29 p4 = new Panel();
30 p5 = new Panel();
31 // cria um rótulo a ser anexado aos painéis e
32 // altera a cor de cada painel para que possam
ser
33 // distinguidos facilmente
34
35 l1 = new Label("Este e o primeiro Painel");
36 p1.setBackground(Color.yellow);
37
38 l2 = new Label("Este é o segundo Painel ");
39 p2.setBackground(Color.green);
40
41 l3 = new Label("Este é o terceiro Painel ");
42 p3.setBackground(Color.magenta);
43
44 l4 = new Label("Este e o quarto Painel ");
45 p4.setBackground(Color.white);
46
47 l5 = new Label("Este é o quinto Painel ");
48 p5.setBackground(Color.cyan);
49
so // Configura aqui o manipulador de eventos
51
52 // adiciona cada painel ao CardLayout do applet
53 f.add(p1, "Primeiro");
54 f.add(p2, "Segundo");
55 f.add(p3, "Terceiro");
56 f.add(p4, "Quarto");
57 f.add(p5, "Quinto");
58
59 // exibe o primeiro painel
60 myCard.show(f,"Primeiro");
61
62 f.setsize (200, 200);
63 f.show();
64 }

Outros gerenciadores de layout

Além dos gerenciadores de layout de fluxo, borda, grade e cartão, o núcleo awt de java também
fornece o GridBagLayout.

O gerenciador de layout de camadas de grades fornece recursos complexos de layout com base em
uma grade, mas permite que componentes individuais assumam o tamanho preferencial em uma
célula, em vez de preencher a célula inteira. Além disso, um layout de camadas de grades permite
que um um único componente ocupe mais de uma célula.

Linguagem de Programação Java 83


Containers
O AWT fornece vários container. Esta seção descreve os dois mais importantes.

Molduras (Frames)

Você já viu o frame utilizado nos exemplos anteriores. Ele apresenta uma janela de "nível superior"
com um título, uma borda e cantos redimensionáveis, de acordo com as convenções locais de
plataforma.

Se você não utilizar explicitamente o método setLayout() , um frame terá, por padrão, um
gerenciador de layout de borda.

A maioria dos aplicativos utilizara, pelo menos, um frame como o ponto inicial das respectivas
interfaces gráficas com o usuário (GUIs), mas é possível utilizar vários frames em um único código.

Painéis (Panels)

Os painéis são containers e praticamente não têm outra função. Eles não possuem uma aparência
própria e não podem ser utilizados como janelas autônomas. Por padrão, existe um gerenciador de
layout de fluxo associado a cada painel, mas você pode mudar isso através do método
setLayout() utilizado anteriormente em molduras.

Os painéis são criados e adicionados a outros containers da mesma maneira que outros
componentes como, por exemplo, botões. No entanto, quando um painel é adicionado a um
recipiente, há dois procedimentos cruciais que você pode executar no painel resultante. São eles:

• Atribuir-lhe um gerenciador de layout, impondo um tipo de layout diferente em uma região da


exibição
• Adicionar componentes ao painel mesmo que, por exemplo, o próprio painel constitua o
único componente que possa ser efetivamente adicionado a uma região de um layout de
borda.

Criar painéis e layouts complexos

Os painéis são criados através do construtor Panel(). Uma vez criado um objeto Panel, ele deverá
ser adicionado a outro recipiente. Para isso, basta utilizar o método add() do container.

O programa abaixo utiliza um painel para permitir que dois botões sejam posicionados na região
North de um layout de borda. Esse tipo de aninhamento é fundamental para layouts complexos.
Observe que o painel é tratado tal como outro componente, no que diz respeito à moldura.

1 import java.awt.*;
2 public class ExGui3
3 private Frame f;
4 private Panel p;
5 private Button bw, bc;
6 private Button bfile, bhelp;
7
8 public static void main(String args[]) {
9 ExGui3 gui = new ExGui3();
10 gui.go();
11 }
12 public void go() {
13 f = newFrame("Exemplo de GUI 3");
14 bw = new Button("Oeste");

Linguagem de Programação Java 84


15 bc = new Button("Espaço de trabalho");
16 f.add(bw, "Oeste");
17 f.add(bc, "Centro");
18 p = new Panel();
19 f.add(p, "Norte");
20 bfile = new Button("Arquivo");
21 bhelp = new Button("Ajuda");
22 p.add(bfile);
23 p.add(bhelp);
24 f.pack();
25 f.setVisible(true);
26 }
27 }

Quando o exemplo é executado, a exibição resultante tem a seguinte aparência:

Se a janela for redimensionada, terá a seguinte aparência:

Observe que a região North do layout de borda agora contém efetivamente dois botões. Na verdade,
essa região apenas acomoda o painel, pois é este que contém os dois botões.

O tamanho e a posição do painel é determinada pelo layout de borda, ao passo que o tamanho
preferencial de um painel é determinado a partir do tamanho preferencial dos componentes desse
painel. O tamanho e a posição dos botões do painel são controlados pelo layout de fluxo que, por
padrão, está associado ao painel.

Linguagem de Programação Java 85


Linguagem de Programação Java 86
Capítulo 8

O modelo de evento AWT

Objetivos

Após concluir este módulo, você será capaz de:


• Criar o código para tratar eventos que ocorram na interface com o
usuário.
• Utilizar a interface apropriada e o método de manipulação para diversos
tipos de evento.
• Determinar, a partir do objeto de evento, detalhes sobre a ação do usuário
que originou o evento.
• Utilizar a classe adequada de filtro para selecionar um subconjunto de
manipuladores de eventos para um listener de eventos.

Linguagem de Programação Java 87


O que é um evento?
Quando um usuário executa uma ação na interface com o usuário, isso faz com que um evento seja
emitido. Os eventos são objetos que descrevem o que aconteceu. Existem diversos tipos de classes
de eventos para descrever diferentes categorias gerais de ação do usuário.

Origens de eventos

Uma origem de evento (em nível da interface com o usuário) é o resultado de alguma ação do
usuário em um componente AWT. Por exemplo, um clique do mouse em um componente de botão
gerará (origem) um ActionEvent. O ActionEvent é um objeto (classe) que contém informações
sobre o status do evento:

• ActionCommand - O nome do comando associa-se à ação

• Modificadores - Quaisquer modificadores utilizados durante a ação

Manipuladores de eventos

Quando ocorre um evento, ele é recebido pelo componente com o qual o usuário interagiu; por
exemplo, o botão, a barra deslizante, o campo de texto etc.. Um manipulador de eventos é um
método que recebe um objeto Event para que o programa possa processar a interação com o
usuário.

Como os eventos são processados

Entre o JDK 1.0 e o JDK 1.1, houve mudanças significativas com relação à maneira como os
eventos são recebidos e processados. Nesta seção, compararemos o modelo de evento anterior
(JDK 1.0) e o modelo de evento atual JDK 1.1 e posteriores.

JDK 1. 0 versus modelo de evento JDK 1. 1


0 JDK 1.0 utiliza um modelo de evento hierárquico, ao passo que o JDK 1.1 utiliza um modelo de
evento de delegação. Embora este curso aborde o JDK 1.1, é importante reconhecer em quais
aspectos esses dois modelos de eventos se assemelham.

Modelo hierárquico (JDK 1.0)

O modelo de evento hierárquico é baseado na capacidade de conter. Primeiro, os eventos são


enviados ao componente e, em seguida, propagam-se na hierarquia de capacidade de conter. Os
eventos que não são tratados no componente continuarão automaticamente a se propagar até o
container do componente.

Moldura

Painel

Botão Evento de ação

Linguagem de Programação Java 88


Por exemplo, na figura acima, um clique do mouse no objeto Button (contido em um Panel em um
Frame) primeiro envia um evento de ação para o Button. Se não for tratado pelo objeto Button, o
evento será, em seguida, enviado para o Panel e, se não for tratado nesse local, será enviado para
o Frame.

Há uma vantagem óbvia nesse modelo:

• Ele é simples e adequado a um ambiente de programação orientado a objetos. Afinal, os


componentes da Java derivam da classe java.awt.Component, na qual o método
handleEvent está localizado.

Existem, porém, algumas desvantagens:

• Não há uma maneira simples de filtrar eventos

• Para tratá-los, você deve dividir o componente em subclasses que recebam o evento ou criar
um método handleEvent extenso no container básico.

Modelo de delegação (JDK 1. 1)

O JDK 1.1 introduziu um novo modelo de evento, chamado modelo de evento de delegação. No
modelo de evento de delegação, os eventos são enviados ao componente, mas cabe a cada
componente registrar uma rotina de manipulador de eventos (chamada de listener) para receber o
evento. Dessa maneira, o manipulador de eventos pode se encontrar em uma classe separada do
componente. Em seguida, o tratamento do evento é delegado à classe separada.

Moldura Manipuladores de eventos


De panel e frame
Painel

Botão Evento de ação

ActionPerformed (ActionEvent e) {

}

Os eventos são objetos que só são relatados a listeners registrados. Cada evento tem uma classe
receptora correspondente (interface). A classe que implementa a interface receptora define os
métodos que podem receber um objeto Event.

Por exemplo, este é um Frame simples com um único Button:

import java.awt.*;
import ButtonHandler;
public class TestButton {
public static void main(String args[]) {
Frame f = new Frame("Teste");

Linguagem de Programação Java 89


Button b = new Button("Me pressione!");
b.addActionListener(new ButtonHandlero);
f.add("Center", b);
f.packo;
f.setVisible(true);
}
}

A classe ButtonHandler é a classe de manipulador ao qual o evento será delegado.

import java.awt.event.*;
public class ButtonHandler implements ActionListener {
public void actionPerformed(ActionEvent e) {
System.out.println("Ação ocorreu");
}
}

• A classe Button possui um método addActionListener(ActionListener).

• A interface ActionListener define um único método, actionperformed, que receberá um


ActionEvent.

• Quando um objeto Button é criado, ele pode registrar um receptor para o ActionEvents
através do método addActionListener, especificando o objeto de classe que implementa a
interface ActionListener.

• Quando você dá um clique no objeto Button, um ActionEvent é enviado para qualquer


ActionListener que esteja registrado através do método actionperformed (ActionEvent).

Há várias vantagens nesse procedimento:

• Os eventos não são tratados acidentalmente. No modelo hierárquico, um evento pode se


propagar até um container e ser tratado em um nível não esperado.

• É possível criar classes de filtro (adaptador) para classificar ações de eventos.

• O modelo de delegação é mais adequado para a distribuição do trabalho entre as classes.

• O novo modelo de evento fornece suporte ao JavaBeans TM

Vale a pena considerar algumas questões/ desvantagens com relação a esse modelo:

• Ele é mais complexo, pelo menos inicialmente, para ser compreendido.

• A transferência do código do JDK 1.0 para o 1.1 não é fácil.

• Embora o JDK atual suporte o modelo de evento do JDK 1.0, além do modelo de delegação, os
modelos de delegação do JDK 1.0 e JDK 1.1 não podem ser combinados.

Linguagem de Programação Java 90


Comportamento da interface gráfica com o usuário (GUI) da
linguagem Java
Categorias de evento

O mecanismo geral para receber eventos de componentes foi descrito no contexto de um único tipo
de evento. Diversos eventos são definidos no pacote java.awt.event e componentes de terceiros
podem ser adicionados a essa lista.

Para cada categoria de eventos, há uma interface que deve ser definida por qualquer classe que
deseje receber os eventos. Essa interface também exige que um ou mais métodos sejam definidos.
Esses métodos serão chamados quando surgirem eventos específicos. A tabela relaciona as
categorias, atribuindo o nome da interface às categorias e aos métodos necessários. Os nomes dos
métodos são mnemônicos indicando as condições que farão com que o método seja chamado.

Comportamento da GUI da linguagem Java

Categoria Nome da interface Métodos

Ação ActionListener actionPerformed(ActionEvent)


Item ItemListener itemStateChanged(ItemEvent)
Movimento do mouse MouseMotionListener mouseDragged (Maus eEvent)
mouseMoved(MouseEvent)
Botão do mouse MouseListener mousePressed(MouseEvent)
mouseReleased(MouseEvent)
mouseEntered(MouseEvent)
mouseExited(MouseEvent)
mouseClicked(MouseEvent)
Tecla KeyListener keyPressed(KeyEvent)
keyReleased(KeyEvent)
keyTyped(KeyEvent)
Foco FocusListener focusGained(FocusEvent)
focusLost(FocusEvent)
Ajuste AdjustmentListener
adjustmentValueChanged(AdjustmentEvent)
Componente ComponentListener componentMoved(CornponentEvent)
componentHidden(ComponentEvent)
componentResized(ComponentEvent)
componentShown(ComponentEvent)
janela WindowListener windowClosing(WindowEvent)
windowOpened(WindowEvent)
windowIconified(WindowEvent)
windowDeiconified(WindowEvent)
windowClosed(WindowEvent)
windowActivated(WindowEvent)
windowDeactivated(WindowEvent)
Recipiente ContainerListener componentAdded(ContainerEvent)
componentRemoved(ContainerEvent)
Texto TextListener textValueChanged(TextEvent)

Linguagem de Programação Java 91


Um exemplo mais complexo

Agora, vamos considerar um exemplo mais complexo. Ele controlará o movimento do mouse quando
o botão for pressionado (arrasto do mouse) e, além disso, detectará esse movimento.

Os eventos causados pelo movimento do mouse com um botão pressionado poderão ser detectados
por uma classe que implementa a interface mouseMotionListener. Essa interface necessita de
dois métodos: mouseDragged() e mouseMoved(). Estamos apenas interessados no movimento
de arrasto, mas, de qualquer forma, fornecemos ambos os métodos. No entanto, o corpo do método
mouseMoved() pode estar vazio.

Para detectar eventos do mouse, incluindo cliques, precisamos implementar a interface


MouseListener. Essa interface contém vários eventos, incluindo mouseEntered,
mouseExited, mousePressed, mouseReleased e mouseClicked.

Quando ocorrem eventos de arrasto do mouse ou de tecla, relatamos nos campos de rótulo as
informações sobre a posição do mouse e a tecla pressionada. Este é o código:

1 import java.awt.*;
2 import java.awt.event.*;
3
4 public class TwoListen implements
5 MouseMotionListener, MouseListener {
6 private Frame f;
7 private TextField tf;
8
9 public static void main(String args[]){
10 TwoListen two = new TwoListeno;
11 two.go () ;
12 }
13 public void go() {
14 f = new Frame("Exemplo de dois ouvidores");
15 f.add (new Label ("Clique e arraste o mouse”),
16 "North");
17 tf = new TextField (30);
18 f.add (tf, "South");
19
20 f.addMouseMotionListener(this);
21 f.addMouseListener (this);
22 f.setSize(300, 200);
23 f.setVisible(true);
24 //f.requestFocus();
25 }
26
27 // Estes são eventos MouseMotionListener
28 public void mouseDragged (MouseEvent e)
29 String s =
30 "Mouse arrastado: X = " + e.getX() +
31 " Y = " + e.getY();
32 tf.setText (s);
33 }
34
35 public void mouseMoved (MouseEvent e) {
36 }
37
38 // Estes são eventos MouseListener
39 public void mouseClicked (MouseEvent e) {

Linguagem de Programação Java 92


40 }
41
42 public void mouseEntered (MouseEvent e) {
43 String s - "0 mouse entrou";
44 tf.setText (s);
45
46 public void mouseExited (MouseEvent e) {
47 String s = "O mouse saiu da construção";
48 tf.settext (s);
49
50
51 public void mousePressed (MouseEvent e)
52
53
54 public void mouseReleased (MouseEvent e)
55 }
56 }

Diversos pontos nesse exemplo devem ser observados.

Declarar várias interfaces

A classe é declarada da seguinte maneira:

implements MouseMotionListener, MouseListener

Observe que você pode declarar várias interfaces simplesmente utilizando a separação por vírgulas.

Ouvir várias origens

Ao se emitir chamadas de métodos,

f.addMouseListener(this);
f.addMouseMotionListener(this);

Os dois tipos de evento fazem com que os métodos sejam chamados na classe TwoListen. Você
pode receber todas as origens de evento desejadas, pertencentes a várias categorias e origens,
como diferentes molduras ou diferentes botões.

Obter detalhes sobre o evento

Quando os métodos de manipulação, como o mouseDragged (), são chamados, eles recebem um
argumento que contém informações potencialmente importantes sobre o evento original. Para
determinar os detalhes de quais informações estão disponíveis para cada categoria de evento, você
deve verificar a documentação de classe apropriada no pacote java.awt.event.

A estrutura de escuta de eventos do AWT permite que vários listeners sejam anexados ao mesmo
componente. Em geral, para criar um programa que execute várias ações com base em um único
evento, você provavelmente deve codificar esse comportamento no método do manipulador. No
entanto, as vezes, o projeto de um programa necessita de várias partes não relacionadas do mesmo
programa para reagir ao mesmo evento. Isso poderá acontecer, se, por exemplo, um sistema de
ajuda contextual estiver sendo adicionado a um programa existente.

O mecanismo de escuta permite chamar um método addListener o número de vezes necessário


para que você possa especificar todos os listener s de que o projeto necessite. Quando ocorrer o
evento, serão chamados os métodos do manipulador de todos os listeners registrados.

Linguagem de Programação Java 93


Observação - A ordem em que os métodos de manipulação são chamados é indefinida. Em
geral, se a ordem de chamada for importante, os manipuladores estarão relacionados e
você não deverá utilizar esse recurso para chamá-los. Em vez disso, registre apenas o
primeiro listener e faça com que ele chame os outros diretamente.

Adaptadores de eventos

Obviamente, dá muito trabalho implementar todos os métodos em cada uma das interfaces Listener,
especialmente a MouseListener e a ComponentListener.

A interface MouseListener, por exemplo, define os seguintes métodos:

• mouseClicked (MouseEvent)

• mouseEntered (MouseEvent)

• mouseExited (MouseEvent)

• mousePressed (MouseEvent)

• mouseReleased (MouseEvent)

Para facilitar o trabalho, a Java fornece uma classe de adaptador para cada interface Listener que
implementa a interface Listener adequada, mas deixa os métodos implementados vazios.

Dessa forma, a rotina Listener definida pode ampliar a classe Adapter e sobrescrever apenas os
métodos necessários.

Por exemplo:

import java.awt.*;
import java.awt.event.*;

public class MouseClickHandler extends MouseAdapter {

// Precisamos apenas do manipulador mouseClick


// e utilizamos o Adaptador para evitar ter de
// criar todos os métodos de manipulação de eventos
public void mouseClicked (MouseEvent e) {
// Executa a ação com o clique do mouse...
}
}

Linguagem de Programação Java 94


Capítulo 9

A biblioteca de componentes AWT

Objetivos

Após concluir este módulo, você será capaz de:


• Reconhecer os principais componentes de AWT.
• Utilizar os componentes de AWT para construir interfaces com o usuário
para programas reais.
• Controlar as cores e as fontes utilizadas por um componente de AWT

Linguagem de Programação Java 95


Recursos do AWT
O AWT fornece uma grande variedade de recursos padrão. Este módulo apresentará os
componentes que estão disponíveis e indicará detalhes sobre os quais você precise saber.

Primeiro, os componentes são descritos. Eles são utilizados para construir interfaces com o usuário.
É óbvio que você precisa pelo menos conhecer o conjunto completo de componentes de UI para que
possa escolher os adequados quando for construir as interfaces.

Os componentes de AWT fornecem mecanismos para controlar sua aparência, especificamente em


termos da cor e da fonte utilizadas para a exibição do texto.

Além disso, o AWT suporta impressão. Esse é um recurso que foi adicionado com a transição para o
JDK 1.1.

Button
Você já conhece o componente Button. Ele fornece um componente básico de interface com o
usuário do tipo "pressione para ativar". Pode ser construído com um rótulo de texto que funcione
como um guia para o usuário, com relação à sua utilização.

Button b = new Button("Exemplo");


add(b);

A interface ActionListener deve ser utilizada para reagir quando botões são pressionados. O
método getActionCommand () do ActionEvent que é emitido quando o botão é pressionado é,
por padrão, a seqüência do rótulo. Isso pode ser modificado através do método
setActionCommand () do botão.

Checkbox
A caixa de seleção fornece um dispositivo simples de entrada "ativada/ desativada", com um rótulo
de texto ao lado.

Checkbox one = new Checkbox("Um", false);


Checkbox two = new Checkbox("Dois", false);
Checkbox three = new Checkbox("Três", true);
add(one);
add(two);
add(three);

O ato de marcar ou desmarcar uma caixa de verificação é enviado à interface ItemListener. O


ItemEvent passado indica se a operação que marcou ou desmarcou a caixa de verificação utilizou
o método getStateChange (). Esse método retorna uma das constantes ItemEvent.

Linguagem de Programação Java 96


DESELECTED ou ItemEvent. SELECTED, conforme apropriado. O método getItem () retorna um
objeto String que representa a seqüência do rótulo da caixa de verificação afetada.

1 class Handler implements ItemListener {


2 public void itemStateChanged(ItemEvent ev)
3 String state = "não selecionado";
4 if (ev.getStateChange() == ItemEvent.SELECTED){
5 state = "selecionado";
6 }
7 System.out.println(ev.getItem()+ “ “ + state);
8 }
9 }

CheckboxGroup
Você pode criar caixas de verificação por meio de um construtor especial que utiliza um argumento
adicional: um CheckboxGroup. Se você fizer isso, a aparência das caixas de seleção será alterada
e todas as caixas de seleção relacionadas ao mesmo grupo exibirão o comportamento de "botão de
opção".

CheckboxGroup cbg = new CheckboxGroup();


Checkbox one new Checkbox("Um", cbg, false);
Checkbox two new Checkbox("Dois", cbg, false);
Checkbox three = new Checkbox("Três", cbg, true);
add(one);
add(two);
add(three);

Choice
Escolha fornece um tipo de entrada simples "selecione um item na lista".

Choice c = new Choice();


c.addItem("Primeira");
c.addItem("Segunda") ;
c.addItem("Terceira");
add(c);

Quando você clica na opção, ela exibe a lista de itens que foram adicionados a ela. Observe que os
itens adicionados são objetos String.

Linguagem de Programação Java 97


A interface ItemListener é utilizada para observar alterações na opção. Os detalhes são o
mesmos para a caixa de verificação.

Canvas
Um objeto Canvas fornece um espaço em branco (com segundo plano colorido). Ele possui um
tamanho preferencial de zero por zero. Por isso, você geralmente deve se certificar de que o
gerenciador de layout atribuirá a ele um valor diferente de zero.

O espaço pode ser utilizado para desenhar ou digitar texto ou receber uma entrada de teclado ou
mouse.

Em geral, o Canvas é utilizado para fornecer um espaço geral de desenho ou é utilizado como a
base de desenvolvimento que fornecerá uma área de trabalho a um componente personalizado.

O Canvas pode ouvir todos os eventos que sejam aplicáveis a um componente geral. De forma
específica, convém adicionar os objetos KeyListener, MouseMotionListener ou
mouseListener a ele para permitir que responda, de alguma maneira, a entrada do usuário. Os
métodos contidos nessas interfaces recebem os objetos KeyEvent e MouseEvent,
respectivamente.

Observação - Para receber eventos de tecla em um canvas, é necessário chamar o método


requestFocus () da tela. Se isso não for feito, geralmente não será possível "direcionar" as
teclas pressionadas para o Canvas. Em vez disso, elas irão para outro componente ou talvez se
perderão totalmente. O exemplo de código demonstra isso.

Eis um exemplo de um Canvas:

1 import java.awt.*;
2 import java.awt.event.*;
3 import java.util.*;
4

Linguagem de Programação Java 98


5 public class MyCanvas
6 implements KeyListener, MouseListener {
7 Canvas c;
8 String s = “”;
9
10 public static void main(String args[]){
11 Frame f = new Frame("Canvas");
12 MyCanvas me = new MyCanvas();
13 mc.c = new Canvas();
14 f.add("Center", mc.c);
15 f.setSize(150, 150);
16 mc.c.addMouseListener(mc);
17 mc.c.addKeyListener(mc);
18 f.setVisible(true);
19 }
20
21 public void keyTyped(KeyEvent ev) {
22 System.out.println("keyTyped");
23 s += ev.getKeyChar();
24 // Não é uma boa técnica de desenho!!!
25 c.getGraphics().drawString(s, 0, 20);
26 } 1
27
28 public void mouseClicked(MouseEvent ev) {
29 System.out.println("mouseClicked");
30 // força o foco para a tela
31 c.requestfocus();
32 }
33 public void keyPressed(KeyEvent ev) {
34 System.out.println("keyPressed");
35 } 1
36
37 public void keyReleased(KeyEvent ev) {
38 System.out.println("keyReleased");
39 }
40 public void mousePressed(MouseEvent ev) {
41 System.out.println("mousePressed");
42 }
43 public void mouseReleased(MouseEvent ev) {
44 System.out.println("mouseReleased");
45 }
46 public void mouseEntered(MouseEvent ev) {
47 System.out.println("mouseEntered");
48 }
49 public void mouseExited(MouseEvent ev) {
50 System.out.println("mouseExited");
51 }
52 }

Linguagem de Programação Java 99


Label
Um objeto Label apenas exibe um única linha de texto. 0 programa pode alterar o texto, mas o
usuário não. Nenhuma borda especial ou outro acessório é utilizado para delinear um rótulo.

Label 1 - new Label("Olá");


add(1);

Labels normalmente não tratam eventos, mas fazem isso, de alguma forma, como um c anvas. Isto
é, as teclas pressionadas só podem ser detectadas confiavelmente através do método
requestFocus ().

TextField
O TextField é um dispositivo de texto de uma única linha.

TextField f = new TextField("Linha única", 30);


add(f);

Como apenas uma linha é possível, um ActionListener pode ser informado através do método
actionperformed (), quando a tecla Enter ou Return é pressionada. Outros listeners de
componentes poderão ser adicionados, se desejado.

Como a área de texto, eles podem ser definidos como somente leitura. Não exibem barras de
rolagem em qualquer das direções, mas podem percorrer um texto longo da esquerda para a direita,
se necessário.

Linguagem de Programação Java 100


TextArea
TextArea é um dispositivo de entrada de texto de várias linhas e colunas. Você pode defini-lo como
não editável, utilizando o método setEditable (boolean), caso em que ele se torna adequado
para consultar o texto. Exibe barras de rolagem horizontal e vertical.

TextArea t = new TextArea("Olá", 4, 30);


add(t);

É possível adicionar ouvidores gerais de componentes à área de texto, mas como o texto é
constituído de várias linhas, se você pressionar a tecla "Enter", apenas fará com que outro caractere
seja inserido no buffer. Se você precisar reconhecer a "conclusão da entrada", poderá inserir um
botão que indique "Aplicar" ou "Gravar", próximo a uma área de texto, para permitir que o usuário
indique isso.

TextComponent

A área de texto e o campo de texto são documentados em duas partes. Se você pesquisar uma
classe chamada TextComponent, encontrará vários métodos essenciais documentados nesse
local. Isso ocorre porque a área e o campo de texto são subclasses do componente de texto.

Você já viu que os construtores das classes TextArea e TextField permitem especificar várias
colunas para exibição. Lembre-se de que o tamanho de um componente exibido é responsabilidade
de um gerenciador de layout. Portanto, essas preferências podem ser ignoradas. Além disso, o
número de colunas e interpretado em termos da largura média dos caracteres contidos na fonte que
está sendo utilizada. O número de caracteres que é realmente exibido poderá variar radicalmente se
uma fonte com espaçamento proporcional for utilizada.

Linguagem de Programação Java 101


List
Com um objeto List, você pode apresentar opções de texto que são exibidas em uma região que
permite a visualização de vários itens ao mesmo tempo. O objeto List pode ser percorrido e
suporta os modos de seleção única e múltipla.

List l = new List (4, true);

O argumento numérico para o construtor define a altura preferencial da lista em termos do número
de linhas visíveis. Como sempre, lembre-se de que isso pode ser sobrescrito por um gerenciador de
layout. O argumento booleano indica se a lista deve permitir que o usuário faca várias seleções.

Um ActionEvent detectado através da interface ActionListener é gerado pela lista, nos modos
de seleção única e múltipla. Os itens são selecionados na lista de acordo com as convenções de
plataforma. Em uma ambiente UNIX/Motif, isso significa que um único clique destaca uma entrada
na lista, mas um clique duplo é necessário para acionar essa lista.

Frame
Essa é a janela de "nível superior" de finalidade geral. Contém acessórios do gerenciador de janelas,
como uma barra de título e alças de redimensionamento.

Frame f = new Frame("Moldura");


f.setsize (100, 100);
f.setVisible(true);

O tamanho de um moldura pode ser definido através do método setsize (). No entanto, ele
geralmente deve ser definido através do método pack (). Isso faz com que o gerenciador de layout

Linguagem de Programação Java 102


calcule um tamanho que acomode adequadamente todos os componentes da moldura. Em seguida,
o tamanho da moldura é definido de acordo.

Os eventos do frame podem ser monitorados por meio de todos os receptores aplicáveis aos
componentes gerais. O WindowListener pode ser utilizado para reconhecer, através do método
windowClosing(), que o botão Quit do menu Window Manager foi selecionado.

Você não deve tentar ouvir os eventos de teclado diretamente de um frame. Embora a técnica
descrita para os com ponentes de tela e de rótulo (isto é, chamar requestFocus() funcione
eventualmente, ela não é confiável. Se você precisar acompanhar eventos de teclado, deverá
adicionar uma tela ou um painel à moldura e, por fim, adicionar o listener a esta para desempenhar
essa função.

Panel
Esse é o recipiente de finalidade geral. Não pode ser utilizado isoladamente, ao contrário de
molduras, janelas e caixas de diálogo.

Panel p = new Panel();

Como um objeto Panel não é exibido, não há uma figura para mostrá-lo.

Panels podem tratar eventos, com a advertência de que o foco no teclado deve ser solicitado
explicitamente, como no exemplo anterior sobre a tela.

Caixa de diálogo
Um objeto Dialog é semelhante a um Frame, no que diz respeito a ser uma janela autônoma com
alguns acessórios. Ele difere de uma moldura, pois menos acessórios são fornecidos, e você pode
solicitar urna caixa de diálogo "modal", que permitira o armazenamento de todas as formas de
entrada até que ela seja fechada.

Dialog d - new Dialog(f, "Diálogo", false);


d.add("Center",
new Label("Olá, eu sou um Diálogo"));
d. pack () ;

Dialogs normalmente não ficam visíveis ao usuário da primeira vez que são criadas. Em vez disso,
normalmente "aparecem" em resposta a alguma outra ação exercida na interface com o usuário; por
exemplo, quando você pressiona um botão.

public void actionPerformed(ActionEvent ev) {


d.setVisible(true);
}

Linguagem de Programação Java 103


Observação - Você deve tratar um objeto Dialog como um dispositivo reutilizável. Isto é, você não
deve destruir o objeto individual quando ele desaparecer da exibição, pois deverá ser guardado para
reutilização posterior. O coletor de lixo pode tornar bem fácil desperdiçar a memória, mas lembre-se
de que a criação e inicialização de objetos levam tempo e não devem ser realizadas sem urna
análise prévia.

Para ocultar um objeto Dialog, você deve chamar setVisible(false) nele. Em geral, para isso,
basta adicionar um WindowListener a ele e esperar que o método windowClosing() seja
chamado nesse receptor. Essa ação é semelhante ao fechamento de uma moldura.

FileDialog
Essa é uma implementação de um dispositivo de seleção de arquivos. Ele possui uma janela
autônoma com acessórios e permite que o usuário percorra o sistema de arquivos para selecionar
um arquivo específico a ser utilizado em outras operações.

FileDialog d = new FileDialog(f, "FileDialog");


d.setVisible(true);
String fname = d.getfile();

Em geral, não é necessário tratar eventos no FileDialog. O setVisible (true) chama blocos
até que o usuário selecione OK. Nesse ponto, você apenas solicita o nome do arquivo selecionado.
Isso é retornado como String.

Painel de rolagem
Fornece um recipiente geral que não pode ser utilizado de forma autônoma. Além disso, fornece
uma porta de visualização para uma região mais ampla e barras de rolagem para manipular essa
porta.

Frame f = new Frame ("Painel de Rolagem") ;


Panel p = new Panel();

Linguagem de Programação Java 104


ScrollPane sp = new ScrollPane();
p.setLayout(new GridLayout(3, 4));
sp.add(p);
f.add(sp); f.setSize(200, 200);
f.setVisible(true);

O objeto ScrollPane cria e gerencia as barras de rolagem, conforme necessário. Contém um


único componente e você não pode controlar o gerenciador de layout utilizado por ele. Em vez disso,
você deve adicionar um Panel ao painel de rolagem, configurar o gerenciador de layout desse
painel e inserir os próprios componentes nesse painel.

Em geral, você não trata eventos em um ScrollPane. Em vez disso, deve fazê-lo nos
componentes contidos nele.

Menus
Os menus diferem dos outros componentes em um ponto crucial. Em geral, não é possível adicionar
menus a recipientes comuns e fazer com que sejam posicionados pelo gerenciador de layout. Você
só pode adicionar menus a elementos específicos, denominados recipientes de menu.
Normalmente, você só pode iniciar uma "árvore" de menus inserindo uma barra de menus em uma
moldura, através do método setMenuBar(). Nesse ponto, é possível adicionar menus à barra de
menus e também adicionar menus ou itens a esses menus.

A exceção é que um menu instantâneo pode ser adicionado a qualquer componente mas,
obviamente, não haverá nenhum layout nesse caso.

O menu de ajuda

Um recurso específico da barra de menus é que você pode designar um menu para ser o menu de
ajuda. Isso é feito através do método setHelpMenu(Menu).O menu a ser tratado como o menu de
ajuda deve ser adicionado à barra de menus. Em seguida, ele será tratado da maneira adequada
para constituir um menu de ajuda na plataforma local. Em sistemas X/Motif esse procedimento
envolve ajustar a entrada do menu à extremidade direita da barra de menus.

Barra de menu

É o menu horizontal. Só pode ser adicionado a um objeto Frame e forma a raiz de todas as árvores
de menus.

Frame f = new Frame("Barra de Menu");


MenuBar mb = new MenuBar();
f.setMenuBar(mb);

Linguagem de Programação Java 105


O objeto MenuBar não suporta listeners. Isso porque é previsto que todos os eventos que ocorrem
na região de uma barra de menus serão processados automaticamente como parte do
comportamento normal do menu.

Menu

A classe Menu fornece um menu subseqüente básico, que pode ser adicionado a um MenuBar ou a
outro Menu.

MenuBar mb = new MenuBar();


Menu ml = new Menu("Arquivo");
Menu m2 = new Menu("Editar");
Menu m3 = new Menu("Ajuda");
mb.add(m1);
mb.add(m2);
mb.add(m3);
mb.setHelpMenu(m3);

Observação - Os menus mostrados aqui estão vazios, daí a aparência do menu Arquivo.

Linguagem de Programação Java 106


Você pode adicionar um ActionListener a um objeto Menu, embora isso não seja comum.
Normalmente, os menus são utilizados apenas para exibir e controlar itens de menu, que serão
abordados a seguir.

Item de menu

MenuItems constituem as ramificações textuais de uma árvore de menus. Em geral, são


adicionados a um menu para completar a estrutura.

Menu ml = new Menu("Arquivo");


MenuItem mil = new MenuItem("Salvar");
MenuItem mi2 = new MenuItem("Abrir");
MenuItem mi3 = new MenuItem("Sair");
m1.add(mi1);
m1.add(mi2);
m1.addSeparator();
m1.add(mi3);

Você normalmente adicionará um ActionListener a um objeto MenuItem para atribuir um


comportamento aos seus menus.

Item de menu de caixa de verificação


Esse é um item de menu que pode ser marcado, de forma a haver opções (ativado ou desativado)
listadas nos menus.

Menu ml = new Menu("Arquivo");


MenuItem mil = new MenuItem("Salvar");
CheckboxMenuItem mi2 = new CheckboxMenuItem("Persistente");
m1.add(mi1);
m1.add(mi2);

Linguagem de Programação Java 107


O CheckboxMenuItem deve ser monitorado através da interface ItemListener. Assim, o método
itemStateChanged () será chamado quando o estado da caixa de verificação for modificado.

Menu instantâneo
Fornece um menu autônomo que pode ser ativado em qualquer componente. Você pode adicionar
itens de menu ou menus a um menu instantâneo.

Frame f = new Frame("Menu Instantâneo");


Button b = new Button("me pressione");
PopupMenu = p new PopupMenu("Popup");
MenuItem s = new MenuItem("Salvar");
MenuItem l = new MenuItem("Abrir");
b.addActionListener(this);
f.add("Center", b);
p.add(s);
p.add(l);
f.add(p);

Observação - O PopupMenu deve ser adicionado a um componente "pai". Isso não equivale a
adicionar componentes comuns a recipientes. Neste exemplo, o pop-up foi adicionado à moldura
delimitadora

Para que o PopupMenu seja exibido, você deve chamar o método de exibição. A exibição exige uma
referência a um componente para funcionar como a origem das coordenadas x e y. Normalmente,
você utiliza o componente de acionamento para isso. Nesse caso, esse componente é o botão b.

public void actionPerformed(ActionEvent ev){


p.show(b, 10, 10);
}

Linguagem de Programação Java 108


Observação - O componente de origem deve estar abaixo ou contido no componente pai na
hierarquia de capacidade de conter.

Controlar aspectos visuais


Você pode controlar a aparência de componentes de AWT em termos da cor utilizada para o
primeiro e o segundo planos e a fonte utilizada para o texto.

Cores

Existem dois métodos para definir as cores de um componente. São eles

• setForeground()

• setBackground()

Esses dois métodos utilizam um argumento que constitui uma instância da classe
java.awt.Color. Você pode utilizar as cores constantes, que são chamadas Color.red
Color.blue etc. O conjunto completo de cores predefinidas está relacionado na página de
documentação referente à classe Color.

Além disso, você pode criar uma cor específica como esta:

int r = 255, g = 255, b = 0;


Color c = new Color(r, g, b);

Esse tipo de construtor criar uma cor com base nas intensidades especificadas (em uma faixa de 0
até 255 para cada) de vermelho, verde e azul.

Fontes

A fonte utilizada para a exibição de texto em um componente pode ser especificada através do
método setFont(). O argumento referente a esse método deve ser uma instância da classe
java.awt.Font.

Nenhuma constante é definida para fontes, mas você pode criar uma fonte especificando o nome
dela, o estilo e o tamanho em pontos.

Font f = new Font("TimesRoman", Font.PLAIN, 14);

Estes são nomes de fonte válido: Dialog, Helvetica, TimesRoman e Courier

Uma lista completa pode ser determinada chamando-se o método getFontList() no objeto
Toolkit. O kit de ferramentas pode ser obtido através do componente, após sua exibição, por meio
do método getToolkit(). Como alternativa, utilize o kit de ferramentas padrão, que pode ser
localizado através de Tookit.getDefaultToolkit().

As constantes de estilo de fonte que são, na verdade, valores int são

• Font.BOLD
• Font.ITALIC
• Font.PLAIN
• Font.BOLD + Font.ITALIC

Linguagem de Programação Java 109


Você deve especificar os tamanhos em ponto utilizando um valor int.

Impressão
A impressão é tratada no Java 1.1 ou superior de uma maneira bastante semelhante à exibição em
tela. Um tipo especial de objeto j ava. awt. Graphics é obtido para que qualquer instrução de
desenho enviada a esses elementos gráficos seja realmente destinada à impressora.

O sistema de impressão do Java 1.1 permite utilizar convenções de controle de impressora local
para que seja exibida uma caixa de diálogo ao usuário quando uma operação de impressão for
iniciada. 0 usuário poderá, então, escolher opções como o tamanho do papel, a qualidade de
impressão e a impressora a ser utilizada.

Frame f = new Frame("Teste de Impressão");


Toolkit t = f.getToolkit();
PrintJob job = t.getPrintJob(f, "MyPrintJob", null);
Graphics g = job.getGraphics();

Essas linhas criam um objeto Graphics que é "conectado" à impressora escolhida pelo usuário.
Obtenha um novo elemento gráfico para cada página.

f.printAll(g);

Você pode utilizar qualquer um dos métodos de desenho da classe de elemento gráfico para enviar
dados à impressora. O exemplo mostra que, como alternativa, você pode simplesmente solicitar a
um componente para que desenhe a própria imagem no elemento gráfico. O método print ()
solicita a um componente para que ele faça o desenho dessa maneira, mas ele só se relaciona com
o componente para o qual foi chamado. No caso de um recipiente, como o deste exemplo, você
pode utilizar o método printAll () para fazer com que o recipiente e todos os componentes nele
contidos sejam desenhados para a impressora.

g.dispose();
job.end();

Após criar a página de saída necessária, utilize o método dispose() para que essa página seja
submetida à impressora.

Após concluir o trabalho, chame o método end () no objeto de trabalho de impressão. Isso indica
que o trabalho de impressão foi concluído e permite que o sistema de spool da impressora execute
efetivamente o trabalho e, em seguida, libere a impressora para outros trabalhos.

Linguagem de Programação Java 110


Capítulo 10

Introdução aos Applets Java

Objetivos

Após concluir este módulo, você será capaz de:


• Descrever a diferença entre um aplicativo Java autônomo e um applet
Java
• Criar um indicador HTML para chamar um applet Java
• Descrever a hierarquia das classes do Applet e do AWT.
• Criar o applet Java Helloworld.
• Indicar e descrever os principais métodos de um applet.
• Descrever e utilizar corretamente o modelo de desenho do AWT.
• Utilizar métodos de applet para ler imagens e arquivos a partir de URLs.
• Utilizar indicadores <param> para configurar applets.

Linguagem de Programação Java 111


Que é um applet?
Applet é um código Java executado em um navegador HTTP. É diferente de um aplicativo na
maneira como é executado. Um aplicativo é iniciado quando o respectivo método main() é
chamado. Por contraste, o ciclo de vida de um applet é bem mais complexo. Este módulo examina
como você pode executar um applet, carregá-lo no navegador e criar o respectivo código.

Carregar um applet

Como o applet é executado em um navegador da Web, ele não é iniciado diretamente através de um
comando. Você deve criar um arquivo HTML que informe ao navegador o que deve ser carregado e
como deve ser executado. Em seguida, você "aponta" o navegador para a URL que especifica o
arquivo HTML.

O formato desse arquivo será descrito resumidamente.

Restrições de segurança do applet

Como os applets constituem códigos que são carregados via rede, eles representam uma
possibilidade inerentemente perigosa. O que aconteceria se alguém criasse um programa malicioso
que lesse seu arquivo de senha e o enviasse através da Internet?

Para impedir que isso ocorra, a linguagem Java fornece uma classe SecurityManager que controla o
acesso a quase toda chamada em nível de sistema na Máquina Virtual Java (JVM)> Esse modelo é
chamado modelo de segurança "sandbox". A JVM fornece uma sandbox que permite a execução de
applets. No entanto, se eles tentarem sair da sandbox, esta os impedirá.

A intensidade em que a segurança é controlada é implementada no nível do navegador. A maioria


dos navegadores (incluindo o Netscape) impede o seguinte:

• A execução de outro programa


• Qualquer E/S de arquivo
• Chamadas a qualquer método original
• Tentativas de abrir um soquete para qualquer host, exceto o host que forneceu o applet

Exemplo "Olá mundo!"

O código fonte do applet HelloWorld (contido no arquivo fonte Helloworld.java) é mostrado


abaixo:

1 //
2 // Exemplo do applet Olá mundo
3 //
4 import java.awt.Graphics;
5 import java.applet.Applet;
6
7 public class HelloWorld extends Applet {
8
9 String hw_text;
10
11 public void init () {
12 hw_text = "Olá mundo”;
13 }
14
15 public void paint(Graphics g){
16 g.drawSLring(hw_text, 25, 25);

Linguagem de Programação Java 112


17 }
18 }

Desenvolver um applet

Para desenvolver um applet, crie uma classe com este formato:

import java.applet.*;
public class HelloWorld extends Applet {

Para se tornar um applet, a classe deve ser pública e o nome do arquivo deve corresponder ao
nome da classe do applet, nesse caso, Helloworld.java. Além disso, essa classe deve ser uma
subclasse da classe java.applet.Applet.

Hierarquia de classes do applet

A classe java.applet.Applet é, na verdade, uma subclasse de java.awt.Container. É útil


se ter uma idéia de toda a hierarquia das classes do Applet e do AWT.

java.lang.object

java.awt.Component

Java.awt.Container

Java.awt.Button

Java.awt.Frame Java.awt.Panel

Java.awt.TextArea

Java.awt.Applet

Essa hierarquia mostra que um applet pode ser utilizado diretamente como o ponto inicial de um
layout de AWT. Como o applet é um painel, ele possui, por padrão, um gerenciador de layout de
fluxo.

Métodos principais de applet

Em um aplicativo, o programa é inicializado no método main(). Em um applet, porém, isso não


ocorre. O primeiro código executado por um applet é o definido para a inicialização e o seu
construtor.

Após a conclusão do construtor, o navegador chama um método do applet denominado init() . O


método init() deve executar a inicialização básica do applet e, em seguida, ser encerrado. Após o
termino do init(), o navegador chama outro método denominado start(). Descreveremos esse
método com mais detalhes posteriormente.

Os métodos init e start são totalmente executados antes de o applet se tornar "ativo" e, por
isso, não podem ser utilizados para programar o comportamento atual em um applet. Na verdade,
ao contrário do método main() em um aplicativo simples, não existe um método que seja
executado continuamente durante o cicio de vida do applet. Você vera, mais adiante, como
conseguir isso utilizando threads, mas, por enquanto, não é possível fazer isso.
Linguagem de Programação Java 113
Criar um applet

Exibição do applet

Os applets são essencialmente gráficos. Por isso, embora você possa emitir chamadas
System.out.println(), você não irá fazê-lo normalmente. Em vez disso, você criará a exibição
em um ambiente gráfico.

Você pode desenhar em uma applet, criando um método paint(). O método paint() é chamado
pelo ambiente de navegação sempre que for necessário atualizar a exibição do applet. Isso
acontece, por exemplo, quando a janela do navegador é ativada após ter sido minimizada.

Você deve criar o método paint() de modo que ele funcione corretamente sempre que for
chamado. Isso porque sua exposição é controlada pelo ambiente e não pelo programa, além de
ocorrer de forma assíncrona.

O método paint() e os gráficos

O método paint() contém um argumento que é uma instância da classe java.awt.Graphics.


Você pode utilizá-lo para desenhar ou criar texto no applet. Veja a seguir um pequeno exemplo de
um applet que utiliza um método paint() para criar um texto:

import java.awt.*;
import java.applet.*;

public class HelloWorld extends Applet {

public void paint (Graphics g) {

g.drawString("Ola mundo!", 25, 25);


}
}

Observação - Os argumentos numéricos referentes ao método drawString são as coordenadas


de pixel x e y para o início do texto. Essas coordenadas se referem à linha de base da fonte.
Portanto, se você digitar na coordenada zero de y, fará com que a massa do texto fique fora da parte
superior da exibição. Apenas os descendentes, como a haste de uma letra "y", ficarão visíveis.

Métodos e ciclo de vida do applet

O ciclo de vida do applet é um pouco mais complexo do que já vimos até agora. Há três métodos
principais que se relacionam a esse ciclo: init(), start() e stop().

init()

Essa função membro é chamada quando o applet é criado e carregado em um navegador Java
(como o AppletViewer). O applet pode utilizar esse método para inicializar valores de dados. Esse
método só é chamado na primeira vez que o navegador abre a página que contém o applet.

start()

O método start() é chamado para informar ao applet que ele deve se tornar "ativo". Isso ocorre
quando o applet é iniciado pela primeira vez, após a execução do método init(). Também ocorre
quando o navegador é restaurado após ser iconizado ou quando ele retorna à página que contém o

Linguagem de Programação Java 114


applet após passar para outro URL. Isso significa que o applet pode utilizar esse método para
realizar tarefas, como iniciar uma animação ou executar sons.

1 public void start(){


2 musicClip.play();
3 }

stop()

O método stop() é chamado quando o applet é "desativado". Isso ocorre quando o navegador é
iconizado ou segue um link para outro URL. O applet pode utilizar esse método para realizar tarefas,
como parar uma animação.

1 public void stop() {


2 musicClip.stop();
3 }

Os métodos start() e stop() formam efetivamente um par. O start() pode ser utilizado para
acionar um comportamento no applet, enquanto o stop() é utilizado para desativar esse
comportamento.

Desenho no AWT
Além dos métodos básicos ciclo de vida, um applet possui métodos importantes relacionados à
exibição. Na realidade, esses métodos são declarados e documentados para uma classe
Component do AWT, e é importante seguir o modelo correto para tratar a exibição utilizando o AWT

A atualização da exibição é feita por um thread separado, que chamaremos thread de AWT. Esse
thread pode ser chamado para tratar duas situações que se relacionam a essa atualização.

A primeira dessas condições é a exposição, onde parte da exibição tiver sido danificada e precisar
ser substituída. Isso pode ocorrer a qualquer momento e o seu programa deve ser capaz de lidar
com essa situação.

A segunda condição ocorre quando o programa decide redesenhar a exibição com novo conteúdo.
Para isso, pode ser necessário que a imagem antiga seja removida primeiro.

O método paint(Graphics g)

O tratamento da exposição ocorre automaticamente e resulta em uma chamada ao método paint


(). Um recurso da classe Graphics, chamado retângulo de recorte, é utilizado para otimizar o
método de desenho, de modo que as atualizações não sejam feitas em toda a área do elemento
gráfico, mas apenas na região que foi danificada.

O método repaint()

Por outro lado, você pode avisar ao sistema que deseja alterar a exibição através do método
repaint().

O método update(Graphics g)

Repaint faz com que o thread do AWT possa chamar outro método denominado update(). O
comportamento do método update normalmente consiste em limpar a exibição atual e, em seguida,
chamar o paint().

Linguagem de Programação Java 115


Esse modelo exige que você adote uma estratégia específica para manter a exibição.

• Manter um modelo da exibição. Isso pode ser feito através de várias formas. Você pode utilizar
um mapa dos valores de pixel ou uma técnica de nível superior, como uma matriz de formas.

• O método paint() deve renderizar a exibição com base apenas no conteúdo do modelo. Isso
permite que ele regenere a exibição de forma consistente, sempre que for chamado. Por isso, a
exposição é tratada corretamente.

• Quando o programa deseja alterar a exibição, ele deve atualizar o modelo e, em seguida,
chamar o método repaint() para que o método update() seja chamado pelo thread do AWT.

Observação - Um único thread do AWT trata de todo desenho de componentes e da distribuição de


eventos de entrada. Por isso, você deve evitar utilizar o método paint() ou update() por muito
tempo. Em situações extremas, você precisará da ajuda de outros threads para fazer isso. A
programação de threads é o assunto de um módulo posterior.

Este diagrama mostra como os métodos paint(), update() e repaint() estão relacionados.

Linguagem de Programação Java 116


O Appletviewer

O appletviewer é um aplicativo Java que permite executar applets sem a utilização de um


navegador da Web.

Observação - Qualquer navegador Java executa as mesmas funções básicas que as descritas aqui
para o appletviewer.

O que é o appletviewer?

Normalmente, um applet é executado em um navegador da Web com Java, como o HotJava ou o


Netscape Navigator TM . No entanto, para simplificar e acelerar o desenvolvimento, o JDK contém
uma ferramenta simples criada apenas para visualizar applets e não páginas HTML. Essa
ferramenta é o appletviewer.

O appletviewer lê a HTML de um URL fornecido na linha de comando. Nessa HTML, ele espera
encontrar instruções para carregar e executar um ou mais applets. Ele ignora qualquer instrução
HTML que não essas instruções. Por isso, o appletviewer não exibe a HTML normal e não
incorpora applets em uma página de texto.

Chamar applets com o appletviewer

0 appletviewer se assemelha ao navegador mínimo. Ele espera carregar o nome de um arquivo


de HTML como um argumento. Esse arquivo contém um indicador HTML que especifica o código
carregado pelo appletviewer:

<html>
<applet code=HelloWorld.class width=100 height=100>
</applet>
</html>

O appletviewer cria um espaço para navegador, incluindo uma área gráfica, na qual o applet será
executado e, em seguida, chama a classe de applet apropriada. Neste exemplo, o appletviewer
carrega urna classe chamada HelloWorld e permite que ela trabalhe com o espaço gráfico.

Linguagem de Programação Java 117


appletviewer e hierarquia de applets

O diagrama abaixo mostra a hierarquia de herança de classes referente ao appletviewer e aos


applets.

java.lang.Object

java.lang.Component

java.lang.Container

java.lang.Panel java.lang.Window

java.lang.Frame

java.lang.Applet sun.applet.AppletViewer

Utilizar o appletviewer

Sinopse

appletviewer [-debug] urls ...

O appletviewer recebe o nome de um arquivo de HTML que contém o indicador do <applet> ou


o nome de um URL para o arquivo de HTML.

Se o arquivo de HTML passado para o appletviewer não contiver um indicador <applet> válido,
o appletviewer não executará qualquer ação. O appletviewer não exibe outros indicadores
HTML.

A única opção válida para o appletviewer e o sinalizador -debug, que inicializa o applet no
depurador de Java jdb. Compile o código Java com a opção -g para ver o código fonte no
depurador.

Exemplo

O comando appietviewer mostrado abaixo inicializa o appletviewer:

C:\> appletviewer HelloWorld.html

Esse procedimento cria e exibe esta pequena janela:

Linguagem de Programação Java 118


O indicador applet

Sintaxe

Veja a seguir a sintaxe completa do indicador applet:

<applet
archive=archiveList
code=appletFile. class
width=pixels height=pixels
[codebase=codebase URL]
[alt=alternateText]
[name=appletInstanceName]
[align=alignment]
[vspace=pixels] [hspace=pixels]
>
[<param name=appletAttributel value=value>]
[<param name=appletAttribute2 value=value>]
...
[alternateHTML]
</applet>

Descrição

• archive=archiveList - Esse atributo opcional descreve um ou mais arquivos que contêm


classes e outros recursos que serão "pré-carregados". As classes são carregadas através de
uma instância de um AppletClassLoader com o CODEBASE especificado. Os arquivos
contidos em archiveList são separados por “,”.

• code=appletFile.class - Esse atributo necessário atribui o nome do arquivo que contém a


subclasse Applet compilada para o applet. Tambem pode assumir o formato
Package.appletFile.class.

Observação - Esse arquivo refere-se ao URL básico do arquivo de HTML do qual você o está
carregando. Ele não pode conter um nome de caminho. Para alterar o URL básico do applet,
consulte o indicador codebase.

• width=pixels height = pixels - Estes atributos necessários definern a largura e a altura iniciais
(em pixels) da área de exibição do applet, sem considerar qualquer janela ou caixa de dialogo
que o applet venha a ativar.

Linguagem de Programação Java 119


• codebase=codebaseURL - Este atributo opcional especifica o URL básico do applet, que é o
diretório que contém o código do applet. Se esse atributo não for especificado, o URL do
documento será utilizado.

• alt=alternateText - Este atributo opcional especifica qualquer texto que deverá ser exibido se o
navegador compreender o indicador applet, mas não conseguir executar applets Java.

• name=appletInstanceName - Este atributo opcional especifica um nome para a instância do


applet. Isso permite que os applets contidos na mesma página se localizem e se comuniquem.

• align=alignment - Este atributo opcional especifica o alinhamento do applet. Os valores


possíveis desse atributo são os mesmos que os do indicador IMG contido na HTML básica:
left, right, top, texttop, middle, absmiddle, baseline, bottom e absbottom.

• vspace=pixels hspace=pixels - Estes atributos opcionais especificam o número de pixels que


se encontram acima e abaixo do applet (vspace) e nas laterais do applet (hspace). São
tratados da mesma maneira que os atributos vspace e hspace do indicador IMG.

• <param name=appletAttribute1 value=value> - Este indicador consiste na única maneira de


especificar um atributo específico do applet. Os applets acessam os respectivos atributos com o
método getparameter ().

Os navegadores sem Java exibirão qualquer HTML comum incluída entre o <applet> e as tags
</applet>. Os navegadores com Java ignoram a HTML comum entre esses dois indicadores.

O URL que você especifica como argumento para o appletviewer deve conter instruções
para carregar um applet. Essas instruções assumem a forma de um indicador de applet que,
em sua forma mais simples, tem esta aparência:

<applet code=HelloWorld.class width=100 height=100>


</applet>

Observe que o formato geral desse indicador e o mesmo que o de qualquer outra HTML, utilizando
os símbolos < e > para delimitar as instruções. Como todas as partes mostradas aqui são
necessárias, você precisa ter <applet . . .> e </applet>. A parte <applet ... > deve
especificar uma entrada de código, além de uma largura e uma altura.

Observação - Em geral, você deve tratar applets como sendo de tamanho fixo e utilizar o tamanho
especificado no indicador de applet. É possível, em determinadas condições, alterar o tamanho de
um applet. Entretanto, os resultados nem sempre serão adequados

Recursos adicionais de applet

Diversos recursos adicionais estão disponíveis em um applet e serão abordados aqui.

Todos os programas Java têm acesso aos recursos de rede, utilizando as classes do pacote
java.net. Além disso, os applets contêm metodos que permitem determinar informações sobre o
ambiente de navegação em que foram iniciados.

A classe java.net.URL descreve URLs e pode ser utilizada para se conectar a eles. Dois métodos
da classe Applet podem determinar o valor de URLs significativas:

• getDocumentBase() retorna um objeto URL que descreve a pagina atual do navegador.

Linguagem de Programação Java 120


• getCodeBase() retorna um objeto URL que descreve a origem do próprio código do applet. Em
geral, isso é o mesmo que a página atual, mas não é necessariamente o caso.

Utilizando o objeto URL como ponto inicial, você pode trazer sons e imagens para o applet.

• getImage(URL base, String target) traz uma imagem do arquivo denominado target,
localizado no URL especificado por base. O valor retornado é uma instância da classe Image.

• getAudioClip(URL base, String target) traz um som do arquivo denominado pelo


target, localizado no URL especificado por base. O valor retornado é um instância da classe
AudioClip.

Um teste de imagem simples

O applet a seguir obterá o arquivo de imagem graphics/javalogo.gif relativo ao caminho de


diretorio retornado pelo método getDocumentBase e o exibirá no appletviewer:

1 //Olá mundo ampliado para o desenho de uma imagem


2 //Pressupõe a existência de
3 //"graphics/javalogo.gif"
4 //
5 import java.awt.*;
6 import java.applet.Applet;
7
8 public class HwImage extends Applet {
9 Image duke;
10
11 public void init () {
12 duke = getImage(getDocumentBase (),
13 "graphics/javalogo.gif");
14 }
15
16 public void paint(Graphics g) {
17 g.drawImage(duke, 25, 25, this);
18 }
19 }

Estes são os argumentos do método drawlmage ():

1. O objeto Image a ser desenhado

2. A coordenada X referente ao desenho

3. A coordenada Y referente ao desenho

4. O objeto observer da imagem. O observador da imagem consiste na informação que deve ser
fornecida se a imagem for alterada.

Uma imagem carregada pelo getImage() será alterada após a primeira emissão da chamada. Isso
ocorre porque a carga é efetuada em segundo plano. Toda vez que mais uma parte da imagem é
carregada, o método paint() é chamado novamente. Isso ocorre porque o applet foi registrado
como um observador, tendo sido passado no quarto argumento da chamada de drawImage().

Clipes de áudio

Linguagem de Programação Java 121


A linguagem Java também possui métodos para executar clipes de áudio. Esses métodos estão
contidos na classe java.applet.AudioClip. Obviamente, você precisa ter o hardware
apropriado para que o computador execute áudio.

Executar um clipe

A maneira mais fácil de ouvir um clipe de áudio é através do método play do applet:

play(URL soundDirectory, String soundfile);

ou simplesmente:

play(URL soundURL);

Um URL comum a ser utilizado com o método play() é o diretório em que os arquivos de HTML
estão localizados. Acesse esse local através do método getDocumentBase().

play(getDocumentBase(), "bark.au");

Para que isso funcione, o arquivo de HTML e o arquivo bark.au devem se encontrar no mesmo
diretório.

Um teste simples de áudio

O applet a seguir imprime a mensagem "Teste de áudio" no appletviewer e, em seguida,


executa o arquivo de áudio sounds/cuco.au:

1 //
2 //olá mundo ampliado para executar áudio
3 //Pressupõe a existência do arquivo "sounds/cuco.au"
4 //
5
6 import java.awt.Graphics;
7 import java.applet.Applet;
8
9 public class HwAudio extends Applet {
10
11 public void paint(Graphics g) {
12 g.drawString("Teste de Audio", 25, 25);
13 play(getDocumentBase (),"sounds/cuco.au");
14 }
15 }
16

Executar um loop em um clipe de áudio

Você também pode tratar clipes de áudio como imagens. Você pode carregá-las e executá-Ias
posteriormente.

Carregar um clipe de áudio

Para executar um clipe de áudio, utilize o método getAudioClip contido na classe


java.applet.Applet:

AudioClip sound;

Linguagem de Programação Java 122


sound = getAudioClip(getDocumentBase (), "bark.au");

Após carregar um clipe, utilize os três métodos associados a ele: play, loop ou stop.

Executar um clipe de áudio

Utilize o método play da classe java.applet.AudioClip para executar o clipe de áudio


carregado:

sound.play();

Para iniciar a execução do clipe e executar um loop nele (fazê-lo repetir automaticamente), utilize o
método loop da classe java.applet.AudioClip:

sound.loop();

Parar um clipe de áudio

Para parar a execução de um clipe, utilize o método stop da classe java.applet.AudioClip:

sound.stop();

Um teste simples de loop

Eis um exemplo em que um loop é executado automaticamente atraves de um clipe de áudio


carregado:

1 //
2 // Ola mundo ampliado para executar um loop em uma trilha de audio
3 // Pressupõe a existência de "sounds/cuco.au"
4
5
6 import java.awt.Graphics;
7 import java.applet.*;
8
9 public class HwLoop extends Applet {
10 AudioClip sound;
11
12 public void init() {
13 sound = getAudioClip(getDocumentBase (),
14 "sounds/cuco.au");
15 }
16
17 public void paint(Graphics g) {
18 g.drawString("Teste de Audio", 25, 25);
19 }
20
21 public void start () {
22 sound.loop();
23 }
24
25 public void stop () {
26 sound.stop();
27 }
28 }

Linguagem de Programação Java 123


Entrada do mouse

Um dos recursos mais úteis suportados pela linguagem Java consiste na interatividade direta. Um
aplicativo pode monitorar o mouse e reagir a alterações efetuadas neste.

O modelo de evento 1.1 suporta um tipo de evento para cada tipo de interatividade. Os eventos do
mouse são recebidos por classes que implementam a interface MouseListener, que recebe os
eventos de:

• mouseClicked - Quando você clica o mouse (o botão do mouse é pressionado e liberado em


um só movimento)

• mouseEntered- Quando o cursor do mouse entra em um componente

• mouseExited - Quando o cursor do mouse sai de um componente

• mousePressed - Quando o botão do mouse é pressionado

• mouseReleased - Quando o botão do mouse e liberado

Um teste simples de mouse

O programa abaixo exibe o local do clique no mouse dentro do applet.

1 //
2 // olá mundo ampliado para monitorar a entrada do mouse
3 // O "Olá mundo!" é reimpresso no local do
4 // clique no mouse.
5 //
6
7 import java.awt.*;
8 import java.awt.event.*;
9 import java.applet.Applet;
10
11 public class HwMouse extends Applet
12 implements MouseListener {
13
14 int mouseX=25;
15 int mouseY=25;
16
17 //Registrar o mousePressed Event
18 public void init () {
19 addMouseListener (this);
20 }
21
22 public void paint(Graphics g) {
23 g.drawString("Olá mundo!", mouseX, mouseY);
24 }
25
26 public void mousePressed(MouseEvent evt){
27 mouseX = evt.getX ();
28 mouseY = evt.getY ();
29 repaint ();
30 }
31
32 // Não estamos utilizando os outros eventos do mouse

Linguagem de Programação Java 124


33 public void mouseClicked (MouseEvent e) {}
34 public void mouseEntered (MouseEvent e) {}
35 public void mouseExited (MouseEvent e) {}
36 public void mouseReleased (MouseEvent e) {}
37
38 }

Ler parâmetros

Em um arquivo HTML, uma tag de applet pode passar informações de configuração para o applet.
Para isso, utilize o tag <param>. Por exemplo:

<applet code=configureMe.class width=100 height=100>


<param name=image value=duke.gif>
</applet>

Dentro do applet, você pode utilizar o método getParameter () para ler esses valores.

import java.awt.*;
import java.applet.*;

public class DrawAny extends Applet {


Image im;

public void init ()


URL mypage = getDocumentBase ();
String imageName = getParameter(“image");
im = getImage(myPage, imageName);
}

public void paint(Graphics g) {


g.drawImage(im, 0, 0, this);
}
}

Se não conseguir encontrar o nome do parâmetro em qualquer <param> contida no par


<applet></applet>, então o método getParameter retornará um valor nulo. Um programa de
produção deve tratar essa situação adequadamente.

O tipo do parâmetro é sempre String. Se você necessitar dele em outros formatos, terá de
convertê-lo. Por exemplo, para ler um parâmetro int:

int speed = Integer.parseInt(getParameter("speed"));

Em virtude da natureza do código HTML, os parâmetros não fazem distinção entre maiúsculas e
minúsculas. No entanto, será um bom estilo adotar apenas maiúsculas ou minúsculas sempre que
aparecerem.

Linguagem de Programação Java 125


Código de dupla finalidade

A partir de um único arquivo de classe, é possível criar um código que seja utilizado como um applet
Java e um aplicativo Java. Você terá um pouco mais de trabalho para compreender os requisitos do
aplicativo mas, uma vez criado, o código do applet/ aplicativo poderá ser utilizado como modelo para
programas mais complexos.

Applet/Aplicativo

Este e o exemplo do "Olá mundo" codificado como um applet/ aplicativo:

1 //Exemplo do Olá mundo como


2 //um applet e um aplicativo
3 //
4
5 import java.applet.Applet;
6 import java.awt.*;
7 import java.awt.event.*;
8
9 public class AppletApp extends Applet {
10
11 //Um aplicativo necessitara de um método main ()
12 public static void main(String args[]) {
13
14 // Cria uma moldura para armazenar o applet
15 Frame frame = new Frame("Aplicação");
16
17 // Cria uma instância da classe (applet)
18 AppletApp app = new AppletApp();
19
20 // Adiciona ao centro da moldura
21 frame.add("Center",app);
22 frame.setSize(200, 200);
23 frame.setVisible(true);
24
25
26 // Registra a classe AppletApp como o
27 // ouvidor de um evento Window Destroy
28 frame.addWindowListener
29 (new WindowControl(app));
30 // Chama os métodos do applet
31 app.init();
32 app.start();
33 } // finalizar main
34
35 public void paint(Graphics g) {
36 g.drawstring("Olá mundo", 25, 25);
37 }
38
39 public void destroy() {
40 System.exit(0);
41 }
42 }
43
44 // Classe utilizada para controlar a janela do applet
45 //
46 class WindowControl extends WindowAdapter {
47

Linguagem de Programação Java 126


48 Applet c;
49
50 public WindowControl(Applet c) {
51 this.c = c;
52 }
53
54 public void windowClosing(WindowEvent e) {
55 c.destroy();
56 }
57 }

Linguagem de Programação Java 127


Linguagem de Programação Java 128
Capítulo 11

Exceções da linguagem Java

Objetivos

Após concluir este módulo, você será capaz de:


• Definir exceções.
• Explicar porque o tratamento de exceções é importante.
• Criar um código para detectar exceções.
• Criar suas próprias exceções.

Linguagem de Programação Java 129


Exceções
O que é uma exceção? Na linguagem Java, a classe Exception define as condições de erro
moderadas que poderão ocorrer em seus programas. Você pode criar um código para tratar as
exceções e continuar o programa, em vez de fechá-lo.

Então, o que é um erro? Em Java, a classe Error define as condições de erro consideradas
bastante sérias, das quais você não deve tentar se recuperar. Na maioria dos casos, é aconselhável
permitir o término do programa quando ocorrer um erro, ou o programa termina sem o controle do
usuário.

A linguagem Java implementa exceções no estilo C++ para ajudá-lo a criar um código com
capacidade de recuperação rápida. Quando ocorre no programa, o código que encontra esse erro
pode “lançar” uma exceção. O processo de lançar uma exceção consiste em avisar ao processo em
execução que ocorreu um erro. Em seguida, você poderá detectar a exceção e, quando possível,
recuperar-se dela.

Exemplo de exceção

Considere uma extensão simples da versão do programa OlaMundo.java:

1 public class OlaMundo {


2 public static void main(String args[]) {
3 int i=0;
4
5 String greeting [] = {
6 “Olá Mundo!”, “Não, eu digo!”, “OLÁ MUNDO!!” };
7 }
8
9 while (i<4) {
10 System.out.println (greetings[i]);
11 i++;
12 }
13 }
14 }

Tratamento de exceções
Normalmente, um programa termina com uma mensagem de erro quando você lança uma exceção,
como faz o programa mostrado anteriormente após o loop ter sido executado quatro vezes.

$ java OlaMundo
Olá mundo!
Não, eu digo!
OLÁ MUNDO!!
Java.lang.ArrayIndexOutOfBoundsException: 3
At OlaMundo.main(OlaMundo.java):12)

No entanto, a importância do tratamento de exceções é que você pode codificar o programa para
detectá-las, tratá-las e, em seguida, continuar a execução.

A linguagem Java possui utilitários que lhe permitem descobrir qual exceção foi lançada e, em
seguida, tentar se recuperar.

Declarações try e catch

Linguagem de Programação Java 130


Para tratar uma exceção específica, utilize a declaração try com o código que deverá lançar essa
exceção. Para detectar e tratar exceções lançadas, utilize a instrução catch para especificar a
exceção a ser detectada e o código a ser executado se a exceção for lançada.

try {
// código que pode lançar uma exceção específica
} catch (MyExceptionType e) {
//código a ser executado se MyExceptionType for lançada
}

Declaração finally

A declaração finally define um bloco de código que você sempre deve executar,
independentemente do fato de uma exceção ter sido ou não detectada. O exemplo de código e a
descrição a seguir foram extraídos do “white paper” (esboço de especificação) de Frank Yellin, Low
Level Security in Java (Segurança em nível inferior na linguagem Java):

try {
abrirTorneira();
correrAgua();
} finally {
fecharTorneira();
}

A linguagem Java garante que a “torneira seja fechada mesmo que ocorra uma exceção ao abrir
essa torneira ou molhar a grama”. O código delimitado entre chaves após try é chamado código
protegido.

A instrução finally só não será executada se o método System.exit()for executado dentro do


código protegido. Em seguida, o programa termina imediatamente.

Exemplo recriado

O exemplo a seguir a uma recriação do método main() contido no programa OlaMundo.java. A


exceção gerada na versão anterior do programa é detectada e o índice do array é redefinido como 0.

1 public static void main (String args[])


2 int i = 0;
3 String greetings [] = {
4 "Ola mundo!", "Não, eu digo!","OLA HELLO MUNDO!!" };
8 while (i < 4) {
9 try
10 System.out.println (greetings[i]);
11 } catch (ArrayIndexOutOfBoundsException e){
12 System.out.println("Reinicializando valor do indice");
13 } catch (Exception e)
14 System.out.println(e.toString());
15 } finally
16 System.out.println("Isto é sempre impresso");
17 }
18 i++;
19 } // finalizar while()
20 } // finalizar main()

A mensagem exibida na tela alterna entre o seguinte, enquanto o loop é executado:

Linguagem de Programação Java 131


Ola mundo!
Isto é sempre impresso
Não, eu digo!
Isto é sempre impresso
OLA MUNDO!!
Isto é sempre impresso
Reinicializando valor do índice
Isto é sempre impresso

Linguagem de Programação Java 132


Exceções
Exceções comuns

A linguagem Java fornece várias exceções predefinidas. Estas exceções são as mais comuns que
você pode encontrar:

• ArithmeticException - Normalmente, o resultado de uma operação de divisão de inteiros


por zero:

int i = 12 / 0;

• NullPointerException - Uma tentativa de acessar um objeto ou método antes de ele ser


instanciado:

Image im [] = new Image [4]; System.out.println(im[0].toString());

• NegativeArraySizeException - Uma tentativa de criar uma matriz com uma dimensão


negativa.

• ArrayIndexOutOfBoundsException - Uma tentativa de acessar um elemento de uma


matriz além da definição original do tamanho da matriz

• SecurityException - Normalmente, exibida em um navegador, a classe


SecurityManager lança uma exceção em applets que tentam:

• Acessar um arquivo local.


• Abrir um soquete que não seja o que leva de volta ao host que serviu ao applet.
• Executar outro programa em um ambiente de execução.

Categorias de exceção
Existem três categorias principais de exceções na linguagem Java. Na verdade, a Java
define a classe java.lang.Throwable, que funciona como a classe pai de todos os objetos que
possam ser lançados e detectados através dos mecanismos de tratamento de exceções. Há três
subclasses essenciais dessa classe, que são mostradas no diagrama abaixo.

A classe Throwable não deve ser utilizada. Em vez disso, uma das outras três descreverá
qualquer exceção especifica. A finalidade de cada uma a descrita abaixo.
• Error indica um problema sério do qual a recuperação a difícil ou ate impossível Um
exemplo e a falta de memória E improvável que um programa consiga tratar tais
condições embora seja possível com cautela.

• RuntimeException deve ser utilizada para indicar um problema de projeto ou


implementação Isto e, deve ser utilizada para indicar condições durante a execução
que nunca deverão acontecer se o programa estiver funcionando corretamente. Isso
se aplicaria, por exemplo, a subscritos de uma matriz fora de limites e ao
cancelamento da referência a uma variável de objeto nulo. Como um programa
devidamente projetado a implementado nunca lança esse tipo de exceção é normal
não tratá-la. Isso gera uma mensagem no tempo de execução e garante que uma
ação pode ser executada para corrigir o problema, em vez de ocultá-lo onde isso não
será notado.

Linguagem de Programação Java 133


• Outras exceções indicam uma dificuldade no tempo de execução que pode ocorrer
em virtude de efeitos ambientais. Essas exceções podem ser tratadas. Por exemplo,
se o usuário cometer um erro de digitação poderão ocorrer situações como um
arquivo não encontrado ou um URL formado incorretamente. Como essas exceções
podem ocorrer em consequencia de um erro do usuário ou de situações fora de
controle, a linguagem Java incentiva o programador a tratá-las.

A regra "declarar ou tratar"


Para incentivar a criação de códigos eficientes, a linguagem Java exige que, se um método executar
alguma ação que possa resultar em uma Exception, o que é diferente de um Error ou
RuntimeException, ele deverá determinar a ação a ser executada se ocorrer o problema.

Ha duas ações que o programador pode executar para satisfazer a essa exigência Um bloco try {
} catch () {} , em que a detecção atribui nomes a qualquer superclasse da exceção lançada a
considerado para a solução do problema, mesmo que o bloco de detecção esteja vazio.

Como alternativa, você pode indicar que a exceção não seja tratada nesse método a que ela, por
conseguinte, seja lançada no método de chamada. Para fazer isso, basta marcar a declaração do
método com uma clausula throws, da seguinte maneira:

public void metodoQualquer() throws IOException

Depois da palavra-chave throws se encontra uma lista de todas as exceções que podem ocorrer
em um método, porem sem um tratamento no local. Embora apenas uma exceção seja mostrada
nesse exemplo, uma lista separada por vírgulas poderá ser utilizada se varias exceções não forem
tratadas no método.

Linguagem de Programação Java 134


Criar suas próprias exceções
Você também pode criar a própria exceção através da seguinte sintaxe:

public class MyException extends Exception {


private String reason;
public MyException (String cause) {
reason = cause;
}
public String getReason() {
return reason;
}
}

Para lançar uma exceção criada por você, utilize esta sintaxe:

throw new MyException( );

Exemplo

Considere um programa cliente-servidor. No código do cliente você tenta se conectar ao servidor e


espera que este responda em cinco segundos. Se o servidor não responder, o código poderá lançar
uma exceção (como uma ServerTimedoutException definida pelo usuário).

Public void connectMe(String serverName) throws ServerTimedOutException {


int success;
int portToConnect = 80;
success = open(serverName, portToConnect);
if (success == -1) throw new ServerTimedOutException();
}
}

Criar a própria exceção


Para detectar a própria exceção utilize a instrução try.

public void findServer () {


...
try {
connectMe(defaultServer);
} catch(ServerTimedOutException e) {
g.drawString("Servidor não respondeu, tentando outro", 5,5);
try {
connectMe(alternateServer);
} catch (ServerTimedoutException e1)
g.drawString("não ha nenhum servidor disponível no momento",5, 5);
}
}
...

Linguagem de Programação Java 135


Linguagem de Programação Java 136
Capítulo 12

Streams (fluxo) E/S e arquivos

Objetivos

Após concluir este módulo, você será capaz de:


• Descrever e utilizar a filosofia de streams do pacote java.io.
• Construir streams de filtro e arquivo e utilizá-los corretamente.
• Distinguir readers e writers a partir dos streams e selecioná-los
adequadamente.
• Examinar e manipular arquivos e diretórios.
• Ler, gravar e atualizar arquivos de texto e de dados.
• Utilizar a interface de Serialização para persistir o estado dos objetos.

Linguagem de Programação Java 137


Fluxo de E/S em Java
Este módulo examina como a linguagem Java utiliza fluxos para tratar E/S de byte e caractere
(inclusive stdin, stdout e stderr). As últimas seções examinam alguns detalhes específicos do
tratamento de arquivos e a forma como trabalhar os dados neles contidos.

Princípios do fluxo

Um fluxo é uma fonte de bytes ou o destino dos bytes. A ordem é significativa. Assim, por exemplo,
um programa que queira ler a partir do teclado poderá utilizar um fluxo para fazer isso.

As duas categorias básicas de fluxo são: fluxos de entrada e fluxos de saída. É possível ler a partir
de um fluxo de entrada, mas é impossível gravá-lo. Da mesma forma, é possível gravar num fluxo de
saída, mas é impossível lê-lo. É claro que para ler bytes a partir de um fluxo de entrada, é
necessário ter uma origem de caracteres associada ao fluxo.

No pacote java.io alguns fluxos são fluxos de "nó", ou seja, lêem a partir de um lugar específico
por exemplo, um arquivo de disco ou uma área de memória ou gravam neles. Outros fluxos são
denominados flltros. Um fluxo de entrada de filtro é criado com uma conexão com um fluxo de
entrada existente, de forma que quando tentar ler a partir do fluxo de entrada de filtro ele lhe fornece
caracteres que originariamente vieram do outro fluxo de entrada.

Nó InputStream FileInputStream
(a partir de arquivo em disco)

Métodos de fluxo de entrada

int read()
int read(byte[])
int read(byte[], int, int)

Estes três métodos fornecem acesso aos dados provenientes da conexão. O método de leitura
simples retorna um int que contém um byte lido a partir do fluxo ou -1 que indica a condição de fim
de arquivo. Os outros dois são lidos numa matriz de byte e retornam o número de bytes lidos. Os
dois argumentos int no terceiro método indicam um sub-intervalo a ser preenchido na matriz alvo.

Observação - Para maior eficiência, sempre leia dados nos blocos práticos maiores

void close()

Quando tiver terminado um fluxo, feche-o. Se tiver uma "pilha" de fluxos, utilizando fluxos de filtro,
feche o fluxo no topo da pilha. Esta operação fecha os fluxos mais baixos.

int available()

Linguagem de Programação Java 138


Isto relata o número de bytes imediatamente disponíveis para serem lidos a partir do fluxo. Uma
operação de leitura real depois dessa chamada pode retornar mais bytes.

skip(long)

Este método descarta o número especificado de caracteres do fluxo.

markSupported()
mark(int)
reset()

Estes métodos são utilizados para executar operações de "push back" num fluxo se o fluxo as
suportar. 0 método markSupported retornará true se os métodos mark() e reset() estiverem
operacionais para o fluxo em particular. O método mark (int) é utilizado para indicar que o ponto
atual no fluxo deve ser observado e deve ser alocado um buffer suficientemente grande para
guardar pelo menos o número especificado de caracteres do argumento. Depois das operações
subseqüentes read(), a chamada do método reset() retorna o fluxo de entrada para o ponto
anterior.

write(int)
write(byte[j)
write(byte[], int, int)

Estes métodos gravam no fluxo de saída. Assim como na entrada, você deve sempre tentar gravar
os dados no maior bloco prático.

close()

Fluxos de saída devem ser fechados quando tiverem sido terminados. Novamente, se tiver uma
pilha e fechá-la no topo, isto fechará o restante dos fluxos.

flush()

Algumas vezes um fluxo de saída pode acumular gravações antes de confirmá-las. O método
flush() lhe permite forçar essa confirmação.

Classes básicas de fluxo


Há diversas classes de fluxo definidas no pacote java.io. Esta é a hierarquia de classes do
pacote. Algumas das mais comuns estão descritas abaixo.

InputStream

SequenceInputStream FileInputStream
PipedInputStream ByteArrayInputStream

FilterInputStream StringBufferInputStream

DataInputStream LineNumberInputStream

PushbackInputStream BufferedInputStream

FileInputStream e FileOutputStream

Linguagem de Programação Java 139


Estas classes são fluxos de nó e, como o próprio nome sugere, utilizam arquivos de disco. Os
construtores dessas classes lhe permitem especificar o caminho para o arquivo a que estão
conectadas. Para construir um FileInputStream, o arquivo associado deve existir e poder ser
lido. Se construir um FileOutputStream o arquivo de saída será sobrescrito se ele já existir.

FileInputStream infile = new FileInputStream("myfile.dat");

FileOutputStream outfile -new FileOutputStream("results.dat");

Observação - Mais no final deste módulo você aprenderá outras duas classes que acrescentam
outras capacidades de E/S de arquivo em java.

BufferedInputStream e BufferedOutputStream

Estes são fluxos de filtro que podem ser utilizados para aumentar a eficiência de operações de E/S.

DataInputStrearn e DataOutputStream

Estes fluxos de filtro lhe permitem leitura e gravação de tipos primitivos de Java pelos fluxos. Uma
série de métodos especiais é oferecida às primitivas diferentes. Por exemplo:

Métodos DataInputStream

byte readByte()
long readLong()
double readDouble()

Métodos DataOutputStream

void writeByte(byte)
void writeLong(long)
void writeDouble(double)

Observe que os métodos do DataInputStream correspondem aos métodos do


DataOutputStream.

Estes fluxos têm métodos para leitura e gravação de seqüências, mas esses métodos não deverão
ser utilizados. Eles foram substituídos por leitores e gravadores que serão discutidos mais tarde.

PipedInputStream e PipedOutputStream

Fluxos de conexão podem ser utilizados para comunicação entre threads. Um objeto de
PipedInputStream num thread recebe suas entradas a partir de um objeto de
PipedOutputStream complementar em outro thread. Os fluxos de conexão devem ser de
entrada e de saída para serem úteis.

Fluxos de entrada de URL


Além do acesso a arquivo básico, Java lhe oferece a habilidade para utilizar URLs como meio para
acessar objetos pela rede. Você utiliza implicitamente um objeto de URL quando acessa sons e
imagens que utilizam o método getDocumentBase() para applets.

String imageFile = new String ("images/Duke/Tl.gif");


images[0] = getImage(getDocumentBase(), imagefile);

Linguagem de Programação Java 140


Entretanto, é possível fornecer um URL direto conforme se segue:

java.net.URL imagesource;
try {
imagesource = new URL("http://mysite.com/~info");
} catch (MalformedURLException e) {}
images[0] = getImage(imageSource, "Duke/Tl.gif");

Abertura de um fluxo de entrada

Também é possível abrir um fluxo de entrada fora de uma URL apropriado, armazenando um
arquivo de dados abaixo do diretório de base do documento.

InputStream is;
String datafile = new String("Data/data.1-96");
byte buffer[] = new byte[24];
try {
//nova URL lança uma MalformedURLException
//URL.openStream() lança uma IOException
is = (new URL(getDocumentBase(), datafile)).openStream();
} catch (Exception e) {}

Agora é possível utilizar is para ler informações, assim como com um objeto de FilelnputStream

try {
is.read(buffer, 0, buffer.length);
} catch (IOException el) {}

Cuidado - Lembre-se de que a maioria dos usuários têm sua segurança de navegador configurada
para impedir que applets acessem arquivos.

Leitores e gravadores
Unicode

Java utiliza Unicode para representar seqüências e caracteres, e o código fornece fluxos para
permitir que caracteres sejam tratados da mesma forma. Essas fluxos são denominadas leitores e
gravadores e, como os outros fluxos, muitos deles estão disponíveis no pacote java.io.

As versões mais importantes são InputStreamReader e OutputStreamWriter. Estas classes


são utilizadas para servir de interface entre fluxos de byte e leitores e gravadores de caractere.

Quando construir um InputstrearnReader ou OutputStreamWriter são definidas regras de


conversão para fazer a alteração entre 16 bits Unicode e outras representações específicas da
plataforma.

Conversões de byte e caractere

Como padrão, se você simplesmente construir um leitor ou gravador conectado a um fluxo, então as
regras de conversão irão mudar entre bytes que utilizam a codificação padrão de caractere da
plataforma e Unicode. Assim, nos países de língua inglesa a codificação de byte utilizada será ISO
8859-1.

É possível especificar urna codificação de byte alternativa utilizando uma das formas de codificação
suportadas apresentadas na lista. Você encontra uma lista das formas de codificação suportadas na
documentação da ferramenta native2ascii.

Linguagem de Programação Java 141


Com a utilização desse esquema de conversão, Java é capaz de usar a flexibilidade total do
conjunto de caracteres da plataforma local embora ainda mantenha a independência da
plataforma com uso interno de Unicode.

O leitor e gravador de buffer

Visto que a conversão de formatos é como qualquer outra operação de E/S, sendo mais eficiente se
executada em grandes blocos, normalmente é interessante encadeá-la BufferedReader ou
BufferedWriter (conforme apropriado) no fim de um InputStreamReader ou
InputStreamWriter. Lembre-se de usar o método flush() num BufferedWriter.

Leitores e gravadores

Leitura de entrada de strings

Este exemplo mostra a técnica simples a ser utilizada para ler informações de string a partir do canal
de entrada padrão.

import java.io.*;
public class CharInput {
public static void main (String args[]) throws java.io.IOException {
String s;
InputStreamReader ir;
BufferedReader in;
ir = new InputStreamReader (System. in);
in = new BufferedReader(ir);
while ((s = in.readline()) != null) {
System.out.println("Lido: + s);
}
}
}

Utilização de outras conversões de caractere

Se tivesse de ler entrada a partir de uma codificação de caractere que não fosse a sua local, quem
sabe ler a partir de uma conexão de rede com um tipo diferente de máquina, seria possível construir
o InputStreamReader com uma codificação de caractere explícita como a seguinte:

ir = new InputStreamReader(System.in, “8859_1")

Observação - Se estiver lendo caracteres a partir de uma conexão de rede, devera utilizar
essa forma. Se não o fizer, o programa sempre tentará converter os caracteres lidos como
se estivessem na representação local, o que provavelmente estaria incorreto. ISO 8859_1 é
o esquema de codificação Latin-1 que mapeia em ASCII.

Arquivos
Antes de executar operações de E/S num arquivo, é necessário obter informações básicas sobre o
arquivo. A classe File oferece vários utilitários para tratamento de arquivos e obtenção de
informações básicas sobre esses arquivos.

Criação de um objeto de arquivo novo

File myfile;

Linguagem de Programação Java 142


myfile = new File ("mymotd")

myfile = new File("/", “mymotd")


//mais útil se o diret6rio ou nome de arquivo for uma variável

File mydir = new File("/");


myfile = new File (mydir, "mymotd")

O construtor utilizado muitas vezes vai depender dos outros objetos de arquivo que você acessar.
Por exemplo, se utilizar apenas um arquivo no seu aplicativo, utilize o primeiro construtor.
Entretanto, se utilizar diversos arquivos a partir de um diretório comum, talvez seja mais fácil usar o
segundo ou o terceiro construtor.

Observação - É possível utilizar um objeto File como argumento do construtor para objetos
FileInputStream e FileOutputStream em vez de uma String. Isto permite maior
independência nas convenções do sistema de arquivo local e é normalmente recomendado.

Testes de arquivo e utilitários


Tendo criado um objeto de File, é possível utilizar qualquer um desses métodos para coletar
informações sobre o arquivo.

Nomes de arquivo

• String getName ()
• String getPath()
• String getAbsolutePath()
• String getParent ()
• boolean renameTo(File newname)

Testes de arquivo

• boolean exists()
• boolean canWrite()
• boolean canRead()
• boolean isFile()
• boolean isDirectory()
• boolean isAbsolute()

Informações gerais de arquivo e utilitários

• Long lastModified()
• long length()
• boolean delete()

Utilitários de diretório

• boolean mkdir()
• String[] list()

Linguagem de Programação Java 143


Arquivos de acesso aleatório
Muitas vezes você vai descobrir que gostaria de ler dados de alguma parte do arquivo sem ter que
lê-lo do início ao fim. Pode querer acessar a arquivos de texto como um banco de dados, neste caso
vai ler um registro aqui, outro ali e depois mais outro-todos em partes diferentes do arquivo. A
linguagem Java fornece uma classe RandomAccessFile para tratamento desse tipo de entrada ou
saída.

Criação de arquivo de acesso aleatório

Ha duas opções para abertura de arquivo de acesso aleatório:

• Com o nome do arquivo

myRAFile = new RandomAccessFile(String name, String mode);

• Com um objeto File

myRAFile = new RandomAccessFile(File file, String mode);

O argumento mode determina se tem acesso a esse arquivo somente leitura (r) ou leitura/ gravação
(rw).

Por exemplo, é possível abrir um arquivo de banco de dados para atualização:

RandomAccessFile myRAFile; myRAFile = new


RandomAccessFile("db/stock.dbf","rw");

Acesso as informações

RandomAccessFile objetos esperam ler e gravar informações da mesma maneira que objetos de
entrada e saída de dados. Você acessa a todas as operações de read() e write() encontradas
nas classes DataInputStream e DataOutputStream

A linguagem Java fornece vários métodos para ajudá-lo a se movimentar dentro do arquivo:

• long getFilePointer();

Retoma o local atual do ponteiro do arquivo.

• void seek(long pos);

Configura o ponteiro do arquivo para a posição absoluta especificada. A posição é dada como um
deslocamento em bytes a partir do início do arquivo. Posição o marca o início do arquivo.

• long length();

Retoma o comprimento do arquivo. Posição length () marca o fim do arquivo.

Anexação de informações

É possível utilizar arquivos de acesso aleatório para realizar um modo de anexação para saída de
arquivo.

Linguagem de Programação Java 144


myRAFile = new RandomAccessFile("java.log","rw”);
myRAFile.seek(myRAFile.length());
//Todas as write()s subseqüentes serão anexadas ao arquivo

Serializável
A partir do JDK 1.1 houve a inclusão da interface java.io.Serializable e mudanças no Java
Virtual Machine para suportar a habilidade de salvar um objeto Java num fluxo.

O ato de salvar um objeto em algum tipo de armazenamento permanente é denominada


persistência. Um objeto é tido como persistente quando for possível armazená-lo em disco ou fita,
ou enviá-lo para outra máquina para armazenamento em memória ou disco.

A interface java.io.Serializable não tem métodos e apenas serve como um "marcador",


indicando que a classe que implementa a interface pode ser considerada para serialização. Objetos
de classes que não implementam Serializable não podem salvar ou restaurar seus estados.

Grafos de objeto

Quando um objeto for serializado, apenas os dados do objeto são preservados; métodos de classe e
construtores não fazem parte do fluxo serializado. Quando uma variável de dados for um objeto, os
membros dos dados do objeto também são serializados. A árvore ou estrutura de dados do objeto,
inclusive os sub-objetos, constituem o grafo do objeto.

Algumas classes de objeto não são serializáveis pois a natureza dos dados que representam está
em constante mutação. Por exemplo, fluxos como java.io.FileInputStream e
java.io.FileOutputStream e java.lang.Thread. Se um objeto serializável tiver uma
referência a um elemento não~serializável, toda a operação de serialização falhará e uma
NotSerializableException será lançada.

Se o gráfico de objeto tiver uma referência a objeto não-serializável, o objeto ainda poderá ser
serializado se a referência estiver marcada com a palavra-chave transient.

public class MyClass implements Serializable {


public transient Thread myThread;
private String customerID;
private int total;

Observe também que o modificador de acesso a campo (public, protected e private) não
afeta o campo de dados sendo serializado, e os dados são gravados no fluxo em formato de byte
com seqüências representadas como caracteres UTF. Utilizando a palavra-chave transient evita
que os dados sejam serializados.

public class MyClass implements Serializable {


public transient Thread mythread;
private transient String customerID;
private int total;

Gravação e leitura de um fluxo de objeto

Gravação

Gravação e leitura de um objeto num fluxo de arquivo é um processo simples. Considere o seguinte
fragmento de código que envia uma instância de um objeto java.util.Date para um arquivo:

Linguagem de Programação Java 145


1 Date d = new Date();
2 FileOutputStream f = new FileCrutputStream("date.ser");
3 ObjectOutputStream s = new objectOutputStream (f);
4 try {
5 s.writeobject (d);
6 s.close ();
7 } catch (IOException e) {
8 e.printStackTraceo;
9 }

Leitura

A leitura de objeto é tão simples quanto a gravação, com apenas uma observação-o método
readObject() retorna o fluxo como um tipo Object, e ele deve ser convertido (cast) para o nome
da classe apropriada antes que métodos dessa classe possam ser executados.

1 Date d = null;
2 FileInputStream f = new FileInputStream ("date.ser");
3 ObjectInputStream s = new ObjectInputStream (f);
4 try {
5 d = (Date)s.readObject
6 } catch (IOException e) {
7 e.printStackTrace();
8 }
9 System.out.println ("Data serializada em: "+ d);

Linguagem de Programação Java 146


Capítulo 13

Java DataBase Connectivity

Objetivos

Ao completar este módulo, você será capaz de:


• Explicar o que é JDBC
• Explicar como a utilização da camada de abstração
fornecida pelo JDBC pode fazer um front-end de banco de
dados portável entre plataformas
• Descrever as cinco maiores tarefas envolvidas na
programação JDBC
• Verificar os requisitos de um driver JDBC e sua relação com
o JDBC driver manager

Linguagem de Programação Java 147


Introdução à interface JDBC
Na fase de implementação de uma estrutura de banco de dados, você possui uma série de escolhas
sobre qual o servidor adequado para a tarefa. Um projeto pode ser implementado em servidores
Oracle, SQL, Sybase e outros.

Pela introdução de uma camada de abstração, JDBC permite que a escolha do database engine
seja feita pelo integrador ou de forma mais simples. JDBC habilita o desenvolvedor a utilizar uma
mesma API padronizada que toma conta de toda a comunicação entre seu front end e o banco de
dados back end.

Se você decidir mais tarde pela alteração do back end, o servidor de banco de dados pode ser
facilmente substituído, pela alteração dos drivers JDBC carregados.

Conformidade ANSI SQL-2

Sistemas de banco de dados suportam uma vasta gama de sintaxe e semântica SQL . Enquanto
diferentes sistemas podem variar em funcionalidades mais avançadas tais como outer joins, eles
compartilham um suporte comum a ANSI SQL-2. Devido ao fato de que aplicações Java podem ser
escritas para utilizar somente comandos com patíveis com este padrão ANSI, essas aplicações serão
altamente portáveis entre diversos bancos de dados.

Entretanto, JDBC não suporta somente ANSI SQL-2. JDBC permite que qualquer string de query
possa ser passada para o banco de dados, de forma que uma aplicação pode utilizar funções
específicas de determinados sistemas de bancos de dados, penalizando a portabilidade do código
entre diferentes estruturas de bancos de dados.

Os dois lados de JDBC

Existem dois componentes maiores em JDBC: uma interface de implementação para fabricantes de
bancos de dados, e uma interface para desenvolvedores de aplicações. As seções seguintes
discutem a estrutura do JDBC da perspectiva de um fabricante de bancos de dados e então cobrem
em maiores detalhes os passos envolvidos em escrever uma aplicação JAVA usando os drivers
ODBC:JDBC, fornecido pela própria Sun, e drivers de outros fabricantes.

Linguagem de Programação Java 148


JDBC Driver Interface

A interface de driver JDBC fornece implementações específicas de um fornecedor das classes


abstratas fornecidas pela API JDBC. Cada fabricante deve fornecer implementações de:
• java.sql.Connection
• java.sql.Statement
• java.sql.PreparedStatement
• java.sql.CallableStatement
• java.sql.ResultSet
• java.sql.Driver

Cada driver de banco de dados deve fornecer uma classe que implementa a interface
java.sql.Driver usada pela classe java.sql.DriverManager genérica quando for
necessário localizar um driver para um banco de dados em particular utilizando um uniform resource
locator (URL). JDBC também fornece uma implementação s obre ODBC simples e eficiente.

A figura abaixo mostra como uma única aplicação (ou applet) Java pode acessar múltiplos sistemas
de bancos de dados através de um ou mais drivers.

Aplicação Java
(API JDBC)

JDBC driver manager

JDBC-ODBC Driver A Driver B


Bridge driver

Drivers ODBC
e de database

Driver JDBC-ODBC bridge

O JDK fornece um driver de compatibilização JDBC-ODBC que permite que fontes de dados ODBC
possam ser acessadas a partir de aplicativos Java utilizando a API JDBC. Apesar da eficiência do
driver diminuir pela maior quantidade de componentes, é bastante popular pela facilidade de oferta e
configuração de drivers ODBC, principalmente em ambientes de teste.

Diversos fabricantes de bancos de dados oferecem suas próprias implementações da API JDBC,
juntamente com seus produtos. Diversos fornecedores independentes também fornecem
implementações de drivers no mercado.

Linguagem de Programação Java 149


Tarefas de programação JDBC

Esta seção cobre algumas das tarefas comuns que deverão ser realizadas na programação JDBC.
Você aprenderá como:
• Criar uma instância de um driver JDBC
• Carregar drivers JDBC através de jdbc.driver
• Registrar um driver
• Especificar um banco de dados
• Abrir uma conexão de banco de dados
• Submeter uma query
• Receber resultados

Será assumido que uma fonte de dados ODBC será utilizada via ponte JDBC-ODBC.

Package java.sql

As oito interfaces associadas com o JDBC são:


• java.sql.Driver
• java.sql.Connection
• java.sql.Statement
• java.sql.PreparedStatement
• java.sql.CallableStatement
• java.sql.ResultSet
• java.sql.ResultSetMetaData
• java.sql.DatabaseMetaData

Cada uma dessas interfaces habilita uma aplicação em particular a abrir conexões com bancos de
dados, executar comandos SQL e processar os resultados.

Fluxo de Programação JDBC

O processo ocorre na seguinte ordem:


• Uma string URL é passada ao método getConnection do DriverManager, que localiza
um Driver.
• Com um Driver, a aplicação obtém uma Connection.
• Com a Connection, a aplicação cria um Statement.
• Quando é executado o método executeQuery de um Statement, é retornado um
ResultSet.

Exemplo Básico de JDBC

Este é um exemplo simples que utiliza o driver de conexão ODBC:JDBC. Este exemplo utiliza os
elementos de uma aplicação JDBC: criar uma instância da classe Driver, obter um objeto
Connection, criar um objeto Statement, executar uma query e processar o objeto ResultSet
resultante.

1. import java.sql.*;

2. public class Lista


3. {
4. public static void main(String[] args) throws SQLException {
5. // CARREGAMENTO DO DRIVER JDBC
6. try {
7. Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");

Linguagem de Programação Java 150


8. } catch (Exception e) { }
9. // CONFIGURACAO E INICIALIZACAO DA CONEXAO
10. String url = "jdbc:odbc:northwind";
11. String user = "";
12. String password = "";
13. Connection con = DriverManager.getConnection(url,user,password);
14. // CRIACAO DO COMANDO
15. Statement stmt = con.createStatement();
16. // CONFIGURACAO DO COMANDO
17. ResultSet rs =
18. stmt.executeQuery("SELECT NomeDaEmpresa, Telefone FROM Clientes");
19. // EXTRACAO DOS DADOS
20. while (rs.next()) {
21. System.out.println("Empresa: "+rs.getString(1));
22. System.out.println("Fone: "+rs.getString(2));
23. System.out.println(" ");
24. }
25. }
26. }

Exemplo de JDBC utilizando um driver externo

Os fabricantes de bancos de dados disponibilizam drivers JDBC para acesso às suas plataformas.
Os drivers são escritos em Java e devem ser invocados de forma apropriada nos programas que os
utilizarem. A documentação do próprio driver fornecerá informações de como isto deverá ser feito. O
exemplo abaixo utiliza o driver Jdbc para o servidor de banco de dados Interbase.

1. import java.sql.*;
2. public class ListaIB
3. {
4. public static void main(String[] args) throws SQLException {
5. // CARREGAMENTO DO DRIVER JDBC
6. try {
7. Class.forName("interbase.interclient.Driver");
8. } catch (Exception e) { }
9. // CONFIGURACAO E INICIALIZACAO DA CONEXAO
10. String url = "jdbc:interbase://127.0.0.1/e:/databases/employee.gdb";
11. String user = "sysdba";
12. String password = "masterkey";
13. Connection con = DriverManager.getConnection(url,user,password);
14. // CRIACAO DO COMANDO
15. Statement stmt = con.createStatement();
16. // CONFIGURACAO DO COMANDO
17. ResultSet rs = stmt.executeQuery("SELECT * FROM customer");
18. // EXTRACAO DOS DADOS
19. while (rs.next()) {
20. System.out.println("Codigo : "+rs.getString(1));
21. System.out.println("Cliente: "+rs.getString(2));
22. System.out.println("Contato: "+rs.getString(3));
23. System.out.println(" ");
24. }
25. stmt.close();
26. con.close();
27. }
28. }

Linguagem de Programação Java 151


Executando uma consulta

Usando o método Statement.executeQuery, é possível executar um comando SELECT na base


de dados. O método retorna um objeto do tipo ResultSet com o resultado do processo. Utilizando
o método next() e os métodos getXXX(), os valores dos campos resultantes podem ser
acessados.

1. ResultSet rs = stmt.executeQuery(“select * from Clientes”);


2. While (rs.next()) {
3. System.out.println(“Cliente: “+rs.getString(1));
4. System.out.println(“Telefone: “+rs.getString(3));
5. }

Os métodos getXXX acessam os campos por ordem numérica ou pelo nome do campo.

Usando os métodos getXXX

Métodos getXXX e tipos Java retornados

Método Tipo Java retornado


getASCIIStream java.io.InputStream
getBigDecimal java.math.BigDecimal
getBinaryStream java.io.InputStream
getBoolean boolean
getByte byte
getBytes byte[]
getDate java.sql.Date
getDouble double
getFloat float
getInt int
getLong long
getObject Object
getShort short
getString java.lang.String
getTime java.sql.Time
getTimestamp java.sql.Timestamp
getunicodeStream java.io.InputStream de caracteres Unicode

Requisitando uma atualização

Usando o método Statement.executeUpdate, comandos como INSERT, UPDATE ou DELETE


podem ser enviados ao banco de dados. JDBC passa o comando SQL inalterado para o banco de
dados, e não faz nenhuma tentativa de interpretar a query.

O método Statement.executeUpdate retorna um inteiro (int) que representa o número de


linhas alteradas na tabela alvo do comando.

1. int count = stmt.executeUpdate(“delete from Clientes where Cidade =


‘Curitiba’);
2. if (count > 0) {
3. System.out.println(“Comando executado com sucesso.”);
4. }

Linguagem de Programação Java 152


Capítulo 14

Threads

Objetivos

Após concluir este módulo, você será capaz de:

• Compreender o conceito de um thread.


• Criar theads separados em Java, controlando o código e os dados
utilizados pelo thread.
• Controlar a execução de um thread e escrever códigos independentes de
plataforma com threads.
• Compreender algumas das dificuldades que surgem quando threads
múltiplos compartilham dados.
• Compreender a utilização de palavra-chave synchronized em Java para
proteger os dados contra a corrupção.

Linguagem de Programação Java 153


O que são threads?
Uma visão simplista de um computador é a de que existe uma CPU que executa computação,
alguma ROM que contém o programa executado pela CPU e RAM que mantém os dados sobre os
quais o programa opera. Nesta visão simplista, e existe apenas a execução de um trabalho de cada
vez. Uma visão mais completa dos sistemas de computadores mais modernos permitem a execução
simultânea de mais de um trabalho.

Nesse estágio do debate não há necessidade de se preocupar como o efeito é alcançado, mas
apenas considerar as implicações sob o prisma da programação. Se tivermos mais de um trabalho
sendo executado, isto é semelhante a ter mais de um computador. Para este módulo,
consideraremos um thread, ou contexto de execução, como sendo o encapsulamento de uma CPU
virtual com seus próprios dados e códigos de programa. A classe java.lang.Thread nas
bibliotecas básicas de Java permitem aos usuários criarem e controlarem seus próprios threads.

Utilizaremos Thread quando se referirem à classe java.lang.Thread e thread para se referir à


idéia de um contexto de execução.

Threads e threading em Java


Três partes de um thread

Um thread compreende três partes principais. Primeiro, há a CPU virtual. Em segundo, há o código
que a CPU está executando. Em terceiro, há os dados sobre os quais o código trabalha.

Em Java, a CPU virtual está incorporada na classe do Thread. Quando um thread é construído, os
argumentos do construtor são passados para o código que ele irá executar e os dados sobre os
quais ele trabalhará.

É importante observar que esses três aspectos são efetivamente independentes. Um thread pode
executar um código que seja igual ao de outro thread ou pode executar um código diferente. Pode
também acessar a dados iguais ou diferentes.

Criação do thread

Vamos analisar a forma de criação de um thread e discutir como os argumentos do construtor são
utilizados para fornecer o código e os dados para quando o thread for executado.

Um construtor de Thread recebe um argumento que seja uma instância de Runnable. C)u seja, é
necessário declarar uma classe para implementar a interface Runnable e construir uma instância
dessa classe. A referência resultante é um argumento conveniente para nosso construtor.

Por exemplo,

1 public class xyz implements Runnable {

Linguagem de Programação Java 154


2 int i;
3 public void run() {
4 while (true)
5 System.out.println("Olá " + i++);
6 }
7 }
8 }

nos permite construir um thread assim:

Runnable r = new xyz();


Thread t = new Thread(r);

A partir disso, temos um novo thread incorporado na referência para o Thread t. Este está
configurado para executar o código iniciando no método run () da classe xyz. (A interface Runnable
exige que um método public void run() esteja disponível.) Os dados que este thread utiliza são
fornecidos pela instância da classe xyz referida como r.

Resumindo, um thread é referenciado por uma instância de um objeto Thread. O código que o
thread irá executar é retirado da classe do argumento passado para o construtor do Thread. Esta
classe deve implementar a interface Runnable. Os dados sobre os quais o thread trabalha são
retirados da instância específica em Runnable, passados para o construtor do Thread.

Inicio do thread

Embora o thread tenha sido criado, ele não é executado imediatamente. Para iniciá-lo, utilizamos o
método start (). Este método encontra-se na classe Thread, assim em vista do esboço anterior
dizemos simplesmente:

t. start() ;

Neste ponto, a CPU virtual incorporada no thread se torna executável. Pense nisto como se
estivesse ligando a CPU virtual.

Escalonamento do thread

Embora o thread se torne executável, isto não quer dizer que ele inicia imediatamente. Em uma
máquina que tenha realmente apenas uma CPU, naturalmente ela só poderá fazer uma coisa de
cada vez. Consideraremos agora a forma de alocação da CPU quando mais de um thread puder
estar trabalhando de forma útil.
Em Java, threads normalmente são preemptivos, mas não necessariamente por fração de tempo. (É
um erro comum acreditar que "preemptivo" seja uma palavra sofisticada para "por fração de tempo".)

Linguagem de Programação Java 155


O modelo de um escalonador preemptivo é que diversos threads podem estar prontos para serem
executados, mas apenas um estará realmente em execução. Este processo continuará a ser
executado até deixar de ser executável ou até que outro de prioridade mais alta se torne executável.
Neste caso dizemos que o thread de prioridade mais baixa é preemptado pelo thread de prioridade
mais alta.

Um thread pode deixar de estar pronto por uma série de motivos. Ele pode executar uma chamada
Thread. sleep (), pedindo deliberadamente para ficar suspenso durante um período. Ele pode ter que
esperar por um dispositivo externo lento, talvez um disco ou o usuário.

Todos os threads executáveis, mas que não estejam sendo executados, são mantidos em filas de
acordo com as prioridades. O primeiro thread na fila não-vazia de prioridade mais alta será
executado. Quando um thread interromper a execução por causa da preempção, ele é retirado do
estado de execução e colocado na parte final da fila de execução. Da mesma forma, um thread que
se torna executável depois de ter sido bloqueado (suspenso ou talvez aguardando por E/S) sempre
ira para a parte final da fila.

Threads de Java não executam necessariamente em uma fração de tempo - na ausência de


compreensão mais avançada e de uma estrutura de programa que se adapte. É necessário
assegurar que o código para os threads ofereça a outros threads a oportunidade de serem
executados de vez em quando. Isto pode ser alcançado emitindo a chamada sleep () em intervalos
como no loop principal.

1 public class xyz implements Runnable {


2 public void run(); {
3 while (true)
4 // faça muitas coisas interessantes
5 :
6 // Dê oportunidade a outros threads
7 try {
8 Thread.sleep(10);
9 } catch (InterruptedException e)
10 //Este thread foi interrompido por outro
11 }
12 }
13 }
14}

Programação de thread

Observe a utilização de try e catch. Observe também que a chamada sleep() e um método
static da classe Thread e portanto, é referenciada como Thread.sleep(x). O argumento
especifica o número mínimo de milissegundos que deverá permanecer inativo. A execução do
thread não prosseguirá até esse tempo passe. Este período inclui o tempo para o bloqueio de outros
threads.

Um outro método na classe Thread, yield(), pode ser emitido para dar a outros threads a
oportunidade de serem executados sem interromper de verdade o thread atual, a menos que seja
necessário. Se outros threads com a mesma prioridade forem executáveis, yield colocará o thread
chamador no fim de sua lista de executáveis e permitirá o início de outro thread. Se não existirem
outros threads executáveis com a mesma prioridade, yield () não fará nada.

Observe que uma chamada sleep() pode permitir que threads de prioridade mais baixa tenham a
oportunidade de serem executados. O método yield() apenas dá uma oportunidade a threads
com a mesma prioridade ou mais alta.

Linguagem de Programação Java 156


Controle básico de threads

Término de um thread

Quando um thread retornar do término do método run (), ele morre. Depois disto, não poderá ser
executado novamente.

Um thread pode ser interrompido forçosamente pela emissão do método stop (). Este deve ser
utilizado numa instância específica da classe Thread; por exemplo:

public class xyz implements Runnable {


// Algumas coisas que queremos num thread

1 public class ttest {


2 public static void main(String args[]) {
3 Runnable r = new xyz();
4 Thread t = new Thread(r);
5 t.starto;
6 // faça outras coisas
7 if (time_to kill)
8 t.stop();
9 }
10 }

Dentro de uma parte de código em particular, é possível obter uma referência para o thread atual
utilizando o método estático Thread.currentThread(); por exemplo:

1 public class xyz implements Runnable {


2 public void run() {
3 while (true) {
4 // muitas coisas interessantes
5 if (time_to_die)
6 Thread.currentThread().stop(),
7 }
8 }
9 }

Observe que neste caso a execução de stop() destruirá o contexto da execução atual e portanto,
mais nenhum loop run() será executado neste contexto. A utilização de
Thread.currentThread() não está restrita ao exemplo específico de stop().

Controle básico de threads

Teste de um thread

Algumas vezes é possível que um thread esteja num estado desconhecido (por exemplo, poderá
ocorrer se o código não estiver diretamente no controle de um determinado thread). É possível
perguntar se um thread ainda é viável utilizando o método isAlive(). Alive não implica em que o
thread esteja sendo executado significa apenas que foi iniciado e que o método stop() não foi
executado ou nem esgotado o término de seu método run().

Colocação de threads em retenção

Linguagem de Programação Java 157


Há diversos mecanismos que podem interromper a execução temporária de um thread. Se seguir
este tipo de suspensão, a execução poderá prosseguir como se não tivesse sido interrompida,
aparentará que o thread simplesmente executou a instrução muito lentamente.

sleep()

O método sleep() foi introduzido na primeira seção e é utilizado para interromper um thread
durante algum tempo. Lembre-se de que normalmente o thread não prosseguirá no momento em
que o tempo de suspensão for esgotado. Isto é porque provavelmente outros threads esteja sendo
executados naquele instante e talvez não possam ser desescalonados a não ser que: (a) o thread
saindo da suspensão seja de prioridade mais alta, (b) o thread em execução seja bloqueado por
algum outro motivo ou, (c) fração de tempo seja ativada. Na maioria dos casos, nenhuma das duas
últimas condições prevalecerá imediatamente.

suspend() e resume()

Algumas vezes é apropriado suspender a execução de um thread indefinidamente, caso em que


outro thread ficará responsável pelo prosseguimento da execução.

Dois métodos de Thread estão disponíveis para esse fim. Denominam-se suspend() e resume().
Por exemplo:

public class xyz implements Runnable {


public void run() {
//faça muitas coisas interessantes ...
//em seguida aguarde ate que alguém diga para continuar
Thread.currentThread().suspend();
// agora faça mais coisas ...
}
}
:
:
Runnable r = new xyz();
Thread t = new Thread(r);
t.start();
// interrompa para permitir a execução temporária de xyz
// assuma que atingiu suspend() ... Thread.sleep(1000);

// obtenha novamente xyz executável


t.resume();

// obtenha-o em execução atualmente


Thread.yield();

Observe que, em geral, um Thread pode ser suspenso por qualquer parte de código que nele tenha
um handle; ou seja, uma variável que se refira a ele. Entretanto, é claro que o Thread só poderá ser
reiniciado por um outro Thread que não seja ele próprio, uma vez que o Thread suspenso não está
executando nenhum código!

join()

O método join() faz com que o thread atual aguarde até que termine o thread sobre o qual o
método join é chamado. Por exemplo:

TimerThread tt = new TimerThread (100);


tt.start
...

Linguagem de Programação Java 158


public void timeout(){
// Espere aqui até que termine o cronômetro do thread
tt.join
...
//Agora continue nesse thread
...
}

join também pode ser chamado com um valor de tempo limite em milissegundos.

void join(long timeout);

onde o método join suspenderá o thread atual por timeout milissegundos ou até que o thread
chamado termine.

Outras formas de criar threads


Discutimos a criação de Threads utilizando uma classe separada que implementa Runnable. Na
verdade, esta não é a única abordagem. A definição da classe Thread afirma que de fato ela
implementa a interface Runnable. É possível criar um Thread através da criação de uma classe
que estenda Thread em vez de implementar Runnable

public class myThread extends Thread


public void run() {
while (running) {
// faça muitas coisas interessantes
sleep(100);
}
}
public static void main(String args[]) {
Thread t = new myThread();
t.start();
}
}

Aqui existe apenas uma classe, myThread. Quando há a criação do objeto de Thread não são
passados argumentos. Esta forma de construtor cria um Thread que utiliza seu próprio método
run() incorporado.

Qual utilizar?

Dadas as opções de abordagens, como decidir por uma delas? Há pontos favoráveis em todas as
abordagens.

Prós da implementação Runnable

Do ponto de vista do projeto orientado a objeto, a classe Thread é rigorosamente o encapsulamento


de uma CPU virtual e, como tal, deve apenas ser estendida quando o comportamento daquele
modelo de CPU tiver de alguma forma de ser mudado ou estendido. Em geral, este não é o objetivo
de nosso exercício. Por causa disto e da importância de fazer a distinção entre as partes CPU,
Código e Dados de um thread em execução, o módulo do curso utilizou essa abordagem.

Visto que Java apenas permite herança simples, é impossível estender qualquer outra classe, como
Applet, se você já tiver estendido Thread. Em algumas situações, isto o obrigará a adotar a
abordagem de implementação Runnable.

Linguagem de Programação Java 159


Uma vez que há situações em que é obrigado a implementar Runnable talvez prefira ser
consistente e sempre fazer dessa forma.

Prós do thread estendido

Onde houver um método de execução incorporado numa classe em que ela própria estenda a
classe Thread, a referência this se refere de fato à instância do Thread real que está no controle
da execução. Por causa disto, o código não precisa mais utilizar controles de longo alcance:

Thread.currentThread().suspend();

mas pode dizer simplesmente:

suspend();

Como o código resultante é ligeiramente mais simples, muitos programadores de Java utilizam o
mecanismo de estender o Thread. No entanto, se você adotar esta abordagem o modelo de herança
simples poderá trazer problemas mais tarde ao ciclo de vida do código.

Utilização de synchronized em Java

Introdução

Este modelo discute utilização da palavra-chave synchronized. Isto oferece à linguagem Java um
mecanismo que permite ao programador o controle sobre threads que estejam compartilhando
dados.

O problema

Imagine uma classe que represente uma pilha. Uma primeira visão da classe apareceria da seguinte
forma:
class stack
{
int idx = 0;
char [] data = new char[6];

public void push(char c) {


data[idx] = c;
idx++;

public char pop() {


idx--;
return data[idx]
}
}

Observe que a classe não faz esforço para tratar de estouro ou estouro negativo na pilha, e que a
capacidade da pilha é bem limitada. Estes aspectos são irrelevantes à discussão.

O comportamento deste modelo exige que o valor do índice tenha o subscrito da matriz da próxima
célula vazia da pilha. A abordagem "pré-decremento, pós-incremento" é utilizada para gerar essas
informações.

Imagine agora que dois threads tenham como referência uma única instância dessa classe. Um
thread está empurrando dados para a pilha e o outro, mais ou menos independentemente, está

Linguagem de Programação Java 160


retirando itens da pilha. Num sentido amplo, pareceria que os dados seriam acrescentados e
removidos com sucesso. Entretanto, existe um problema em potencial.

Suponha que o thread a esteja acrescentando e o thread h removendo caracteres. Thread a acaba
de depositar um caractere, mas ainda não incrementou o contador de índice. Por algum motivo,
esse thread perde agora a prioridade de execução. Neste ponto, o modelo de dados representado
em seu objeto é inconsistente.

buffer |p|q|r| | | |
idx = 2 ^

Especificamente, a consistência exigiria que idx = 3 ou que o caractere ainda não tivesse sido
acrescentado.

Se o thread a prosseguisse na execução talvez não houvesse dano, mas suponha que o thread b
também estivesse aguardando para remover um caractere. Enquanto o thread a estivesse
aguardando uma outra oportunidade de execução, thread h teria sua oportunidade para remover um
caractere.

Na entrada para o método pop(), temos uma situação de dados inconsistente. O método pop
prossegue para diminuir o valor de índice

buffer |p|q|r| | | |
idx = 1 ^

que na verdade serve para ignorar o caractere "r". Depois disto, ele então retorna ao caractere "q".
Até o momento, o comportamento é como se a letra "r" não tivesse sido empurrada, então é difícil de
dizer se existe um problema. Porém, agora, vejamos o que acontece quando o thread original, a,
continuar a ser executado.

Thread a seleciona o que foi deixado pelo método push(). Ele prossegue para incrementar o valor
de índice. Agora temos:

10 buffer |p|q|r| | | |
11 idx = 2 ^

Observe que essa configuração implica em que "q" seja válido e que a célula com "r" seja a próxima
célula vazia. Ou seja, vamos ler "q" como se tivesse sido colocado duas vezes na pilha, mas nunca
observaremos a letra "r".

Este é um exemplo simples de um problema geral que pode surgir quando diversos threads
estiverem acessando dados compartilhados. Precisamos de um mecanismo que assegure a
proteção dos dados contra esse tipo de acesso descontrolado.

Uma abordagem seria evitar que o thread a fosse desligado até que tivesse completado a seção
crítica do código. Esta abordagem é comum em programação de maquina de baixo nível, mas em
geral é inadequada para sistemas de múltiplos usuários.

Uma outra abordagem, em que Java trabalha, é fornecer um mecanismo para tratar dos dados como
frágeis.

O sinalizador de bloqueio de objeto

Em Java, toda instância de qualquer objeto possui um sinalizador associado a ela. Este sinalizador
pode ser considerado como um "sinalizador de bloqueio". Uma palavra-chave synchronized é
fornecida para permitir interação com o sinalizador. Observe o fragmento do código modificado:

Linguagem de Programação Java 161


public void push(char c) {
synchronized(this) {
data[idx] = c;
idx++;
}
}

Quando o thread alcançar a instrução synchronized, observará o objeto passado como o


argumento e tentará extrair dele o sinalizador de bloqueio.

É importante perceber que isso, por si mesmo, não protegeu os dados no objeto this. Se o método
pop(), sem modificação, for invocado por qualquer outro thread ele ainda correrá o risco de
danificar a consistência do objeto this.

O que devemos fazer e modificar o método pop da mesma forma. Na verdade acrescentamos uma
chamada para synchronized(this) em torno de partes frágeis da operação pop exatamente da
mesma forma que fizemos com o método push(). Se um outro thread então tentar executar o
método enquanto o thread original detiver o sinalizador de bloqueio, acontecerá o seguinte:

Linguagem de Programação Java 162


Quando o thread tentar executar a instrução synchronized(this), ele tentará retirar o sinalizador
de bloqueio do objeto this Uma vez que o sinalizador está ausente, é impossível executar o thread.
O thread então adere a uma fila de threads em espera que estão associados ao sinalizador de
bloqueio do objeto. Quando o sinalizador retornar ao objeto, será oferecido ao primeiro thread que
estiver aguardando por ele e continuará a ser executado.

Liberação do sinalizador de bloqueio

Visto que um thread que aguarda pelo sinalizador de bloqueio de um objeto não continuará a ser
executado até que o sinalizador retorne ao thread retido, é absolutamente importante retornar o
sinalizador quando não for mais necessário.

Na verdade, o sinalizador de bloqueio é retornado automaticamente ao seu objeto quando o thread


que o retém ultrapassar o final do bloco associado à chamada synchronized() que o obteve em
primeiro lugar. Java tem muito cuidado para garantir que o sinalizador seja sempre retornado
corretamente, portanto se o bloco synchronized gerar uma exceção ou se uma quebra de loop
saltar para fora do bloco, o sinalizador será devidamente retornado. Da mesma forma, se um thread
emitir uma chamada synchronized duas vezes no mesmo objeto, o sinalizador será liberado
corretamente na saída do bloco mais externo e aquele mais interno será efetivamente ignorado.

Estas regras tornam a utilização de blocos sincronizados muito mais simples de gerenciar do que
facilidades equivalentes encontradas em outros sistemas, como os famosos semáforos binários de
Dijkstra.

Montagem

Conforme sugerido, o mecanismo synchronized () apenas trabalhará se o programador colocar


as chamadas em locais corretos. Analisaremos agora como montar uma classe protegida
corretamente.

Considere a acessabilidade dos itens de dados que formam as partes frágeis do objeto. Se estes
não estiverem marcados como private, então poderão ser acessados por código fora da própria
definição de classe. Neste caso, é preciso confiar que outros programadores não omitirão as
proteções necessárias. Nitidamente esta não é uma estratégia segura. Por este motivo, os dados
deverão estar sempre marcados como private.

Uma vez que mostramos que os dados devem ser efetivamente marcados como private, o
argumento para a declaração synchronized () normalmente deverá ser this. Por causa dessa
generalização, a linguagem Java permite uma abreviação. Então, em vez de escrever:

Linguagem de Programação Java 163


public void push(char c) {
synchronized(this) {
:
:
}
}
escreva:
public synchronized void push(char c) {
:
:
}

que produz o mesmo resultado.

Por que utilizar um em vez do outro? Se utilizarmos synchronized como um modificador de método,
todo o método se tornará um bloco sincronizado, que poderá resultar na retenção do sinalizador de
bloqueio por um tempo maior do que o necessário. Isto poderia ser ineficiente. Por outro lado, a
marcação do método dessa forma permite aos seus usuários saber que sincronização está
ocorrendo, o que pode ser importante quando se projeta defesas contra impasse (que será
abordado na próxima seção). Observe que o gerador de documentação javadoc propagará o
modificador synchronized nos arquivos de documentação, mas a utilização de synchronized
(this) não será documentada.

Impasse

Em programas onde múltiplos threads estão competindo pelo acesso a vários recursos, pode existir
a possibilidade de uma condição conhecida como impasse. Isto ocorre quando um thread estiver
aguardando por um bloqueio retido por outro thread, mas o outro thread está aguardando por um
bloqueio que já foi retido pelo primeiro thread. Nesta condição, nenhum deles pode continuar até o
outro tenha ultrapassado o término do bloco synchronized. Uma vez que ambos são incapazes de
continuar, nenhum deles pode ultrapassar o término do bloco.

Java não detecta nem tenta evitar essa condição. Portanto o programador tem a responsabilidade
de garantir que a condição não ocorra.

Eis uma maneira prática de proceder que lhe permitirá evitar o impasse: Se tiver diversos objetos
que desejam ter acesso synchronized, tome uma decisão global sobre a ordem em que cada um
obterá esses bloqueios e atenha-se a ela ao longo de todo o programa.

Uma discussão mais detalhada sobre o problema ultrapassa o escopo deste material.

Linguagem de Programação Java 164


Interação de thread - wait() e notify()

Introdução

Com freqüência threads são criados especificamente para executar tarefas não relacionadas. No
entanto, por vezes os trabalhos executados estão na verdade de alguma forma relacionados.
Quando isto ocorrer talvez seja necessário programar alguma interação entre os threads. Há várias
maneiras padronizadas de fazer isso e com qualquer mecanismo, os outros podem ser
implementados com base nesse. Este módulo examina o mecanismo que Java oferece e descreve
sua utilização.

O problema

Por que dois threads precisam interagir? Um exemplo simples: imagine duas pessoas, uma lavando
pratos e a outra secando. Essas pessoas representam nossos threads. Entre elas existe um objeto
compartilhado, o escorredor. As pessoas são preguiçosas e prefeririam estar sentadas se não
tivessem nada de útil para fazer. Certamente, o secador não pode iniciar a secagem de alguma
coisa se não tiver pelo menos um item no escorredor. Assim como o escorredor ficará cheio se o
lavador de pratos for muito rápido, da mesma forma o lavador só poderá prosseguir quando o
escorredor tiver algum espaço livre.

Vamos apresentar o mecanismo que Java oferece para tratar disso de forma eficiente.

A solução

Seria possível programar uma solução para essa situação utilizando os métodos suspend() e
resume(). Tal solução exigiria que ambos os threads fossem criados em cooperação, uma vez que
cada um necessita de um handle do outro. Em vista desta inconveniência, Java fornece um
mecanismo de comunicação com base em instâncias de objetos.

Cada instância de objeto em Java tem duas filas de thread associadas a ela. A primeira é utilizada
por threads que aguardam a obtenção do sinalizador de bloqueio e foi abordada na seção
"Utilização de synchronized em Java." A segunda fila é utilizada para implementar os
mecanismos de comunicação wait() e notify().

Três métodos úteis são definidos na classe de base java.lang.Object. São eles wait(),
notify() e notifyAll(). Em primeiro lugar estamos preocupados com wait() e notify().
Vamos analisar o exemplo da lavagem de pratos.
Thread a está lavando e thread b está secando. Ambos têm acesso ao objeto escorredor. Suponha
que thread b - o thread de secagem, queira secar um item, mas que o escorredor esteja vazio. Então
poderia ser codificado:

if (drainingBoard.isEmpty())
drainingboard.wait();

Agora quando o thread b emitir a chamada wait(), ele interrompe a execução e se junta à lista de
espera do objeto escorredor. Ele não será executado de novo até que algo o remova dessa lista.

Então, como o thread secador poderá ser reiniciado? O thread lavador é responsável pela
informação de que há algo útil para o secador fazer. Isto é atingido pela emissão da chamada
notify() para o escorredor, assim:

drainingBoard.addItem(plate);
drainingboard.notify();

Neste ponto, o primeiro thread, que foi bloqueado na fila de espera do

Linguagem de Programação Java 165


escorredor, é removido da fila e disputa para ser novamente executado.

Observe que a chamada notify() é emitida nesse caso sem considerar se os threads estão
aguardando ou não. Esta abordagem é sem dúvida desperdiçadora; pois é possível emitir a
chamada só para o caso de o prato a ser acrescentado transformar o escorredor vazio em cheio.
Porém, isto e um detalhe e foge ao âmbito dessa discussão. O que é mais importante perceber é
que se o método notify() for chamado quando nenhum thread estiver bloqueado na fila de
espera, a chamada é sem efeito. Chamadas para notify() não são armazenadas.

Além disso, notify() libera no máximo um único thread da lista de espera. Se houver mais de um
aguardando, os outros permanecerão na fila. O método notifyAll() pode ser utilizado para
liberar todos os threads em espera se a estrutura do programa exigir esse comportamento.

Utilizando esse mecanismo, é possível coordenar de modo simples os threads de lavagem e


secagem, tudo sem precisar saber a identidade desses threads. Sempre que executarmos uma
operação que garanta que o outro thread possa fazer um trabalho útil, estaremos emitindo
notify() para o objeto escorredor. Sempre que tentarmos trabalhar, mas não pudermos
prosseguir pois o escorredor está cheio ou vazio, estaremos emitindo wait() para o objeto
escorredor.

A verdade

Os mecanismos discutidos até o momento são, em princípio, corretos, mas a implementação de


Java não é assim tão simples quanto a descrevemos. De modo específico, a própria fila de espera
consiste numa estrutura de dados delicada e precisa ser protegida utilizando o mecanismo
synchronized. Não precisamos nos preocupar com os detalhes do caso, mas é importante saber
que antes de fazer uma chamada para qualquer um wait(), notify() ou notifyAll() em um
objeto, devemos reter o sinalizador de bloqueio para esse objeto. Especificamente, esses métodos
devem ser invocados em blocos synchronized. Assim, temos que alterar nosso código:

synchronized(drainingboard) {
if (drainingBoard.isEmpty())
drainingboard.wait();
}

e da mesma forma:

synchronized(drainingboard)
drainingBoard.addItem(plate);
drainingBoard.notify();
}

Se desejar poderá assumir isso como uma simples regra de linguagem, mas ela desperta uma
observação importante. Uma vez que sabemos que a instrução synchronized requer o thread
para obter o sinalizador de bloqueio do objeto antes de prosseguir, poderia parecer que o thread
lavador nunca poderia atingir a declaração notify() se o thread secador fosse bloqueado no
estado wait().

Na pratica, a implementação é tal que isso não ocorre. Especificamente, a emissão de uma
chamada wait() primeiro retorna o sinalizador de bloqueio ao objeto. No entanto, para evitar erros
quando um thread notify() for emitido, ele não será executável. Ao contrário, será simplesmente
removido da fila de espera para a fila do sinalizador de bloqueio. Desta maneira, não poderá
realmente prosseguir até que ganhe de novo o sinalizador de bloqueio.

Linguagem de Programação Java 166


Um outro aspecto da implementação de Java é que o método wait() pode ser terminado por um
notify() ou invocando o método interrupt() no Thread. Neste caso wait() lança uma
InterruptedException que normalmente exige que ele seja colocado numa construção
try/catch.

Montagem
Vamos tentar agora montar um exemplo real. Vamos trabalhar com o princípio básico de lavagem e
secagem, mas em vez de pratos num escorredor, passaremos para caracteres num objeto em pilha.

Em computação este é um exemplo clássico de problema de consumidor-produtor.

Iniciaremos analisando o esboço do objeto em pilha e os detalhes dos threads que serão produzidos
e consumidos. Finalmente, analisaremos o detalhe da pilha e os mecanismos utilizados para
protegê-la e implementar a comunicação de thread.

A classe da pilha, denominada SyncStack para fazer a distinção entre a classe principal
java.util.Stack, oferecerá a seguinte API pública:

public void push(char c);


public char pop();

Produtor

O thread produtor executará o seguinte método:

public void run() {


char c;
for (int i=0; i<20; i++){
c = (char)(Math.random() * 26 + 'A');
theStack.push(c);
System.out.println("Produzido: " + c);
try {
Thread.sleep((int)(Math.random() * 100));
} catch (InterruptedException e) {
//ignore-o.
}
}
}

Isto irá gerar 20 caracteres de letras maiúsculas aleatórios e os empurrará para a pilha com um
atraso aleatório entre cada push. O atraso estará na faixa de 0 a 100 milissegundos. Cada caractere
empurrado será relatado no console.

Consumidor

O thread consumidor executará o seguinte método:

public void run() {


char c;
for (int i = 0; i < 20; i++) {
c = theStack.pop();
System.out.println("Consumido: " + c);
try {
Thread.sleep((int)(Math.random() * 1000));
} catch (InterruptedException e) {

Linguagem de Programação Java 167


//ignore-o..
}
}
}

Isto coletará 20 caracteres da pilha, com um atraso entre cada tentativa. Aqui o atraso é de 0 a 2
segundos. Isto quer dizer que a pilha será esvaziada mais devagar do que será cheia e portanto,
deverá ficar totalmente cheia muito rápido.

Agora, vamos considerar a construção da classe da pilha. Precisamos de um índice e de uma matriz
de buffer. O buffer não deverá ser muito grande, uma vez que a finalidade do exercício é demonstrar
a correta operação e sincronização no preenchimento. Neste caso a matriz terá seis caracteres.

Classe SyncStack

Uma SyncStack recentemente construída deverá estar vazia. Isto pode ser arranjado facilmente
utilizando a inicialização padrão dos valores, mas será feito aqui para maior clareza por uma
questão de bom estilo. Desta forma, podemos iniciar a construção da classe:

class SyncStack
{
private int index = 0;
private char [] buffer = new char[6];

public synchronized char pop() {


}

public synchronized void push(char c) {


}
}

Observe a ausência de qualquer construtor. Considera-se bom estilo incluir um construtor, mas não
foi incluído para ser mais rápido.

Consideremos agora os métodos push e pop. Precisaremos torná-los synchronized para


proteger os frágeis buffer e index. Além disso, precisamos acionar wait() se o método não
puder prosseguir e notify() quando fizermos nosso trabalho. O método pop() é mais ou menos
assim:

public synchronized void push(char c) {


while (index == buffer.length) {
try {
this.wait();
} catch (InterruptedException e) {
//ignore-o..
}
}
this.notify();
buffer[index] = c;
index++;
}

Observe que a chamada wait() foi tomada explícita como this.wait (). A utilização de this é
redundante, mas foi incluída para enfatizar que o encontro foi feito neste objeto da pilha. A chamada
wait () é colocada numa construção try/catch. Por causa da possibilidade de wait () sair
num interrupt (), devemos fazer um loop no teste para o caso de o Thread ser levantado
prematuramente em wait ().

Linguagem de Programação Java 168


Considere a chamada para notify(). De novo, fizemos um this.notify explícito, que é
redundante, mas descritivo. Em que ponto notify é chamado? A chamada vem antes de a
mudança ser feita; isto funciona? A resposta é que qualquer thread que esteja no estado wait ()
não pode continuar a ser executado até que o bloco synchronized tenha saído; portanto,
podemos emitir a chamada notify () a qualquer tempo depois de as mudanças necessárias
ocorrerem.

O ponto final a ser considerado é o de verificação de erro. Você pode observar que não há código
explícito para evitar a condição de sobrecarga. Isto é desnecessário, pois a única maneira de
acrescentar à pilha é por meio desse método e este entrará no estado wait() se for chamado para
provocar sobrecarga. Portanto, a verificação de erro é desnecessária. Podemos estar confiantes
sobre isso num sistema em execução por um outro motivo. Se a lógica resultar em falha, faremos
acessos de matriz fora de alcance que imediatamente fará com que uma Exception seja lançada,
para que não haja a possibilidade de o erro passar desapercebido. É possível utilizar exceções de
tempo de execução para criar nossas próprias verificações em outras circunstâncias.

O método pop() é semelhante e os comentários que acabamos de fazer também se referem a ele.

public synchronized char pop() {


while (index == 0) {
try {
this.wait();
} catch (InterruptedException e) {
//ignore a Exception
}
this.notify(); //seja explicito
index--;
return buffer[index];
}

Essas peças precisam ser montadas em classes completas e ter um apetrecho anexado para
manter tudo em execução. Eis o código final.

SyncTest.java

1 package mod13;
2 public class SyncTest
3 {
4 public static void main(String args[]) {
5 SyncStack stack = new SyncStack();
6 Runnable source = new Producer(stack);
7 Runnable sink = new Consumer(stack);
8 Thread t1 = new Thread(source);
9 Thread t2 = new Thread(sink);
10 t1.start();
11 t2.start();
12 } 1
13}

Producer.java

1 package mod13;
2 public class Producer implements Runnable
3 {
4 SyncStack theStack;
5

Linguagem de Programação Java 169


6 public Producer(SyncStack s) {
7 thestack = s;
8 }
9
10 public void run() {
11 char c;
12 for (int i = 0; i < 20; i++) {
13 c = (char)(Math.random() * 26 + 'A');
14 theStack.push(c);
15 System.out.println (" Produzido: " + c);
16 try {
17 Thread.sleep((int)(Math.random() * 100));
18 } catch (InterruptedException e) {
19 //ignore-o
20 }
21 }
22 }
23 }

Consumer.java

1 package mod13;
2 public class Consumer implements Runnable
3 {
4 SyncStack thestack;
5
6 public Consumer(SyncStack s) {
7 thestack - s;
8 }
9
10 public void run() {
11 char c;
12 for (int i = 0; i < 20; i++) {
13 c = theStack.pop();
14 System.out.println("Consumido: " + c);
15 try {
16 Thread.sleep((int)(Math.random() * 1000));
17 } catch (InterruptedException e) {
18 //ignore-o
19 }
20 }
21 }
22 }

SyncStack.java

1 package mod13;
2 public class SyncStack
3 {
4 private int index - 0;
5 private char [] buffer = new char[6];
6
7 public synchronized char pop() {
8 while (index == 0) {
9 try {
10 this.wait();
11 } catch (InterruptedException e) {

Linguagem de Programação Java 170


12 //ignore-o
13 }
14 }
15 this.notify();
16 index--;
17 return buffer[index];
18 }
19
20 public synchronized void push(char c) {
21 while (index == buffer.length) {
22 try {
23 this.wait();
24 } catch (Interrupted-Exception e) {
25 //ignore-o
26 }
27 }
28 this.notify();
29 buffer[index] = c;
30 index++;
31 } 1
32 }

Linguagem de Programação Java 171


Linguagem de Programação Java 172
Capítulo 15

Utilização de rede com Java

Objetivos

Após concluir este módulo, você será capaz de:


• Criar um servidor TCP/IP simples em Java
• Criar um cliente TCP/IP simples em Java
• Entender como os sockets UDP são implementados em Java

Linguagem de Programação Java 173


Utilização de rede com Java
Soquetes (sockets)

Soquete é o nome atribuído, em determinado modelo de programação às extremidades de uma


ligação de comunicação entre processos. Como esse modelo de programação é muito conhecido,
tem sido reutilizado em outras áreas, incluindo Java.

Quando processos se comunicam via rede, Java usa o seu modelo de fluxo. Um soquete e mantém
dois fluxos: um fluxo de entrada em um fluxo de saída para enviar dados a um outro processo via
rede, basta que um processo escreva para o fluxo de saída associado ao soquete. Para ler os dados
escritos por outro processo, o processo deve ler a partir do fluxo de entrada associado ao soquete
.
Quando a conexão em rede estiver operacional, o uso de fluxos associados a essa conexão será
semelhante ao uso de qualquer outro fluxo.

Estabelecendo a conexão

Para estabelecer a conexão, uma máquina precisa estar executando um programa que aguarde
uma conexão e a outra extremidade deve tentar alcançar a primeira. É como um sistema telefônico:
uma pessoa faz a chamada enquanto a outra precisa estar aguardando o seu recebimento quando
do momento da chamada.

Fazendo a conexão

Ao fazer uma chamada telefônica, você precisa saber o número a ser discado. Ao fazer a conexão
em rede, você precisa saber um endereço ou nome da máquina remota. Além disso, em uma
conexão de rede, o número de porta que equivale a um número de ramal. Após conectar-se ao
computador apropriado, identifique o objetivo da conexão. Então, assim como um número de
extensão específico é usado para permitir a comunicação com o departamento de contabilidade, o
número de porta específico deve ser usado para permitir a comunicação com o programa de
contabilidade.

Número de portas

Os números de portas em sistemas TCP/IP são números de 16 bits e encontram-se na faixa de 0-


65535. Na prática, os números de portas inferiores a 1024 estão reservados para serviços pré
definidos. Então, só utilizem esses números caso queira comunicar-se com um desses serviços
(como telnet, correio SMTP, ftp e assim por diante).

Deve haver um acordo prévio entre os dois programas: o cliente deve iniciar a comunicação e o
servidor deve "aguardar a chamada". Se os nomes das portas usadas pelas duas partes do sistema
não coincidirem, não haverá comunicação.

Linguagem de Programação Java 174


Modelo de rede em Java

Na linguagem Java, as conexões ao soquete TCP/IP são implementadas com classes do pacote
java.net. O diagrama a seguir mostra o que ocorre no lado servidor e no lado cliente.

• O servidor atribui o número de porta. Quando o cliente é solicita uma conexão, o servidor
abre a conexão de soquete através do método accept ( ).
• O cliente que estabelece uma conexão com o host na porta port #.
• O cliente que o servidor se comunicam através de um InputStream e um OutputStream.

Observe uma amostra do código ser implementado para esse modelo simples nas próximas duas
páginas :

Servidor TCP/IP simples


Os aplicativos de servidor TCP/IP baseiam-se nas classes de rede SeverSocket e Socket
fornecidas pela linguagem Java. A classe SeverSocket trata da maior parte do trabalho ao
estabelecer um servidor.

1 import java.net.*;
2 import java.io.*;
3
4 public class SimpleServer {
5 public static void main(String args[]) {
6 ServerSocket s = null;
7 Socket s1;
8 String sendString = "Olá mundo da rede!";
9 int slength = sendString.length();
10 OutputStream s1out;
11 DataOutputStream dos;
12
13 //Registre seu serviço na porta 5432
14 try {
15 s = new ServerSocket (5432);

Linguagem de Programação Java 175


16 } catch )IOException e ) { }
17
18 // Execute o loop listen/accept para sempre
19 while (true) {
20 try {
21 // Aguarde um sinal de conexão
22 s1=s.accept();
23 // Obtenha um fluxo de comunicação associado ao soquete
24 s1out = s1.getOutoutStream();
25 dos = new DataOutputStream (s1out);
26
27 // Envie a string
28 dos.writeUTF(sendString);
29
30 //Feche a conexão
31 dos.close();
32 s1out.close();
33 s1.close();
34 } catch (IOException e) { }
35 }
36 }
37}

Cliente TCP/IP simples


O lado cliente de um aplicativo TCP/IP baseia-se na classe Socket. Novamente, grande parte do
trabalho de estabelecer conexões foi feito pela classe Socket. Esse cliente é anexado ao servidor
apresentado na página anterior e ecoa todos os dados enviados pelo servidor para o stdout.

1 import java.net.*;
2 import java.io.*;
3
4 public class SimpleClient {
5 public static void main(String args[]) throws IOException {
6 int c;
7 Socket s1;
8 InputStream s1In;
9 DataInputStream dis;
10
11 //Abra sua conexão para sunbert na porta 5432
12 s1 = new Socket ("127.0.0.1",5432);
13 // Obtenha um handle do arquivo de entrada a partir do soquete e
leia a entrada
13 s1In = s1.getInputStream();
14 dis = new DataInputStream (s1In);
15
16 String st = new String (dis.readUTF());
17 System.out.printIn(st);
18
19 // Quando terminar, feche a conexão e saia
20 dis.close();
21 s1In.close();
22 s1.close();
23 }
24 }

Linguagem de Programação Java 176


Soquete UDP
Enquanto o TCP/IP é um protocolo orientado a conexão, o UDP (User Datagram Protocol) é o
protocolo "sem conexão”.Para compreender as diferenças entre esses dois protocolos, considere a
diferença entre uma chamada telefônica e uma correspondência postal.
Nas chamadas telefônicas, a comunicação síncrona está garantida pois as mensagens são enviadas
e recebidas na hora de especificada. Uma comunicação por cartões-postais enviados por correio
postal nem sempre é síncrona - as mensagens podem chegar fora de ordem ou ser extraviadas.

O UDP é suportado por duas classes Java: DatagramSocket e DatagramPacket. O pacote é


uma mensagem completa inclui informações sobre o remetente, o tamanho da mensagem e a
própria mensagem.

DatagramPacket

DatagramPacket tem dois construtores: um para receber dados e outro para enviar dados:

• DatagramPacket(byte [] recvBuf, int readLenght) – Usado para configurar uma


matriz de bytes para receber um pacote UDP. A matriz byte está vazia quando é transmitida
ao construtor e a variável int é definida com o número de bytes a serem lidos (e não é
superior ao tamanho da matriz de bytes).

• DatagramPacket(byte [] sendBuf, int sendLenght, InetAddress iaddr,


int iport) – Usado para configurar um pacote UDP para a transmissão. O sendlength
não é superior à matriz de bytes sendBuf.

Soquetes UDP

DatagramSocket

O DatagramSocket é usado na leitura e gravação de pacotes UDP. Essa classe tem três
construtores que permitem especificar a porta e o endereço de internet aos quais será feito o
vínculo:

• DatagramSocket() – Vincula a qualquer porta disponível no host local

• DatagramSocket(int port) – Vincula a porta especificada no host local

• DatagramSocket(int port, InetAddress iaddr) – Vincula à porta especificada no


endereço especificado.

Linguagem de Programação Java 177


Linguagem de Programação Java 178
Capítulo 16

Introdução a API Servlets

Objetivos

Após concluir este módulo, você será capaz de:


• Criar uma página web dinâmica baseada em Servlets
• Receber e tratar parâmetros do usuário

Linguagem de Programação Java 179


Introdução a API Servlets
Servlets

Servlets são o paralelo tecnológido de Java aos CGIs (Commom Gateway Interface). São
programas que executam em um servidor Web, agindo como camada intermediária entre uma
requisição vinda de um navegador Web ou outro cliente HTTP e bancos de dados ou aplicações no
servidor HTTP ou outros servidores.

Sua função é:

• Ler dados enviados pelo usuário


• Analisar quaisquer outras informações sobre a requisição que possam estar embutidas na
requisição HTTP
• Gerar os resultados
• Formatar os resultados dentro de um documento
• Configurar os parâmetros de resposta HTTP apropriados
• Enviar o documento de volta ao cliente

Vantagens sobre CGIs tradicionais

Java Servlets são mais eficientes, simples para uso, mais poderosos, mais portáveis, seguros e
baratos que CGIs tradicionais e muitas alternativas a CGIs.
Eficiente
Não inicia novo processo a cada cliente.
Conveniente
Já decodifica informações HTTP sem onerar o desenvolvimento com isso.
Poderoso
Pode acessar funções nativas do servidor Web e é baseado em uma linguagem de baixo nível.
Portável
Por serem escritos em Java, os Servlets podem migrar de servidor e sistema operacional sem
recompilação.
Seguros
Muitos problemas de segurança baseados em caracteres codificados em requisições são eliminados
em servlets.
Baratos
Pode-se implementar servlets em servidores gratuitos, com sistemas operacionais gratuitos.

Em servlets, o servidor Web trata a maior parte do processo, sendo responsável pela inicialização
do código Java e pela chamada adequada e fornecimento das APIs. Temos diversos servidores
Web compatíveis com servlets e a implementação de referência, o Jakarta Tomcat, que é parte do
projeto Apache, e portanto, gratuito.

Java Server Pages

Enquanto em servlets, a codificação pode se tornar grande, em JSP, o código Java é mesclado com
a codificação Html, diminuindo o ciclo de desenvolvimento. A sintaxe é idêntica ao Servlet,
respeitadas as tags Html para início e fim do código JSP.

Estrutura Básica de Servlets

A listagem abaixo mostra um servlet básico que manuseia requisições GET. A requisição GET é o
tipo usual de requisições de um navegador Web. Servlets podem também manusear requisições
POST.

Linguagem de Programação Java 180


Para ser um servlet, a classe deve herdar de HttpServlet e sobrepor os métodos doGet e/ou doPost,
dependendo da página que gera a requisição. Se é desejado que o servlet responda tanto a GETs
quanto a POSTs, basta chamar doGet a partir de doPost, ou vice-versa.

Ambos os métodos recebem dois argumentos: um HttpServletRequest e um HttpServletResponse.


O objeto HttpServletRequest possui métodos que permitem descobrir informações sobre a
requisição http, os headers http e outros dados. O HttpServletResponse permite que se
especifiquem códigos de retorno, headers de resposta e mais importante, permite que um
PrintWriter possa ser estabelecido para enviar a resposta ao cliente. Para servlets simples, a maior
parte das respostas é enviada com comandos println que geram a página de resposta adequada.

Existem ainda as exceções que devem ser tratadas e as classes de herança, apontadas na
listagem.

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class ExemploServlet extends HttpServlet {

public void doGet(HttpServletRequest request, HttpServletResponse


response) throws ServletException, IOException {
// request será usado para receber os parâmetros
// response será usado para enviar a resposta

PrintWriter out = response.getWriter();


// out sera usado para envio da resposta com println
}
}

Um Servlet simples gerando resposta HTML

A listagem abaixo mostra um servlet simples enviando uma página estática como resposta. O servlet
não está tratando ainda nenhum parâmetro.

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class OlaMundo extends HttpServlet {

public void doGet(HttpServletRequest request, HttpServletResponse


response) throws ServletException, IOException {

response.setContentType(“text/html”);
PrintWriter out = response.getWriter();
out.println(“<HTML>\n”);
out.println(“<HEAD><TITLE>Ola Mundo</TITLE></HEAD>\n”);
out.println(“<BODY>\n”);
out.println(“<H1>Ola Mundo</H1>\n”);
out.println(“</BODY></HTML>\n”);

}
}

O servlet será iniciado segundo as regras do servidor em que está baseado, mas geralmente será
iniciado pelo próprio nome da classe na URL, como em

Linguagem de Programação Java 181


http://servidor.dominio:8080/servlet/OlaMundo. Os servidor podem mudar as regras de URL, mas o
servlet em si não precisa ser recompilado, se a versão da API for mantida.

Recebendo parâmetros do usuário via formulários

Para que uma página tenha um maior grau de dinamismo, é desejável que o usuário possa enviar
parâmetros através de controles de um formulário HTML em uma página web e receba um conteúdo
gerado através desses parâmetros.

Para recebermos parâmetros, devemos possuir uma página Web que possa enviar parâmetros por
um método POST ou GET. Na listagem abaixo, uma página contendo um formulário simples envia
três parâmetros por método GET. Se for desejado enviar os parametros por POST, deve ser
acrescentado method=”POST” na declaração do FORM. Os pontos mais importantes, como a
página que irá tratar a informação e o nome dos parâmetros estão em negrito.

<HTML>
<HEAD>
<TITLE>Enviando tres parametros</TITLE>
</HEAD>
<BODY>
<H1> Coletando tres parametros</H1>
<FORM ACTION="/servlet/TresParametros">
Primeiro parametro: <INPUT TYPE="TEXT" NAME="param1"><BR>
Segundo parametro: <INPUT TYPE="TEXT" NAME="param2"><BR>
Terceiro parametro: <INPUT TYPE="TEXT" NAME="param3"><BR>
<INPUT TYPE="SUBMIT" VALUE=”OK”>
</FORM>
</BODY>
</HTML>

Para se tratar parâmetros enviados por GET ou POST no código Servlet, é necessário que se
capture os parâmetros de forma semelhante ao tratamento de parâmetros de linha de comando. Da
mesma forma que na linha de comando, os parâmetros serão enviados como Strings, ficando a
cargo do servlet converter para outros tipos primitivos ou classes. O método getParameter, aplicado
em request irá retornar os parâmetros.

Os métodos que recebem os parâmetros estão destacados para melhor visualização.

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class TresParametros extends HttpServlet {

public void doGet(HttpServletRequest request, HttpServletResponse


response) throws ServletException, IOException {

response.setContentType(“text/html”);
PrintWriter out = response.getWriter();
out.println(“<HTML>\n”);
out.println(“<HEAD><TITLE>Parametros</TITLE></HEAD>\n”);
out.println(“<BODY>\n”);
out.println(“<H1>Primeiro parâmetro:</H1>\n”);
out.println(request.getParameter(“param1”)+”<BR>”);
out.println(“<H1>Segundo parâmetro:</H1>\n”);
out.println(request.getParameter(“param2”)+”<BR>”);
out.println(“<H1>Terceiro parâmetro:</H1>\n”);

Linguagem de Programação Java 182


out.println(request.getParameter(“param3”)+”<BR>”);
out.println(“</BODY></HTML>\n”);

}
}

Dessa forma, podemos enviar os parâmetros necessários ao código servlet, e acessar quaisquer
recursos ou informações necessárias para montar a página de resposta.

Linguagem de Programação Java 183


Linguagem de Programação Java 184
Apêndice A

Apresentações de Introdução

Linguagem de Programação Java 185

Você também pode gostar