You also want an ePaper? Increase the reach of your titles
YUMPU automatically turns print PDFs into web optimized ePapers that Google loves.
3<br />
M ó d u l o<br />
INSTITUTO DE SOFTWARE DO CEARÁ – INSOFT<br />
XI Semana da Tecnologia da Informação: na onda das férias<br />
básico e intermediário
XI SEMANA DA TECNOLO GIA DA INFORMAÇÃO<br />
<strong>Java</strong> básico e intermediário<br />
60.150-160 • Av Santos Dumont, 1180 • Fortaleza - CE<br />
Tel.: (85) 488.5200 • Fax: (85) 488.5210<br />
Instrutor:<br />
Felipe Gaúcho<br />
gaucho@atlantico.com.br<br />
• todos os direitos reservados •
Índice analítico<br />
A tecnologia <strong>Java</strong> 4<br />
O que é <strong>Java</strong> ? 4<br />
O que é máquina virtual ? 5<br />
O que é garbage collection ? 6<br />
A estrutura de um código em <strong>Java</strong>. 6<br />
Exercícios 7<br />
Implementando o primeiro programa 8<br />
O ambiente de desenvolvimento 8<br />
O código fonte 9<br />
Compilando e executando o programa 10<br />
Erros mais comuns 11<br />
Exercícios 12<br />
Sintaxe da linguagem <strong>Java</strong> 14<br />
Delimitadores em <strong>Java</strong> 14<br />
Comentários 14<br />
Identificadores e palavras reservadas 15<br />
Declarando variáveis em <strong>Java</strong> 16<br />
Tipos primitivos da linguagem <strong>Java</strong> 17<br />
Tipos lógicos: boolean 17<br />
Tipos textuais: char e String 17<br />
Tipos numéricos inteiros: byte, short, int e long<br />
18<br />
Tipos numéricos de ponto flutuante: float e<br />
double 19<br />
Convenções de codificação 19<br />
Exercícios 19<br />
Expressões 21<br />
Operadores lógicos e aritméticos 21<br />
Concatenação de Strings com o operador + 22<br />
Promoção e Casting 23<br />
Operadores de deslocamento (>>, >) 24<br />
Circuitos lógicos 25<br />
Exercícios 26<br />
Fluxo de controle 27<br />
Ramificação if, else 27<br />
Ramificação switch 28<br />
Repetição for 29<br />
Repetição while 30<br />
Comandos especiais de controle de fluxo: break,<br />
continue e label 31<br />
Lendo valores do teclado 32<br />
Exercícios 33<br />
Agrupamento de dados (Arrays) 34<br />
Declarando e criando Arrays 34<br />
Inicializando os valores de um Array 35<br />
Array multi-dimensional 36<br />
Array esparso 36<br />
Exercícios 37<br />
Introdução à Orientação a Objetos 38<br />
Motivação ao uso de um novo paradigma,<br />
orientado a objetos. 38<br />
Tipos agregados de dados 39<br />
Criando objetos 41<br />
Alocação de memória durante a criação de<br />
objetos 42<br />
Atribuição de referências a uma variável 42<br />
Termos básicos em Orientação a Objetos 43
JAVA BÁSICO E INTERMEDIÁRIO<br />
Abstração de dados 44<br />
Tipos abstratos de dados 44<br />
Definição de métodos 45<br />
A referência this 48<br />
Gerando a documentação de um programa -<br />
javadoc 49<br />
Documentação da API <strong>Java</strong> 50<br />
Exercícios 50<br />
Encapsulamento e sobrecarga de métodos 52<br />
Acessibilidade dos membros de uma classe 52<br />
O modificador final 53<br />
O modificador private 54<br />
Encapsulamento 55<br />
Sobrecarga de métodos 55<br />
Exercícios 56<br />
Construtores 57<br />
Processo de instanciação de objetos 57<br />
Inicialização explícita de membros variáveis 57<br />
Construtores 59<br />
Exercícios 60<br />
Herança e polimorfismo 61<br />
O conceito de classificação 61<br />
A palavra reservada extends 62<br />
Herança em <strong>Java</strong> 63<br />
A superclasse Object 64<br />
Polimorfismo 64<br />
Argumentos e coleções heterogêneas 65<br />
O operador instaceof 66<br />
Exercícios 67<br />
Classes abstratas e interfaces 68<br />
Classes abstratas e concretas 68<br />
O modificador abstract 69<br />
Restrição de herança pelo modificador final 70<br />
As referências this e super 70<br />
Exercícios 71<br />
Tratamento de exceções 73<br />
O que são exceções ? 73<br />
Diferença entre exceções e erros 73<br />
Tratamento de exceções 74<br />
Tratamento de exceções em <strong>Java</strong> 75<br />
A hierarquia das exceções 76<br />
Tratamento pendente de exceções 77<br />
Exceções implementadas pelo programador 77<br />
Sinalizando uma exceção (throw) 78<br />
Throwable.printStackTrace() e<br />
Throwable.getMessage() 78<br />
Exceções mais comuns 79<br />
Exercícios 80<br />
Interface gráfica com o usuário 81<br />
Componentes gráficos – o pacote AWT 81<br />
Código do exemplo de componentes AWT 83<br />
Gerenciadores de Layout 86<br />
Containers 86<br />
Flow layout 87<br />
CardLayout 88<br />
BorderLayout 91<br />
GridLayout 93<br />
GridBagLayout 94<br />
Applets 97<br />
O que é um Applet ? (java.awt.Applet) 97<br />
Restrições de segurança em applets 97<br />
O primeiro applet 98<br />
Ciclo de vida de um applet 100<br />
Contexto gráfico AWT 101<br />
Aprendendo a usar o appletviewer 103<br />
O código HTML de carga do applet 103<br />
Lendo parâmetros do Html com um applet 105<br />
Manipulando imagens 106<br />
Exercícios 107<br />
Interfaces gráficas baseadas em behaviorismo – O<br />
pacote Swing 108<br />
O que é Swing ? 108<br />
Containers Swing 108
JAVA BÁSICO E INTERMEDIÁRIO<br />
Introdução a padrões de projeto (design<br />
patterns) 109<br />
Programação behaviorista - o paradigma<br />
modelo-visão-controle (MVC) 110<br />
Implementando um modelo em <strong>Java</strong><br />
(Observable) 113<br />
Implementando visões para um modelo 114<br />
Criando o primeiro aplicativo MVC 114<br />
Acesso a dispositivos de entrada e saída - I/O 116<br />
O que são I/O Streams ? 116<br />
Leitura de dados (java.io.InputStream) 117<br />
Escrita de dados (java.io.OutputStream) 118<br />
As classes de manipulação de Streams em <strong>Java</strong><br />
(o pacote java.io) 118<br />
Leitores e Escritores de dados em Streams com<br />
buffer 120<br />
Conversão entre bytes e caracteres 121<br />
O que é UNICODE? 121<br />
Manipulação de arquivos seqüenciais 122<br />
Manipulação de arquivos randômicos 123<br />
Serialização de objetos 124<br />
O que são Grafos de Objetos? 124<br />
Lendo objetos serializados de um arquivo 125<br />
Exercícios 126
A tecnologia <strong>Java</strong><br />
Esta seção contextualiza a tecnologia <strong>Java</strong> no mercado de informática e enumera suas<br />
principais características e termos.<br />
O que é <strong>Java</strong> ?<br />
<strong>Java</strong> é:<br />
?? Uma linguagem de programação<br />
?? Um ambiente de desenvolvimento<br />
?? Um ambiente de aplicação<br />
1<br />
<strong>Java</strong> é uma linguagem de programação desenvolvida pela SUN com o objetivo de manter o poder<br />
computacional de C++, agregando características de segurança, robusteza e portabilidade.<br />
Os objetivos primários da SUN ao desenvolver a linguagem <strong>Java</strong> foram:<br />
?? Criar uma linguagem orientada a objetos<br />
?? Prover um ambiente de desenvolvimento com duas características básicas:<br />
o Velocidade de desenvolvimento, eliminando o ciclo compilar-ligar-carregar-testar<br />
tradicional em outras linguagens como C++, Pascal, etc.<br />
o Portabilidade – prover um ambiente meta interpretado, permitindo que o mesmo código<br />
rode em diversos sistemas operacionais sem a necessidade de adaptação ou uso de<br />
bibliotecas específicas.<br />
?? Eliminar exigências de programação que tradicionalmente afetam a robusteza de um código de<br />
computador:<br />
o Aritmética de ponteiros (comum em ANSI C/C++)<br />
o Controle de alocação/liberação de memória (comum em Pascal, C, C++, Basic, ...)<br />
?? Permitir a programação multitarefa, mesmo em sistemas operacionais que não dêem suporte<br />
nativo a Threads.<br />
?? Permitir que um programa seja dinamicamente modificado através da carga de componentes via<br />
redes de computadores, como a Internet.<br />
?? Prover um modo de checar a integridade de um programa quanto à sua origem, garantindo a<br />
segurança do sistema operacional e de dados durante a sua execução.<br />
A arquitetura <strong>Java</strong> é formada pelas seguintes características:<br />
?? A máquina virtual <strong>Java</strong> (JVM – <strong>Java</strong> Virtual Machine, atualmente na versão 1.4)<br />
?? Gerenciador de alocação/liberação de memória (Garbage Collection)<br />
?? Sand box – módulo de garantia de segurança de código (é impossível criar um vírus em <strong>Java</strong>)
A TECNOLOGIA JAVA<br />
O que é máquina virtual ?<br />
De acordo com a especificação da SUN, a máquina virtual do <strong>Java</strong> pode ser vista como:<br />
Uma máquina imaginária que é implementada via software ou hardware. Um código a<br />
ser executado por essa máquina deve ser gravado em um arquivo com extensão<br />
.class. e possuir um código compatível com as instruções <strong>Java</strong>.<br />
Para um programa <strong>Java</strong> ser executado, ele precisa passar pelo processo ilustrado na figura abaixo:<br />
O código é compilado, gerando um conjunto de instruções chamado de byte-code. Esse byte-code é<br />
aplicado à Máquina Virtual <strong>Java</strong> (JVM) que se encarrega de interpretar os comandos para o sistema<br />
operacional onde o programa está rodando. Ou seja, a máquina virtual traduz as instruções do código<br />
<strong>Java</strong> para instruções válidas no sistema operacional em que está rodando. Se essa portabilidade fosse<br />
requerida em C, o código deveria ser compilado várias vezes – uma para cada sistema operacional<br />
desejado. No caso do <strong>Java</strong>, o código é compilado apenas uma vez, gerando o byte-code. Esse byte-code<br />
poderá então ser interpretado por qualquer máquina virtual <strong>Java</strong>, rodando em Linux, Windows, Palm OS,<br />
Solaris ou qualquer outro sistema operacional que possua uma máquina virtual <strong>Java</strong> implementada.<br />
(Compile once, run anywhere).<br />
Uma JVM possui definições concretas para a implementação dos seguintes itens:<br />
?? Conjunto de instruções (equivalentes às instruções da CPU)<br />
?? Conjunto de registradores<br />
?? Formato padrão de classes<br />
?? Pilha de memória<br />
?? Pilha de objetos coletados pelo garbage-collector<br />
?? Área de memória<br />
IMPORTANTE: a JVM não permite que um programa <strong>Java</strong> acesse recursos de hardware diretamente,<br />
protegendo o computador de operações perigosas, como acesso à regiões protegidas da memória ou<br />
formatação física do disco rígido.<br />
Um programa <strong>Java</strong> só é executado caso o seu byte-code passe pela verificação de segurança da JVM,<br />
que consiste em dizer que:<br />
?? O programa foi escrito utilizando-se a sintaxe e semântica da linguagem <strong>Java</strong><br />
?? Não existem violações de áreas restritas de memória no código<br />
?? O código não gera Stack Overflow<br />
5
A TECNOLOGIA JAVA<br />
?? Os tipos de parâmetros dos métodos são corretos<br />
?? Não existe nenhuma conversão ilegal entre dados do programa, como a tentativa de conversão<br />
de inteiros em ponteiros<br />
?? O acesso a objetos está corretamente declarado<br />
Caso alguma das condições acima não seja satisfeita, a máquina virtual <strong>Java</strong> causará um erro de<br />
execução (runtime error).<br />
O que é garbage collection ?<br />
Durante a execução de um programa de computador, ocorre a alocação e liberação dinâmica de<br />
memória RAM. Dados são escritos e lidos da memória do computador satisfazendo os requisitos de cada<br />
programa. Em linguagens tradicionais como Pascal, Basic e C/C++, o programador é responsável por<br />
controlar essa alocação, impedindo o estouro de memória (stack overflow) e outros problemas, como o<br />
acesso indevido a áreas reservadas de memória. Para facilitar a vida dos programadores, e evitar os<br />
erros comuns associados à alocação de memória, a linguagem <strong>Java</strong> introduziu um novo conceito: o<br />
garbage-collection.<br />
Garbage-collection é um mecanismo de controle automático de alocação e liberação de memória.<br />
Quando uma variável é declarada em um código de computador, a JVM cria um ponteiro para uma área<br />
de memória equivalente ao tamanho do tipo de dado utilizado por essa variável. Quando essa variável é<br />
associada a outra região de memória, a JVM coloca o espaço alocado anteriormente em uma pilha de<br />
objetos em desuso. Caso o computador fique com pouca memória disponível, a JVM remove objetos<br />
dessa pilha, permitindo que esse espaço de memória seja re-alocado.<br />
O processo de garbage-collection ocorre automaticamente durante a execução de um programa <strong>Java</strong>. O<br />
programador não precisa se preocupar com aritmética de ponteiros (grande dificuldade em linguagens<br />
como C e Pascal).<br />
A estrutura de um código em <strong>Java</strong>.<br />
Como todas as outras linguagens de programação, <strong>Java</strong> possui um formato básico para a escrita de<br />
códigos. Tal formato é demonstrado abaixo:<br />
1. // Duas barras significam comentário<br />
2. /* comentários também podem seguir o formato de C++ */<br />
3.<br />
4. public class NomeDoPrograma<br />
5. {<br />
6. // O método main sempre deve estar presente para que um código<br />
7. // <strong>Java</strong> possa ser executado:<br />
8. static public void main(String[] args)<br />
9. {<br />
10. // aqui virão os comandos, que são parecidos com C++<br />
11. }<br />
12. }<br />
Compreendendo o código <strong>Java</strong>:<br />
?? linhas 1 e 2: representam comentários. Um comentário pode conter qualquer informação<br />
relevante ao comportamento do programa, autor, versão, etc.<br />
?? linha 3: está em branco, pois <strong>Java</strong> permite linhas em branco entre os comandos<br />
6
A TECNOLOGIA JAVA<br />
?? linha 4: é a declaração do "nome do programa", que é case-sensitive (existe diferença<br />
entre maiúsculas e minúsculas). O arquivo que contém o código <strong>Java</strong> deve ser salvo com<br />
o mesmo nome que aparece após a declaração public class e mais a extensão .java (o<br />
exemplo acima deveria ser salvo como NomeDoPrograma.java).<br />
?? linha 5 e 9: a abertura de chave { indica início de bloco (tal qual begin em Pascal)<br />
?? linha 8: essa linha deve aparecer em todos os códigos <strong>Java</strong>. Quando um programa <strong>Java</strong><br />
é executado, o interpretador da JVM executa os comandos que estiverem dentro do<br />
bloco indicado pelo método "static public void main(String)".<br />
?? Linha 10: aqui seria escrito o código propriamente dito. Instruções como for-next, print,<br />
etc.<br />
?? Linha 11 e 12: o fechamento de chave } indica início de bloco (tal qual end em Pascal)<br />
Exemplo de código <strong>Java</strong>:<br />
/**<br />
* Instituto de Software do Ceará - INSOFT<br />
* XI Semana tecnológica de férias<br />
* Primeiro programa – escrever a mensagem alô mundo na tela.<br />
*/<br />
public class AloMundo<br />
{<br />
static public void main(String[] args)<br />
{<br />
System.out.println("Alo Mundo");<br />
}<br />
}<br />
Como rodar o programa acima ?<br />
1. Salve o código acima em um arquivo nomeado: AloMundo.<strong>Java</strong> (não esqueça o case-sensitive)<br />
2. Digite no console:<br />
a. C:\fic>javac AloMundo.java<br />
3. Caso não ocorra nenhuma mensagem de erro, digite:<br />
a. C:\fic>java AloMundo<br />
Exercícios<br />
a. Verifique se o sjdk1.4.1 está corretamente instalado em sua máquina (digite java no console e<br />
observe. Caso não aconteça nada, procure a documentação que vem junto ao sjdk1.4.1)<br />
b. Escreva um programa em <strong>Java</strong> para imprimir seu nome na tela do computador. Compile e rode<br />
esse programa.<br />
Dica: o comando em <strong>Java</strong> para imprimir mensagens na tela é o seguinte:<br />
System.out.println("mensagem"); Não esqueça do ponto e vírgula no final<br />
7
Implementando o primeiro<br />
programa<br />
Ao final da seção anterior, o aluno foi apresentado ao formato de programas <strong>Java</strong>. O<br />
texto abaixo orienta a edição, compilação e execução destes programas.<br />
O ambiente de desenvolvimento<br />
Para que você possa compreender o conteúdo desta aula, é necessário que você já tenha instalado o<br />
<strong>Java</strong> em sua máquina. As instruções de instalação do j2sdk1.4.1 serão apresentadas na aula de<br />
laboratório. Para verificar se o <strong>Java</strong> foi corretamente instalado em sua máquina, faça o seguinte:<br />
- abra um console DOS em seu computador (no Win2000, clique iniciar/executar e digite o comando<br />
cmd).<br />
- depois que o console estiver ativo, digite o seguinte comando : <strong>Java</strong> –version <br />
- deverá aparecer uma mensagem parecida com esta:<br />
C:\>java -version<br />
java version "1.4.1"<br />
<strong>Java</strong>(TM) 2 Runtime Environment, Standard Edition (build 1.4.1 -b92)<br />
<strong>Java</strong> HotSpot(TM) Client VM (build 1.4.1-b92, mixed mode)<br />
C:\><br />
Caso não esteja funcionando, revise o processo de instalação do j2sdk1.4.1 antes de continuar essa<br />
aula. Um site que pode lhe ajudar nessa instalação está em:<br />
http://geocities.com/canaljava/olamundo.html<br />
Não esqueça de configurar as variáveis de ambiente de seu sistema operacional. Em ambiente Windows,<br />
as configurações são as seguintes:<br />
path = %path%;c:\j2sdk1.4.1\bin<br />
classpath = %classpath%;.;c:\j2sdk1.4.1\jre\lib<br />
2<br />
* Em ambientes UNIX/Linux, o delimitador das variáveis de ambiente é o dois pontos (:) ao invés de<br />
ponto e vírgula.<br />
** Detalhes sobre a instalação podem ser encontrados na página da SUN: Your First Cup of <strong>Java</strong><br />
http://java.sun.com/docs/books/tutorial/getStarted/cupojava/index.html
IMPLEMENTANDO O PRIM EIRO PROGRAMA<br />
O código fonte<br />
Como qualquer outra linguagem de programação, <strong>Java</strong> é usada para criar aplicações de computador. O<br />
texto que contém os comandos a serem executados pela JVM é chamado de código-fonte, ou<br />
simplesmente fonte. O conjunto mínimo de instruções necessário para que um código-fonte seja<br />
considerado um programa <strong>Java</strong> aparece no quadro abaixo:<br />
/**<br />
* Instituto de Software do Ceará - INSOFT<br />
* XI Semana tecnológica de férias<br />
* Primeiro programa – escrever a mensagem alô mundo na tela.<br />
*/<br />
public class AloMundo<br />
{<br />
static public void main(String[] args)<br />
{<br />
System.out.println("Alô mundo");<br />
}<br />
}<br />
Analisando o código acima:<br />
?? As primeiras 5 linhas representam um bloco de comentário, que tem por objetivo identificar a<br />
função do programa, seu autor, versão, etc. Mais adiante na disciplina aprenderemos a utilizar a<br />
ferramenta de documentação do <strong>Java</strong>, chamada javadoc.<br />
?? A linha seguinte (public class AloMundo) declara uma classe chamada AloMundo. Após<br />
compilado, esse código gerará um arquivo AloMundo.class no mesmo diretório em que se<br />
encontra o código fonte. Um código fonte em <strong>Java</strong> pode descrever mais de uma classe. Após a<br />
compilação, cada descrição de classe gerará um arquivo .class separado. Observer que pode<br />
haver no máximo um classe public dentro de cada código-fonte <strong>Java</strong>. Caso você<br />
inadvertidamente declare mais de uma classe como public dentro de um código-fonte <strong>Java</strong>,<br />
ocorrerá um erro de compilação. O corpo da classe (o código que define a classe) deve ser<br />
delimitado por chaves, assim como toda a estrutura de dados, decisão ou controle em <strong>Java</strong>.<br />
?? A seguir encontra-se a declaração do método inicial de um programa <strong>Java</strong>. Todo programa <strong>Java</strong><br />
começa a ser executado pelo método main (tal qual C/C++ e várias outras linguagens). Note que<br />
o método main é declarado com uma série de modificadores e com uma matriz de Strings como<br />
parâmetro. Não se preocupe se no momento esses conceitos parecem confusos, mais tarde você<br />
aprenderá o motivo de cada um deles. Por enquanto basta saber o seguinte sobre o método<br />
main: static public void main(String[] args)<br />
o static: um modificador utilizado pelo compilador para identificar métodos que podem ser<br />
executados apenas no contexto da classe AloMundo, sem a necessidade que um objeto<br />
dessa classe seja instanciada.<br />
o public: o método main opde ser executado por qualquer processo ativo no sistema<br />
operacional, incluindo o interpretador <strong>Java</strong>.<br />
o void: indica o tipo do valor (int, char, etc.) a ser retornado pelo método main. Quando um<br />
tipo de retorno é declarado como void, significa que o método não retorna nenhum valor.<br />
O método main sempre deverá ser declarado static public void. Caso contrário o<br />
programa não poderá ser executado (Exception in thread "main"<br />
java.lang.NoSuchMethodError: main).<br />
9
IMPLEMENTANDO O PRIM EIRO PROGRAMA<br />
o String[] args: um array de objetos do tipo String, que serve para armazenar a lista de<br />
argumentos digitados na linha de comando após o nome da classe a ser executada:<br />
C:\alo>java AloMundo nome numero ...<br />
C:\alo>_<br />
Esses argumentos são acessados pelo índice da matriz args, ou seja, args[0] = nome,<br />
args[1] = numero, etc.<br />
?? Dentro do método main, existe um exemplo de comando em <strong>Java</strong>. Nosso próximo passo será<br />
identificar o conjunto de comandos mais comuns em <strong>Java</strong>. Por hora, usaremos esse comando<br />
apenas para treinar a compilação e execução de programas <strong>Java</strong>. O método System.out.println<br />
é utilizado para escrever uma mensagem no dispositivo padrão de saída do <strong>Java</strong>, que é a tela do<br />
computador (mais tarde, aprenderemos como configurar dispositivos de entrada e saída, que<br />
podem ser: a tela do computador, uma conexão da Internet, o disco rígido, etc.)<br />
Compilando e executando o programa<br />
Para compilar o programa AloMundo proceda da seguinte maneira:<br />
1. Digite o código do AloMundo, conforme aparece na figura acima, utilizando um editor de textos<br />
padrão Ascii. (Para ter certeza que o seu texto será salvo em Ascii, no windows, use o notepad.<br />
Ou então utilize o editor recomendado pela disciplina)<br />
2. Salve o código-fonte do AloMundo em um arquivo chamado: AloMundo.java<br />
3. Ative um console DOS (no Windows, iniciar/executar/CMD)<br />
4. Vá para o diretório em que você salvou o código-fonte do AloMundo (cd )<br />
5. Execute o compilador <strong>Java</strong>, passando como parâmetro o n ome do arquivo a ser compilado:<br />
C:\>_<br />
C:\>cd alo<br />
C:\alo>javac AloMundo.java<br />
C:\alo>_<br />
6. Se após executar o comando javac o sistema não acusou nenhum erro, é porque o programa<br />
AloMundo.java foi compilado com sucesso.<br />
7. Para verificar se o byte-code do seu programa foi gerado com sucesso, liste o conteúdo diretório<br />
em que você está trabalhando:<br />
C:\alo>dir<br />
O volume na unidade C é WINDOWS<br />
O número de série do volume é B4EC-EDD8<br />
Pasta de C:\alo<br />
19/02/2002 15:54 .<br />
19/02/2002 15:54 ..<br />
19/02/2002 15:54 190 AloMundo.class<br />
10
IMPLEMENTANDO O PRIM EIRO PROGRAMA<br />
19/02/2002 12:52 191 AloMundo.java<br />
2 arquivo(s) 381 bytes<br />
2 pasta(s) 931.860.480 bytes disponíveis<br />
C:\alo>_<br />
8. Observe que o compilador <strong>Java</strong> criou um arquivo chamado AloMundo.class em seu diretório.<br />
Esse arquivo contém o byte-code, o conjunto de instruções executáveis pela JVM.<br />
Para rodar o programa AloMundo, basta carregar a máquina virtual <strong>Java</strong> (JVM) passando como<br />
parâmetro o nome da classe a ser executada:<br />
C:\alo>java AloMundo<br />
Al mundo<br />
C:\alo>_<br />
Note também que o nome do arquivo aparece sem a extensão .class. Esse é um detalhe importante,<br />
pois causa problemas à maioria dos iniciantes em <strong>Java</strong>. O ponto em nome de arquivos obedece ao<br />
padrão UNIX, ou seja, se você executar o programa através de seu nome + . + extensão o JVM vai<br />
pensar que se trata de um subdiretório:<br />
C:\alo>java AloMundo.class<br />
Exception in thread "main" java.lang.NoClassDefFoundError: AloMundo/class<br />
C:\alo>_<br />
Erros mais comuns<br />
Erros de compilação:<br />
Variável de ambiente (PATH) mal configurada: um dos erros mais freqüentes entre iniciantes <strong>Java</strong> é a<br />
configuração incorreta das variáveis de ambiente PATH e CLASSPATH. Lembre-se que o diretório que<br />
contém os arquivos executáveis que compõem a JVM deve estar indicado na variável PATH do sistema<br />
operacional (na instalação padrão, esse diretório é o ..\j2sdk1.4.1.1\bin) Caso contrário ocorrerá o<br />
seguinte erro:<br />
C:\alo>javac AloMundo.java<br />
'javac' não é reconhecido como um comando interno<br />
ou externo, um programa operável ou um arquivo em lotes.<br />
C:\alo>_<br />
SOLUÇÃO: você deve incluir o diretório onde o <strong>Java</strong> foi instalado na definição da variável de ambiente<br />
PATH. No Windows 2000, abra o painel de controle, clique no ícone Sistema e depois na aba Avançado.<br />
Você verá um botão chamado Variáveis de ambiente... Clique nesse botão e inclua o diretório da<br />
instalação <strong>Java</strong> na variável PATH. Importante: você vai ter que fechar o console atual e abrir um outro<br />
para que a alteração tenha efeito. Para entender melhor o que é uma variável de ambiente, consulte o<br />
Help do Windows.<br />
Erro de digitação: muito comum quando ainda não se está acostumado com a sintaxe de uma<br />
linguagem. No exemplo abaixo, ao invés de digitar println o usuário digitou printl.<br />
11
IMPLEMENTANDO O PRIM EIRO PROGRAMA<br />
C:\alo>javac AloMundo.java<br />
AloMundo.java:9: cannot resolve symbol<br />
symbol : method printl (java.lang.String)<br />
location: class java.io.PrintStream<br />
System.out.printl("Al mundo");<br />
^<br />
1 error<br />
C:\alo>_<br />
SOLUÇÃO: revisar o código. Note que o compilador acusa em quais linhas ocorreram erros.<br />
case-sensitive ou nome de arquivo diferente do nome da classe pública: Outra fonte de erros é o<br />
case-sensitive, ou seja, no <strong>Java</strong> palavras minúsculas são diferentes das maiúsculas. Se, por exemplo, o<br />
programador salvar o arquivo com o nome de alomundo.java, o compilador procurará uma classe<br />
chamada alomundo e não AloMundo esses dois nomes, em <strong>Java</strong>, são diferentes.<br />
C:\alo>javac AloMundo.java<br />
AloMundo.java:5: class aloMundo is public, should be declared in a file<br />
named aloMundo.java<br />
public class aloMundo<br />
^<br />
1 error<br />
C:\alo>_<br />
SOLUÇÃO: revisar o código. Note que o compilador acusa em quais linhas ocorreram erros – no exemplo<br />
acima, o erro ocorreu na linha 5.<br />
Erros de execução:<br />
Variável de ambiente (CLASSPATHPATH) mal configurada: durante o processo de compilação, a má<br />
configuração da variável PATH causava um erro de compilação. Aqui o erro é de execução, e pode ser<br />
causado pela ausência da variável de ambiente CLASSPATH.<br />
C:\alo>java AloMundo<br />
Exception in thread "main" java.lang.NoClassDefFoundError: AloMundo<br />
C:\alo>_<br />
SOLUÇÃO: faça o mesmo procedimento de configuração de variáveis de ambiente e ajuste a variável<br />
CLASSPATH para o diretório que contém as bibliotecas padrão <strong>Java</strong> (na instalação padrão: ../<br />
j2sdk1.4.1/jre/lib).<br />
Exercícios<br />
a. Crie o arquivo AloMundo.<strong>Java</strong> com algum editor de textos. Em seguida compile e execute o<br />
programa AloMundo.<br />
b. Modifique o programa AloMundo para que ele desenhe os seguintes formatos na tela:<br />
C:\alo>java AloMundo<br />
* * * * * *<br />
* * * * * * *<br />
* * * * * * * *<br />
* * * * * * *<br />
12
IMPLEMENTANDO O PRIM EIRO PROGRAMA<br />
* * * * * *<br />
C:\alo>_<br />
c. Dê uma olhada nos arquivos de documentação do j2sdk1.4.1.<br />
13
Sintaxe da linguagem <strong>Java</strong><br />
Nesta seção será apresentado o formato de codificação da linguagem <strong>Java</strong>como códigos<br />
de computador devem ser escritas aluno deve reconhecer Desde o seu surgimento, a<br />
tecnologia <strong>Java</strong> vem contribuindo no amadurecimento de processamento remoto e ligado<br />
à Internet. o mercado a se adpatar , nos anos 90, Reconhecida como tecnologia<br />
Delimitadores em <strong>Java</strong><br />
Em um código-fonte <strong>Java</strong>, alguns símbolos são utilizados pelo compilador para diferenciar comandos,<br />
blocos de comandos, métodos, classes, etc. Tais símbolos, chamados de delimitadores, são<br />
enumerados abaixo:<br />
Comentários<br />
Comentários servem para identificar a função de um comando, um trecho de código, ou um método.<br />
Além disso, os comentários podem ser utilizados para documentar aspectos de desenvolvimento de um<br />
programa: como o nome da empresa que o desenvolveu, nome dos programadores, versão, etc. Existem<br />
dois tipos de comentário: o simples e o de documentação.<br />
O comentário simples é delimitado por duas baras // e termina ao final de uma linha.<br />
O comentário de documentação é iniciado pelo símbolo /** e encerrado pelo símbolo */, podendo conter<br />
várias linhas de texto e linhas em branco.<br />
// comentário simples: não será incluído na documentação<br />
// do programa (note que comentários simples exigem<br />
// um par de barras para cada linha).<br />
/**<br />
FIC – Sistemas Orientados a Objetos I<br />
Exercício de aula<br />
Esse tipo de comentário será utilizado pela ferramenta geradora de<br />
documentação javadoc, que acompanha o ambiente de desenvolvimento<br />
sjdk1.3.1. O uso do javadoc será comentado em aula. A descrição das tags<br />
utilizadas pelo javadoc pode ser encontrada na documentação da <strong>Java</strong>.<br />
@author FIC - Faculdade Integrada do Ceará<br />
@version 1.0 beta<br />
@date fevereiro de 2002<br />
*/<br />
3<br />
Ponto e vírgula, blocos e espaços em branco: em <strong>Java</strong>, todo comando é terminado por um ponto e<br />
vírgula (;). Exemplo: System.out.println("note o ponto e vírgula no final ");
SINTAXE DA LINGUAGEM JAVA<br />
Um bloco é formado por um conjunto de instruções delimitadas por chaves, como no exemplo abaixo:<br />
/** Exemplo de bloco */<br />
{ // Chamamos essa chave de início de bloco<br />
int ano; // Note o ponto e vírgula<br />
ano = 2002; // sempre ao final de um comando <strong>Java</strong><br />
} // Chamamos essa chave de final de bloco<br />
o espaço em branco, quebra de linha e caracteres de tabulação são permitidos em qualquer trecho do<br />
código-fonte, devendo ser utilizados para realçar o aspecto visual de seu código.<br />
apesar do aspecto visual do código-fonte não ter nenhum impacto no desempenho de um programa<br />
<strong>Java</strong>, o uso de endentação é uma característica de bons códigos-fonte. Lembre-se que você não será o<br />
único a ler o código-fonte de seus programas, portanto escreva-o da maneira mais organizada e legível<br />
possível.<br />
Identificadores e palavras reservadas<br />
Em <strong>Java</strong>, um identificador é uma seqüência de símbolos UNICODE (64K símbolos) que começa com<br />
uma letra, um símbolo subscrito _, ou o caractere $. Os demais símbolos de um identificador podem<br />
conter também números. Identificadores são case-sensitive e não tem um tamanho máximo estabelecido.<br />
Apesar da tabela UNICODE ser bastante extensa, um bom hábito de programação é utilizar somente<br />
letras do alfabeto (a-Z) e números para nomear identificadores.<br />
Exemplo de identificadores válidos em <strong>Java</strong>:<br />
?? data<br />
?? _data<br />
?? $data<br />
?? data_do_mês<br />
?? data1<br />
?? uma_variável_pode_SER_bastante_extensa_e_conter_Numeros234876238476<br />
?? data_public_class_NoteQueEsseIdentificadorContemPalavrasReservadas<br />
Apesar desta "liberdade" de opções para nomes de identificadores, algumas palavras não são permitidas.<br />
Tais palavras são ditas palavras reservadas, e representam o conjunto de comandos que forma a sintaxe<br />
da linguagem <strong>Java</strong>. O conjunto de palavras reservadas em <strong>Java</strong> é o seguinte:<br />
abstract double int strictfp<br />
boolean else interface super<br />
break extends long switch<br />
byte final native synchronized<br />
case finally new this<br />
catch float package throw<br />
char for private throws<br />
class goto protected transient<br />
const if public try<br />
continue implements return void<br />
default import short volatile<br />
15
SINTAXE DA LINGUAGEM JAVA<br />
do instanceof static while<br />
você não precisa se preocupar em memorizar o nome das palavras reservadas em <strong>Java</strong>. À medida<br />
que você for praticando a programação em <strong>Java</strong>, isso se tornará natural. Além disso,o compilador acusa<br />
um erro de nomenclatura quando você tenta utilizar uma palavra reservada para nomear uma variável,<br />
um método ou uma classe.<br />
Declarando variáveis em <strong>Java</strong><br />
Uma variável é sempre declarada seguindo do seguinte esquema:<br />
+ + identificador + ;<br />
ou<br />
+ + identificador + = + valor + ;<br />
onde:<br />
é um tipo primitivo de dados ou o nome de uma classe ou interface<br />
identificador é o nome da variável<br />
valor é o valor atribuído à variável. Caso você declare uma variável e não atribua nenhum<br />
valor, ela não poderá ser utilizada em um código <strong>Java</strong> – a tentativa de utilizar uma<br />
variável não inicializada em <strong>Java</strong> gerará um erro de compilação.<br />
exemplos:<br />
/**<br />
* FIC - Faculdade Integrada do Ceará<br />
* Sistemas Orientados a Objetos I<br />
* Incluindo variáveis no programa AloMundo.<br />
*/<br />
public class AloMundo<br />
{<br />
static public void main(String[] args)<br />
{<br />
boolean obrigatorio;<br />
int semestre = 2;<br />
String mensagem = "Alo mundo.";<br />
}<br />
}<br />
System.out.println(mensagem);<br />
veremos mais adiante que <strong>Java</strong> possui um mecanismo de inicialização de variáveis de seus tipos<br />
primitivos, mas o aluno deve evitar considerar essa inicialização como prática de programação. De fato,<br />
esta inicialização automática não funciona para variáveis de tipos agregados ou abstratos de dados e<br />
também que o escopo das variáveis – de classe ou de instância - tem influência na sua inicialização. O<br />
aluno é fortemente recomendado a pensar em variáveis como espaços alocados na memória RAM,<br />
inicialmente podendo conter qualquer valor (conhecido como lixo na memória).<br />
16
SINTAXE DA LINGUAGEM JAVA<br />
Tipos primitivos da linguagem <strong>Java</strong><br />
A linguagem <strong>Java</strong> utiliza oito tipos primitivos de dados e um tipo especial. Esses tipos primitivos podem<br />
ser utilizados para declarar constantes ou variáveis utilizadas em programas <strong>Java</strong>. Os tipos primitivos<br />
estão divididos em quatro categorias: lógicos, textuais, numéricos inteiros e numéricos de ponto flutuante.<br />
Tipos lógicos: boolean<br />
Valores lógicos possuem dois estados, normalmente ditos verdadeiro/falso, sim/não e ligado/ desligado.<br />
Em <strong>Java</strong> um tipo lógico é definido pela palavra boolean, e pode assumir dois valores: true ou false.<br />
// Exemplo de variável que suporta valores booleanos<br />
boolean anoBissexto = false;<br />
boolean anoPar = true;<br />
// Apesar de uma variável poder ser declarada<br />
// sem receber um valor, ela só poderá ser usada<br />
// após a atribuição de algum valor a ela.<br />
boolean valido;<br />
Tipos textuais: char e String<br />
Caracteres simples são representados pelo tipo char. Um char representa um caracter UNICODE, ou<br />
seja, um número inteiro sem sinal de 16 bits, no intervalo de 0 até 2 16 -1. O valor de um literal char deve<br />
ser delimitado por aspas simples:<br />
// Exemplo de representação de caracteres UNICODE<br />
char primeiraLetra = 'a';<br />
char tabulacao = '\t';<br />
// Código UNICODE para o caractere de interrogação<br />
char unicode = '\u0A02';<br />
// Lembre-se: Uma variável só poderá<br />
// ser manipulada após receber um valor.<br />
char inutil; // variável sem utilidade neste momento<br />
inutil = '@'; // variável útil a partir de agora<br />
Palavras são representadas por uma seqüência de dados do tipo char, agrupadas em um tipo especial<br />
de dados: a classe String. Apesar de ser uma classe, uma variável do tipo String suporta operações<br />
como se fosse um tipo primitivo de dados. O valor de uma variável String deve ser delimitado por aspas<br />
duplas "valor".<br />
// Exemplo de uso de variáveis do tipo String<br />
String disciplina = "Sistemas Orientados a Objetos I";<br />
// Uma variável pode receber o valor de outra<br />
17
SINTAXE DA LINGUAGEM JAVA<br />
String outraVariavel = disciplina;<br />
// A concatenação de Strings pode ser feita através do<br />
// operador de soma (+)<br />
disciplina = "Sistemas " + "Orientados a Objetos I";<br />
// Concatenação de String com outro tipo de dados:<br />
disciplina = "Sistemas Orientados a Objetos" + 'I';<br />
disciplina = "Sistemas Orientados a Objetos" + 1;<br />
// Para comparar duas variáveis do tipo String<br />
// devemos usar o método equals():<br />
// disciplina == "Sistemas orientados..." INCORRETO<br />
// disciplina.equals("Sistemas orientados...") CORRETO<br />
a concatenação de qualquer tipo de dado com um dado do tipo String resulta em um novo dado do<br />
tipo String.<br />
Tipos numéricos inteiros: byte, short, int e long<br />
Existem quatro tipos primitivos de números em <strong>Java</strong>. Além disso, os valores numéricos podem ser<br />
representados de forma decimal, octal ou hexadecimal:<br />
Valores numéricos inteiros em <strong>Java</strong>:<br />
2 decimal<br />
077 um número que começa com zero está representado de forma octal<br />
0xBABE representação hexadecimal<br />
todos os valores numéricos em <strong>Java</strong> tem sinal positivo ou negativo.<br />
Um valor numérico é sempre considerado do tipo int, a menos que seja acompanhado do sufixo L, que<br />
representa um valor do tipo long. A diferença de um inteiro para um longo é a capacidade de dígitos que<br />
podem ser representados, conforme aparece no quadro abaixo.<br />
Valores numéricos em <strong>Java</strong>, representados como long:<br />
2L decimal<br />
077L um número que começa com zero está representado de forma octal<br />
0xBABEL representação hexadecimal<br />
// Valores inteiros representáveis pelos tipos<br />
// numéricos em <strong>Java</strong>:<br />
byte a = 127; // -2 7 ... 2 7 -1<br />
short b = 32767; // -2 15 ... 2 15 -1<br />
int c = 2147483647; // -2 31 ... 2 31 -1<br />
long d = 9223372036854775807L; // -2 63 ... 2 63 –1<br />
18
SINTAXE DA LINGUAGEM JAVA<br />
Tipos numéricos de ponto flutuante: float e double<br />
Um valor fracionário pode ser representado em <strong>Java</strong> através dos tipos float e doublé. A diferença entre<br />
esses dois tipos é o tamanho da mantissa:<br />
float 32 bits<br />
double 64 bits<br />
Para um número ser considerado do tipo ponto flutuante, é necessário a inclusão de um ponto, do<br />
caractere E (de expoente) ou do sufixo D ou F, conforme mostra o quadro abaixo:<br />
// Representação de valores numéricos de ponto flutuante<br />
float pi = 3.141516;<br />
float taxa = 6.02E23;<br />
double valor= 123.4E+306D;<br />
todo o valor numérico de ponto flutuante é considerado do tipo double, a menos que o programador o<br />
declare explicitamente como float.<br />
Convenções de codificação<br />
Durante a nossa disciplina, adotaremos a seguinte convenção de codificação:<br />
?? Classes – as classes devem ser designadas por nomes, começando por uma letra maiúscula e<br />
depois minúsculas. Cada nova palavra que formar o nome da classe deve ser capitalizada. Ex:<br />
class Calculadora, class CalculadoraCientifica, ...<br />
?? Intefaces – igual às classes. Ex: interface Calculo, interface EquacaoLogaritmica, ...<br />
?? Métodos – métodos devem nomeados por verbos, seguindo o mesmo formato de capitalização<br />
das classes. Entretanto, um método sempre deve começar com letra minúscula. Ex: public void<br />
calcular(int numero), public void extrairRaiz(int numero), ...<br />
?? Constantes – constantes devem ter todas as suas letras em maiúsculo, com o símbolo de<br />
subscrito para separa as palavras. Ex: final int ANO = 2002, final boolean VERDADE = true, ...<br />
?? Variáveis – tal qual os métodos, as variáveis devem começar com uma letra minúscula e depois<br />
alternar a cada palavra. Procure usar nomes significativos para variáveis. Evite declarar variáveis<br />
usando apenas um a letra.<br />
Exercícios<br />
d. Altere o programa AloMundo.<strong>Java</strong> para que ele imprima na tela todos os tipos primitivos de<br />
dados suportados pela linguagem <strong>Java</strong>.<br />
e. Altere o programa AloMundo para que ele imprima os parâmetros recebidos pela linha de<br />
comando de execução do programa. (Dica: são os valores do array args: args[0], args[1], etc.)<br />
19
SINTAXE DA LINGUAGEM JAVA<br />
f. Crie um programa que receba três argumentos da linha de comando e imprima-os na mesma<br />
linha, em ordem inversa.<br />
Exemplo: java Programa a1 a2 a3<br />
Saída: a3 a2 a1<br />
20
Expressões<br />
Esta seção apresenta a manipulação de variáveis e implementação de tomada de<br />
decisões em programas <strong>Java</strong>.<br />
Operadores lógicos e aritméticos<br />
Os operadores <strong>Java</strong> são similares em estilo e função aos operadores da<br />
linguagem C/C++. A tabela abaixo enumera esses operadores em ordem de<br />
precedência:<br />
Delimitadores . [] () ; , Servem para delimitar<br />
partes distintas de um<br />
comando, método ou<br />
classe.<br />
Ordem<br />
de<br />
leitur<br />
a:<br />
Operador<br />
:<br />
Função:<br />
4<br />
Unário ++ Incrementa o valor da variável em uma unidade.<br />
Exemplo: i++; contador++;<br />
Unário -- Diminui o valor da variável em uma unidade. Exemplo:<br />
i--; contador--;<br />
+ - Operadores aritméticos<br />
* / % Multiplicação, divisão, resto<br />
>><br />
>> Operadores de deslocamento aritmético e lógico<br />
== != Igualdade e desigualdade<br />
^ Potência<br />
&& AND<br />
|| OR<br />
?: Operador condicional. Exemplo: i=0; (i>2?i=0:i--);<br />
= *= %= Operadores aplicados sobre a atribuição<br />
+= -=<br />
<br />
=<br />
>>>= &=<br />
^= |=<br />
instance<br />
of<br />
Identificador de classes
EXPRESSÕES<br />
Concatenação de Strings com o operador +<br />
Quando o operador + é aplicado a dados do tipo String, ele cria um novo dado<br />
do tipo String, concatenando os dois operandos:<br />
/**<br />
* Concatenação de Strings<br />
*/<br />
String sigla = "SOO-I";<br />
String nome = "Sistemas Orientados a Objetos I";<br />
String titulo = sigla + " - " + nome;<br />
// Esse comando imprimirá na tela a frase:<br />
// SOO-I – Sistemas Orientados a Objetos I<br />
System.out.prinln(titulo);<br />
int i = 10;<br />
String legenda = "valor = ";<br />
// campo é uma variável do tipo String<br />
String campo = legenda + i;<br />
Alguns métodos úteis em dados do tipo String:<br />
/**<br />
* Strin: métodos úteis<br />
*/<br />
String disciplina = "Sistemas Orientados a Objetos I";<br />
System.out.println("disciplina: " + disciplina);<br />
// Isolando um caractere:<br />
System.out.print("primeiro caractere: ");<br />
System.out.println(disciplina.charAt(0));<br />
System.out.print("segundo caractere: ");<br />
System.out.println(disciplina.charAt(1));<br />
// O primeiro caractere de uma String tem o<br />
// índice 0, o segundo o índice 1 e assim por diante<br />
// letra = 's';<br />
char letra = disciplina.charAt(2);<br />
// substrings:<br />
System.out.print("primeiras cinco letras: ");<br />
System.out.println(disciplina.substring(0, 5));<br />
System.out.print("letras a partir da quarta: ");<br />
System.out.println(disciplina.substring(4));<br />
22
EXPRESSÕES<br />
// número de caracteres em uma String:<br />
System.out.print("tamanho da frase: ");<br />
System.out.println(disciplina.lenght() + " letras");<br />
// usando os caracteres de tabulação e quebra<br />
// de linha:<br />
System.out.println(""<br />
+ disciplina.lenght()<br />
+ " letras"<br />
+ " \n"<br />
+ " Nova linha\ttabulação"<br />
);<br />
Promoção e Casting<br />
A linguagem <strong>Java</strong> não suporta atribuições arbitrárias entre variáveis de tipos<br />
diferentes. Por exemplo, você não pode inicializar uma variável inteira com um<br />
valor de ponto flutuante sem explicitar isso através de um processo que<br />
chamamos de casting.<br />
Quando atribuímos um valor a uma variável, e esse valor é incompatível com o<br />
tipo de dado definido para a variável, ocorrerá uma conversão. Em alguns<br />
casos, essa conversão será automática, em outros o programador deve indicar de<br />
que forma o valor será convertido ao tipo de dado da variável.<br />
Quando o processo de conversão for automático, dizemos que ocorreu uma<br />
promoção, ou seja, um valor com um tipo de dado foi promovido a outro tipo de<br />
dado. Veja no exemplo abaixo:<br />
//<br />
// Promoção entre valores de tipos de dados distintos<br />
// Apesar 6 ser um inteiro, o valor da variável grande<br />
// continua sendo do tipo long<br />
long grande = 6;<br />
// Uma variável do tipo inteiro não possui<br />
// espaço para armazenar um valor longo.<br />
// A instrução abaixo é ilegal, e causará um erro de compilação.<br />
int pequeno = 99L;<br />
float a = 12.121F; // correto<br />
float b = 12.121; // 12.121 é um double - incorreto<br />
Como visto acima, algumas conversões não podem ser realizadas de forma<br />
automática, pois o compilador não pode assumir que tipo de conversão ele deve<br />
realizar (o tamanho do tipo de dado a ser recebido por uma variável é maior<br />
23
EXPRESSÕES<br />
que o tamanho pré-definido para o tipo dessa variável, logo o compilador não<br />
sabe como "ajustar" os bits excedentes). Nesse caso, o programador deve<br />
indicar ao compilador que tipo de conversão deverá ocorrer, digitando o tipo<br />
de dado que o valor deverá assumir entre parênteses:<br />
//<br />
// Casting entre valores de tipos de dados distintos<br />
//<br />
// Apesar 6 ser um inteiro, o valor da variável grande<br />
// continua sendo do tipo long<br />
long grande = 6;<br />
int pequeno = (int)99L; // sem problemas<br />
float a = 12.121F;<br />
float b = (float)a; // sem problemas<br />
Operadores de deslocamento (>>, >)<br />
<strong>Java</strong> provê operadores para a manipulação dos bits em variáveis de tipo numérico: o deslocamento<br />
aritmético >> e o deslocamento lógico >>>.<br />
O operador de deslocamento aritmético >> executa um deslocamento de um bit para a direita de um<br />
número (na prática, o primeiro argumento desse operador é dividido por dois 'n' vezes – onde n é o<br />
segundo argumento do operador):<br />
8 >> 2 = 2<br />
128 >> 1 = 64<br />
256 >> 4 = 16<br />
* Notação em complemento de dois: o operador >> mantém o sinal do bit mais significativo durante o<br />
deslocamento.<br />
O operador de deslocamento lógico >>> executa um deslocamento no padrão dos bits ao invés do<br />
significado aritmético de um valor numérico. Esse operador sempre adiciona o valor 0 ao bit mais<br />
significativo:<br />
1010 ... >> 2 = 111010 ...<br />
1010 ... >>> 2 = 001010 ...<br />
* Os operadores de deslocamento reduzem seus operandos à direita módulo 32 para um valor do tipo int<br />
e módulo 64 para um tipo long. Dessa forma, para qualquer valor do tipo int:<br />
int x x >>> 32 = x<br />
o operador de deslocamento lógico >>> só pode ser aplicado a valores inteiros, e não é efetivo em<br />
valores int e long. Se for aplicado a valor short ou byte, o valor será promovido a um int antes da<br />
aplicação do operador. Por isso, um deslocamento sem sinal acaba se tornando um deslocamento com<br />
sinal.<br />
24
EXPRESSÕES<br />
Circuitos lógicos<br />
<strong>Java</strong> possui três operadores básicos para implementar circuitos lógicos :<br />
NOT: operador !<br />
AND: operador &&<br />
OR: operador ||<br />
Esses operadores permitem a representação de expressões booleanas, que formam<br />
o argumento para comandos de decisão (IF), seguindo a seguinte tabela:<br />
AND:<br />
true && true = true;<br />
true && false = false;<br />
false && true = false;<br />
false && false = false;<br />
!true = false;<br />
!false = true;<br />
!(a && b) = !a || !b<br />
!(a || b) = !a && !b<br />
25<br />
OR:<br />
true || true = true;<br />
true || false = true;<br />
false || true = true;<br />
false || false = false;<br />
Os comandos de controle (if, while, switch) utilizam o valor de expressões<br />
booleanas para guiar o fluxo de controle de um programa, como no exemplo<br />
abaixo:<br />
/**<br />
* Comandos de decisão utilizando expressões booleanas<br />
*/<br />
int mes = 2;<br />
if((mes == 12) || (mes == 1))<br />
{<br />
System.out.println("férias :)" );<br />
}<br />
if((mes > 1) && (mes < 12))<br />
{<br />
System.out.println("semestre em andamento");<br />
}<br />
if((mes != 2))<br />
{<br />
System.out.println("não tem carnaval ");<br />
}<br />
a<br />
b<br />
OR<br />
a || b<br />
a<br />
NOT<br />
!a<br />
a<br />
b<br />
AND<br />
a && b
EXPRESSÕES<br />
Exercícios<br />
1. Implemente um programa para calcular a área de um trapézio, onde:<br />
h = altura<br />
b = base menor<br />
B = base maior<br />
Área = (h . (b + B)) / 2<br />
2. Faça o programa acima calcular utilizando valores de ponto flutuante e depois imprima na tela<br />
duas informações:<br />
Valor exato da área:<br />
Valor arredondado para inteiro:<br />
3. Calcule o valor das seguintes equações:<br />
a. 3 – 2 – 1 + 2 + 1 + 3<br />
b. 2 * 3 – 4 * 5<br />
c. 2 + 6 – 3 / 7 * 9<br />
d. 3 % 4 – 8<br />
4. Indique qual o valor verdade das seguintes expressões:<br />
a. (1 > 2) // exemplo: false<br />
b. (8 == 8) // exemplo: true<br />
c. ((12 – 5) > 6)<br />
d. (0 < 3) && (8 < 9)<br />
e. ((i++) > i)<br />
f. ((10 * 90 / 50 – 2) == 16)<br />
26
Fluxo de controle<br />
Esta seção demonstra como implementar tomada de decisões em códigos <strong>Java</strong>.<br />
Ramificação if, else<br />
A sintaxe básica para declarações if, else é a seguinte:<br />
exemplo:<br />
if (expressão_booleana)<br />
{<br />
// bloco de comandos<br />
}<br />
else<br />
{<br />
// bloco alternativo de comandos<br />
}<br />
/**<br />
* Usando if, else<br />
*/<br />
int maior = 10;<br />
int menor = 5;<br />
if (maior > menor)<br />
{<br />
// (10 > 5) = true<br />
System.out.println(maior + ">" + menor);<br />
}<br />
else<br />
{<br />
// (10 > 5) != true<br />
System.out.println(menor + ">" + maior);<br />
}<br />
// Lembre-se que o controle de fluxo é feito através<br />
// do valor verdade de uma expressão booleana<br />
boolean verdade = (10 > 5);<br />
if (verdade)<br />
{<br />
5
FLUXO DE CONTROLE<br />
}<br />
else<br />
{<br />
}<br />
// (10 > 5) = true<br />
System.out.println(maior + ">" + menor);<br />
// (10 > 5) != true<br />
System.out.println(menor + ">" + maior);<br />
Ramificação switch<br />
Switch é uma declaração semelhante ao if, mas que usa valores inteiros para a<br />
tomada de decisões ao invés de expressões booleanas. (só pode ser usada em<br />
dados dos tipos short, int, byte ou char). Se o tipo de dado não for inteiro,<br />
o comando switch irá executar uma promoção desse valor ao tipo int para<br />
somente depois executar a ramificação.<br />
A sintaxe básica para declarações switch é a seguinte:<br />
exemplo:<br />
Switch ((int)expressão)<br />
{<br />
// bloco de comandos<br />
case ((int) valor_1):<br />
// bloco de comandos<br />
break;<br />
case ((int) valor_2):<br />
// bloco de comandos<br />
break;<br />
default :<br />
// bloco de comandos padrão.<br />
// Se nenhum dos valores acima corresponder à<br />
// expressão definida no comando switch, então<br />
// o programa executará o trecho default.<br />
// o trecho default é opcional.<br />
break;<br />
}<br />
/**<br />
* Usando switch<br />
*/<br />
// Considere valorDoTeclado() como um número<br />
// inteiro digitado pelo usuário<br />
int valor = valorDoTeclado();<br />
switch (valor)<br />
{<br />
case 0:<br />
System.out.println("cadastro de produto");<br />
28
FLUXO DE CONTROLE<br />
}<br />
Repetição for<br />
break;<br />
case 1:<br />
System.out.println("emitir nota fiscal");<br />
break;<br />
case 2:<br />
System.out.println("cancelar compra");<br />
break;<br />
default:<br />
System.out.println("efetuar venda");<br />
break;<br />
A declaração for é utilizada para definir que um bloco de comandos deve ser executado 'n' vezes, onde 'n'<br />
é um número inteiro. A sintaxe do comando for é a seguinte:<br />
exemplo:<br />
for (int i = 0; i < 10; i++)<br />
{<br />
// bloco de comandos<br />
}<br />
/**<br />
* repetição de comandos usando FOR<br />
*/<br />
// Calculando o fatorial de um número:<br />
int numero = 10;<br />
int fatorial = 1;<br />
for (int i = numero; i > 0; i--)<br />
{<br />
fatorial = fatorial * i;<br />
}<br />
System.out.println("fatorial de " + valor<br />
+ " = " + fatorial);<br />
// Imprimindo os dias do ano:<br />
for (int mes = 1; mes < 12; mes++)<br />
{<br />
switch (mes)<br />
{<br />
case 1:<br />
System.out.println("janeiro");<br />
break;<br />
case 2:<br />
System.out.println("março");<br />
break;<br />
case 3:<br />
29
FLUXO DE CONTROLE<br />
}<br />
Repetição while<br />
}<br />
System.out.println("abril");<br />
break;<br />
// escreva aqui os demais comandos<br />
A declaração while é utilizada para definir que um bloco de comandos deve ser executado enquanto uma<br />
expressão booleana (condição de parada) não seja verdade.. A sintaxe do comando while é a seguinte:<br />
exemplo:<br />
While (expressão_booleana)<br />
{<br />
// bloco de comandos executados enquanto a<br />
// expressão boolena tiver valor verdade = true<br />
}<br />
ou<br />
do<br />
{<br />
// bloco de comandos executados pelo menos uma vez<br />
} While (expressão_booleana);<br />
/**<br />
* repetição de comandos usando FOR<br />
*/<br />
// Calculando o fatorial de um número:<br />
int numero = 10;<br />
int fatorial = 1;<br />
int i = numero;<br />
while (i > 0)<br />
{<br />
fatorial = fatorial * i;<br />
i--;<br />
}<br />
System.out.println("fatorial de " + valor<br />
+ " = " + fatorial);<br />
// Lendo a condição de parada do teclado:<br />
int numero = 0;<br />
while (número < 10)<br />
{<br />
30
FLUXO DE CONTROLE<br />
}<br />
numero = valorDoTeclado();<br />
System.out.println("usuário digitou um número maior<br />
que 10");<br />
Comandos especiais de controle de fluxo: break, continue e<br />
label<br />
Os comandos de repetição em <strong>Java</strong> suportam dois tipos de desvios: o break e o continue. O break faz<br />
com que um laço seja interrompido, enquanto o continue é usado para "pular" uma execução e continuar<br />
a partir da próxima.<br />
Exemplo:<br />
// pulando a execução No 10 e parando na No 15<br />
for (int i=0; i
FLUXO DE CONTROLE<br />
}<br />
// quando o valor de i for igual a 10<br />
break desvio;<br />
}<br />
System.out.println("contador: " + i);<br />
Lendo valores do teclado<br />
Mais tarde você aprenderá o conceitos de Streams e o acesso a dados lidos de arquivos, teclado, etc.<br />
Por enquanto basta que você consiga ler valores do teclado para facilitar a implementação dos exercícios<br />
propostos. A leitura do teclado será brevemente explicada em aula e, no decorrer da disciplina,<br />
revisaremos em detalhes a leitura de dados em dispositivos de entrada e saída.<br />
/**<br />
* FIC - Faculdade Integrada do Ceará<br />
* Sistemas Orientados a Objetos I<br />
* Lendo valores do teclado<br />
*/<br />
// Tem que importar a biblioteca de acesso aos<br />
// dispositivos de Entrada e Saída (I/O) do <strong>Java</strong>:<br />
import java.io.*;<br />
public class Teclado<br />
{<br />
static public void main(String[] args)<br />
{<br />
// Tem que usar tratamento de exceções,<br />
// conforme explicado em aula.<br />
try<br />
{<br />
// Essas duas linhas criam um "leitor com buffer"<br />
// do dispositivo padrão de entrada do <strong>Java</strong>:<br />
// o teclado (System.in). Mais tarde você aprenderá que esse<br />
// leitor pode ser redirecionado para ler informações<br />
// de outros dispositivos, como uma conexão com a Internet,<br />
// um Socket, o mouse, etc.<br />
InputStreamReader dados = new InputStreamReader(System.in);<br />
BufferedReader teclado = new BufferedReader(dados);<br />
System.out.print("digite uma frase: ");<br />
String frase = teclado.readLine();<br />
System.out.println("Frase digitada:\t" + frase);<br />
System.out.print("\ndigite um numero inteiro: ");<br />
int numero = Integer.parseInt(teclado.readLine());<br />
System.out.println("Número digitado vezes dois =\t"<br />
+ (numero * 2));<br />
System.out.print("\ndigite um numero fracionario: ");<br />
double flutuante = Double.parseDouble(teclado.readLine());<br />
32
FLUXO DE CONTROLE<br />
}<br />
}<br />
System.out.println("Número digitado dividido por dois =\t"<br />
+ (flutuante / 2));<br />
// outros métodos:<br />
// char letra = (char)teclado.read();<br />
// Float.parseFloat(teclado.readLine());<br />
// Long.parseLong(teclado.readLine());<br />
}<br />
catch(Exception error)<br />
{<br />
// Se houver um erro na leitura do teclado,<br />
// a execução do programa será desviada para<br />
// o bloco 'catch'<br />
System.out.println("[ERRO] – "<br />
+ "voce digitou um valor invalido");<br />
}<br />
Exercícios<br />
1. Escreva um programa que imprima na tela a soma dos números ímpares entre 0 e 30 e a<br />
multiplicação dos números pares entre 0 e 30.<br />
2. Faça um programa para imprimir os números primos de 1 a 123.<br />
3. Faça um programa para ler um número do teclado e imprimir na tela se ele é par ou ímpar.<br />
Imprima também se ele é primo.<br />
4. O valor pago por um Hotel da Praia de Iracema para seus porteiros é de R$ 10,25 por hora de<br />
trabalho. Faça um programa que pergunte ao usuário quantas horas ele trabalhou e imprima na<br />
tela o valor do salário a ser recebido por ele.<br />
5. Modifique o programa anterior para que o sistema imprima uma mensagem de alerta quando o<br />
valor a ser pago ao funcionário seja inferior a R$ 50,00: "Atenção, dirija-se à direção do Hotel!".<br />
6. Existem 454 gramas em uma libra, e 1000 gramas em um quilo. Faça um programa que converta<br />
quilos para libras e vice-versa. (Dica: use um caractere indicando a ordem da conversão,<br />
exemplo "java q 1000" seria o comando para converter 1000 quilos para libra, e "java l 1000"<br />
seria o comando para converter 1000 libras para quilo)<br />
33
Agrupamento de dados (Arrays)<br />
Uma dos fatores de qualidade de software é o uso correto de estruturas de dados. Nesta seção<br />
analisaremos o agrupamento de dados, uma estrutura simples mas essencial para a compreensão de como<br />
<strong>Java</strong> manipula dados na memória.<br />
Declarando e criando Arrays<br />
Arrays são tipos indexados de dados que permitem a representação de agrupamento de dados como<br />
vetores e matrizes.<br />
Podemos declarar arrays de qualquer tipo de dado suportado em <strong>Java</strong> (primitivo ou agregado – tipos<br />
agregados de dados serão discutidos posteriormente). Essa declaração é feita pela adição do símbolo []<br />
a um tipo de dado, que pode aparecer antes ou após o identificador da variável:<br />
char[] arrayDeCaracteres;<br />
String[] arrayDePalavras;<br />
Ponto[] arrayDeObjetosDoTipoPonto;<br />
int números[];<br />
Na memória, um array é um agrupamento de dados, indexados pelo tamanho do tipo de dado que o array<br />
suporta:<br />
... ...<br />
0 1 2 3 4 5 ... N-1<br />
O primeiro índice de um array é sempre 0, e o último índice é o N-1, onde N é o número de<br />
elementos do array.<br />
apesar da posição do símbolo [] poder ser no início ou no final do nome da variável, é fortemente<br />
recomendado que você sempre aplique o [] após o tipo da variável. Isso torna o código muito mais<br />
legível.<br />
Para criar um array, usamos a palavra chave new:<br />
int[] números = new int[50];<br />
A quantidade de elementos de um array sempre deve ser um valor inteiro. O comprimento de um array é<br />
dado pelo método length:<br />
// Imprimindo o comprimento de um array<br />
6
AGRUPAMENTO DE DADOS (ARRAYS)<br />
char [] alfabeto = new char[24];<br />
int tamanhoDoAlfabeto = alfabeto.length;<br />
System.out.println("alfabeto com " + tamanhoDoAlfabeto<br />
+ " letras");<br />
// Muito importante: um array de 10 elementos tem os<br />
// índices de 0 a 9<br />
Inicializando os valores de um Array<br />
A linha de comando acima cria uma referência para um vetor com 50 valores do tipo inteiro. No exemplo<br />
acima, o array de inteiros é inicializado automaticamente pelo JVM com o valor ZERO em todas as<br />
posições do array. Isso acontece porque o int é um tipo primitivo de dados em <strong>Java</strong>. Em outros tipos de<br />
dados (agregados), um array não será inicializado, ou seja, a declaração apenas criará uma referência a<br />
uma área de memória que não conterá valor algum.<br />
Por exemplo, olhe o seguinte fragmento de código:<br />
// Essas linhas causarão um erro de execução, pois a posição 0 do array<br />
// contendo as frases não possui nenhum valor (null) e, portanto,<br />
// não pode ser usada como parâmetro para nenhum tipo de operação.<br />
String[] frases = new String[10];<br />
int comprimentoDaPrimeiraFrase = frases[0].length();<br />
Exception in thread "main" java.lang.NullPointerException<br />
Para que possamos usar os dados contidos em um array, precisamos antes inicializá-los, conforme os<br />
exemplos abaixo:<br />
// Inicializando arrays<br />
String[] frases = new String[5];<br />
frases[0] = "primeira frase";<br />
frases[1] = frases[0];<br />
frases[2] = frases[0] + frases[1];<br />
frases[3] = "outro texto qualquer";<br />
frases[4] = "último índice do vetor";<br />
// inicialização no momento da declaração de um array:<br />
String[] dias = {"segunda", "terça", "quarta"<br />
, "quinta", "sexta", "sábado", "domingo"};<br />
int[] meses = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};<br />
Ponto[] cordenadas = {new Ponto(5, 3), new Ponto(0, 2)};<br />
35
AGRUPAMENTO DE DADOS (ARRAYS)<br />
arrays de tipos primitivos de dados são inicializados no momento de sua criação. Arrays de tipos<br />
agregados de dados necessitam de inicialização explícita.<br />
Array multi-dimensional<br />
Quando declaramos um array através do símbolo [], estamos de fato criando um array unidimensional<br />
(vetor). Além de vetores, a linguagem <strong>Java</strong> permite a declaração de arrays n-dimensionais, conforme os<br />
exemplos abaixo:<br />
// array unidimensional (VETOR)<br />
String[] frases = new String[5];<br />
// array bidimensional (MATRIZ)<br />
String[][] tabela = new String[5][15];<br />
// array n-dimensional<br />
double [][][] densidade;<br />
densidade = new double[10][10][10];<br />
// observe que nem todos os valores de um array<br />
// precisam estar preenchidos. Sempre que você for<br />
// usar o valor contido em uma posição de um array<br />
// você deve ter certeza que essa posição<br />
// foi inicializada<br />
densidade[0][0][0] = 35.034;<br />
densidade[1][0][0] = 30.876;<br />
System.out.println("densidade na coordenada 1,0,0 = "<br />
+ densidade[1][0][0]);<br />
// arrays com mais de três dimensões são raramente<br />
// encontrados em programas de computador, mas<br />
// são permitidos em <strong>Java</strong><br />
float[][][][][][] espacoHexaDimensional;<br />
int[][][][] quartaDimensao = new int[564][2][200][1];<br />
Ponto[][][][][][][][][][][][] hiperespaco;<br />
Array esparso<br />
<strong>Java</strong> permite a declaração de estruturas esparsas, ou seja, um agrupamento indexado de dados onde<br />
nem todas as dimensões são iguais:<br />
// Matrizes esparsas podem ser representadas em <strong>Java</strong><br />
// através de arrays com dimensões heterogêneas:<br />
int[][] esparso = new int[4][];<br />
esparso[0] = new int[10];<br />
esparso[1] = new int[2];<br />
36
AGRUPAMENTO DE DADOS (ARRAYS)<br />
esparso[2] = new int[5];<br />
esparso[3] = new int[1];<br />
// Na memória, o espaço de dados ocupado pelo<br />
// array acima seria aproximadametne assim:<br />
// [ | | | | | | | | | ]<br />
// [ | ]<br />
// [ | | | | ]<br />
// [ ]<br />
Exercícios<br />
1. Faça um programa que enumere os parâmetros recebidos via linha de comando.<br />
2. Faça um programa que leia cinco nomes do teclado, e depois imprima esses nomes na ordem<br />
inversa em que foram digitados pelo usuário.<br />
3. Altere o programa acima para que ele continue a ler nomes do teclado até que o usuário digite a<br />
palavra "fim", e então imprima os nomes digitados na ordem em que foram digitados.<br />
4. Implemente um jogo-da-velha, com as seguintes características:<br />
g. .<br />
a. O tabuleiro é formado por um array bidimensional, onde as posições são numeradas<br />
conforme abaixo:<br />
0 1 2<br />
3 4 5<br />
6 7 8<br />
b. Para jogar, os jogadores devem digitar o número correspondente ao quadro que desejam<br />
jogar. Caso o quadro já esteja ocupado, ou o valor digitado pelo usuário não seja um<br />
número de 0 a 8, o programa deve acusar a jogada inválida e pedir que o jogador digite<br />
novamente.<br />
c. O programa deve considerar dois jogadores (que podem ser nomeados jogadorX e<br />
jogadorO), e utilizar dois símbolos distintos para representar a cada um deles.<br />
d. O programa deve perguntar alternadamente a próxima jogada, ou seja, primeiro pergunta<br />
a jogada do jogadorX, depois do jogadorO e assim sucessivamente.<br />
e. A cada nova jogada o programa deve mostrar o tabuleiro com a situação atual do jogo<br />
(seja criativo). Por exemplo:<br />
O - -<br />
X - X<br />
- - -<br />
Ou<br />
37<br />
[O] [ ] []<br />
[X] [X] []<br />
[] [ ] []<br />
Dica: você pode usar os caracteres \t para gerar tabulações tela de impressão, e o \n<br />
para gerar linhas em branco entre os tabuleiros.<br />
f. Se um dos jogadores ganhar, o sistema deve mostrar uma mensagem de vitória. Se<br />
houver empate, o programa deve emitir a respectiva mensagem.
Introdução à Orientação a Objetos<br />
Esta seção visa estimular o aluno a pensar no conceito de classificação de objetos,<br />
atividade corriqueira do ser humano e que permite grande produtividade quando<br />
adotada como paradigma de programação. A migração da codificação estruturada para<br />
a orientada a objetos requer disciplina e paciência, mas capacita o aluno a compreender<br />
técnicas avançadas de análise e desenvolvimento de software.<br />
Motivação ao uso de um novo paradigma, orientado a objetos.<br />
Programadores iniciantes costumam visualizar variáveis como elementos isolados em um sistema de<br />
computador. Por exemplo, digamos que um programa deva representar a carteira de estudante dos<br />
alunos da FIC, onde essa identificação seja composta pelo nome e pelo número de matrícula.<br />
Na forma estruturada de programação, o aluno poderia usar o seguinte conjunto de variáveis:<br />
// Variáveis que armazenam as informações sobre o RG:<br />
String nome = "Fernando Henrique Gomes";<br />
int matricula = 78623423;<br />
Apesar de simples, a forma de representação de dados acima permite dois grandes problemas:<br />
Se quisermos representar informações sobre um número maior de alunos, teremos que declarar muitas<br />
outras variáveis:<br />
// Variáveis que armazenam as informações sobre o RG:<br />
String nome0 = "Fernando Henrique Gomes";<br />
int matricula0 = 906231230;<br />
String nome1 = "Ciro Cardoso";<br />
int matricula1 = 903526345;<br />
String nome2 = "Luis Inácio Jereissat";<br />
int matricula2 = 003526388;<br />
7<br />
7
INTRODUÇÃO À ORIENTA ÇÃO A OBJET OS<br />
Imagine o exemplo de código acima necessário para manipular a informação de todos os alunos da FIC,<br />
cerca de oito mil alunos. Imagine a dificuldade de lidar com tantas variáveis ? Além disso, o número<br />
máximo de alunos suportados pelo sistema deveria ser previamente conhecido.<br />
Ainda usando programação estruturada, uma forma um pouco melhor de representação dos dados seria<br />
o uso de informações indexadas em arrays:<br />
String[] nome = new String[5000];<br />
int[] matricula = new int[5000];<br />
// Variáveis que armazenam as informações sobre o RG:<br />
nome[0] = "Fernando Henrique Gomes";<br />
matricula[0] = 903564756;<br />
nome[1] = "Ciro Cardoso";<br />
matricula [1] = 787564234;<br />
// Mesmo assim seriam necessárias variáveis de indexação (confusas):<br />
int fhg = 0;<br />
int cardoso = 1;<br />
// O uso de dados indexados era o paradigma vigente antes da popularização do<br />
// uso de Orientação a Objetos. Linguagens antigas, como Cobol, tem a indexação<br />
// como formato básico de acesso a dados na memória. Cobol usa inclusive o tamanho<br />
// dos dados como o separador dos valores gravados na memória.<br />
Outro problema, o principal, é a representação feita por tipos de dados, ou seja, o nome é uma variável<br />
do tipo String a o número de matrícula é uma variável do tipo inteiro. – não há nada no código que os<br />
vincule a uma carteira de estudante.<br />
Note que a nível conceitual, existe uma associação entre as variáveis 'nome' e 'numero',. Elas são parte<br />
do mesmo "objeto", nesse exemplo, uma carteira de estudante.<br />
No final dos anos setenta, com a popularização dos computadores pessoais e o aumento da presença da<br />
informática em nossa sociedade, pesquisadores começaram a notar a necessidade de representar os<br />
dados de forma mais fiel à forma como eles eram definidos em cada problema. Ou seja, uma carteira de<br />
estudante deveria ter um tipo de dado que representasse a sua estrutura, tal qual ela era conceituada no<br />
mundo real (e não um conjunto de variáveis alocadas na memória). Surgiu a Orientação a Objetos,<br />
paradigma que iremos estudar a partir de agora.<br />
Tipos agregados de dados<br />
A maioria das linguagens de programação suporta o conceito de variáveis tipadas, ou seja, uma variável<br />
pode ser do tipo int, double, char, etc. Embora essas linguagens possuam um bom número de tipos prédefinidos,<br />
seria mais interessante que o programador pudesse definir seus próprios tipos de dados,<br />
como, por exemplo, o tipo "carteira de estudante".<br />
Essa questão é resolvida através da implementação de Tipos Agregados de Dados (em algumas<br />
linguagens, também conhecidos como Tipos Estruturados de Dados ou Registros).<br />
39
INTRODUÇÃO À ORIENTA ÇÃO A OBJET OS<br />
Dados agregados são tipos de dados definidos pelo programador no código-fonte de um sistema.<br />
Uma vez que um tipo de dado agregado tenha sido definido pelo programador, ele pode ser usado<br />
normalmente para declarar variáveis.<br />
Em <strong>Java</strong>, os tipos agregados de dados são definidos através da palavra reservada class:<br />
class CarteiraDeEstudante<br />
{<br />
String nome;<br />
int numero;<br />
}<br />
Note que a palavra class deve ser escrita em minúsculo, pois é uma palavra-reservada da linguagem<br />
<strong>Java</strong>. Já CarteiraDoEstudante é o nome do tipo de dado que está sendo criado, batizado conforme a<br />
preferência do programador. Essa preferência deve ser regida por dois fatores:<br />
O nome de um tipo de dado criado pelo programador deve ser fiel à função desse tipo de dado.<br />
No nosso exemplo, para representar uma carteira de estudante, não faria sentido criar um tipo de<br />
dado chamado: class Salario.<br />
O nome de uma classe deve obedecer ao padrão de codificação especificado na empresa em<br />
que o programador está trabalhando. Aqui na nossa disciplina, estamos utilizando o padrão<br />
sugerido pela SUN, onde o nome de toda a classe deve começar com uma letra maiúscula e ser<br />
alternado a cada palavra que compõe o nome. O padrão de codificação é muito importante para<br />
evitar a confusão de tipos primitivos com tipos definidos pelo programador, entre outros detalhes<br />
que serão discutidos no decorrer da disciplina.<br />
o conceito de classes é bem mais amplo que simplesmente um tipo agregado de dados. As<br />
características de uma classe serão apresentadas em detalhes ao longo do curso. Por hora, basta que o<br />
aluno tenha consciência da possibilidade de definir seus próprios tipos de dados em <strong>Java</strong>.<br />
A partir do momento que um programador definiu um tipo agregado de dados, ele pode declarar variáveis<br />
desse tipo utilizando o nome da classe como o tipo da variável:<br />
// Tipo de dado definido pelo programador<br />
class CarteiraDeEstudante<br />
{<br />
String nome;<br />
int numero;<br />
}<br />
// Variáveis declaradas a partir da classe CarteiraDeEstudante:<br />
CarteiraDeEstudante carteira1;<br />
CarteiraDeEstudante carteira2;<br />
CarteiraDeEstudante[] carteirasDaFic = new CarteiraDeEstudante[8000];<br />
As partes integrantes de um tipo agregado de dados pode ser acessadas através do operador ponto (.),<br />
como no exemplo abaixo:<br />
carteira1.nome = "Fernando Inácio";<br />
carteira1.numero = 02378946;<br />
40
INTRODUÇÃO À ORIENTA ÇÃO A OBJET OS<br />
carteira2.nome = "Luis Gomes";<br />
carteira2.numero = 03648919;<br />
Na terminologia de Orientação a Objetos, chamamos os elementos que compõe uma classe de<br />
membros dessa classe. No nosso exemplo, as variáveis nome e numero são chamadas de membros da<br />
classe CarteiraDeEstudante.<br />
Além disso, quando criamos uma instância de um tipo agregado de dados, chamamos essa instância de<br />
objeto.<br />
Criando objetos<br />
Quando declaramos variáveis de um tipo primitivo de dados (boolean, byte, chort, char, int, long, float ou<br />
double) o interpretador <strong>Java</strong> aloca um espaço de memória para essa variável. Quando declaramos um a<br />
variável de um tipo de dado definido pelo programador (ou da API <strong>Java</strong>), o espaço de memória<br />
necessário para alocar o valor dessa variável não é alocado imediatamente.<br />
De fato, uma variável declarada a partir da definição de uma classe não representa a informação<br />
propriamente dita, mas sim uma referência à informação.<br />
Se você preferir, você pode pensar no termo referência como um ponteiro. Muitas linguagens, como<br />
C/C++, utilizam a terminologia de ponteiros ao invés de referência.<br />
Antes que você possa utilizar uma variável declarada a partir de uma classe, você deve alocar o espaço<br />
de memória necessário para guardar as informações referenciadas por essa variável. Em Orientação a<br />
Objetos dizemos que é necessário criar uma instância de uma classe. Isso é feito em <strong>Java</strong> pela<br />
palavra reservada new, conforme o exemplo abaixo:<br />
// Tipo agregado de dados que modela uma classe<br />
// de objetos conhecidos como carteiras de estudante<br />
class CarteiraDeEstudante<br />
{<br />
String nome;<br />
int numero;<br />
}<br />
// Um programa de testes, que usa uma variável<br />
// do tipo carteira de estudante<br />
public class Teste<br />
{<br />
static public void main(String[] args)<br />
{<br />
// Usa-se a palavra reservada new para criar um objeto de um tipo<br />
// de dado definido pelo programador.<br />
// No nosso exemplo, é criado um objeto da classe CarteiraDeEstudante<br />
CarteiraDeEstudante carteira = new CarteiraDeEstudante();<br />
carteira.nome = "Fernando Gomes";<br />
carteira.numero = 89762347;<br />
41
INTRODUÇÃO À ORIENTA ÇÃO A OBJET OS<br />
}<br />
}<br />
System.out.println(carteira.nome);<br />
No exemplo acima, carteira é um objeto da classe CarteiraDeEstudante.<br />
Note que, a partir do momento em que o objeto é instanciado, seus membros passam a ter um valor real,<br />
que pode ser manipulado com o operador ponto (.).<br />
Alocação de memória durante a criação de objetos<br />
Quando declaramos uma variável do tipo de uma classe, o JVM apenas aloca o espaço necessário para<br />
armazenar o endereço dessa referência. Quando um objeto é criado e atribuído a essa variável, através<br />
do comando new, é que a máquina virtual aloca o espaço necessário para armazenar os valores dos<br />
membros dos objetos.<br />
Imagine o nosso exemplo das carteiras de estudante. A declaração da variável carteira gera a alocação<br />
do um espaço de memória necessário para guardar a referência "carteira":<br />
CarteiraDeEstudante carteira;<br />
carteira ???<br />
Após declarar a variável, a construção do objeto força a alocação do espaço ocupado pelas informações<br />
da classe CarteiraDeEstudante:<br />
CarteiraDeEstudante carteira;<br />
carteira = new CarteiraDeEstudante();<br />
carteira ???<br />
nome ""<br />
numero 0<br />
Finalmente, quando atribuímos o objeto à variável "carteira", é criada a referência ao objeto recém<br />
construído:<br />
CarteiraDeEstudante carteira;<br />
carteira = new CarteiraDeEstudante();<br />
carteira CarteiraDeEstudante@113750<br />
nome ""<br />
Numero 0<br />
42<br />
referência<br />
Atribuição de referências a uma variável<br />
<strong>Java</strong> trata as variáveis declaradas a partir de uma classe como referências, ou seja, passamos a chamar<br />
essas variáveis apenas como referências a uma determinada classe.
INTRODUÇÃO À ORIENTA ÇÃO A OBJET OS<br />
Veja o exemplo:<br />
int x = 2002;<br />
int y = x;<br />
CarteiraDeEstudante carteira1 = new CarteiraDeEstudante();<br />
CarteiraDeEstudante carteira2 = carteira1;<br />
Quatro variáveis são criadas: duas do tipo int e duas referências a objetos do tipo CarteiraDeEstudante.<br />
O valor de x é 2002, e a segunda linha copia o valor da variável x na variável y. Ambas as variáveis<br />
inteiras continuam independentes entre si.<br />
Já com as variáveis carteira1 e carteira2 o comportamento é diferente. Quando instanciamos a variável<br />
carteira1, um objeto da classe CarteiraDeEstudante é alocado na memória. Quando atribuímos o<br />
conteúdo dessa variável à carteira2, não estamos criando um novo objeto, mas sim copiando o endereço<br />
do objeto referenciado pela variável carteira1.<br />
Na memória, o endereçamento ficará assim:<br />
x 2002<br />
y 2002<br />
carteira1 CarteiraDeEstudante@113750<br />
carteira2 CarteiraDeEstudante@113750<br />
Termos básicos em Orientação a Objetos<br />
43<br />
CarteiraDeEstudante@113750:<br />
nome ""<br />
Numero 0<br />
Alguns termos são básicos quando estamos descrevendo um sistema desenvolvido a partir do paradigma<br />
de Orientação a Objetos. Abaixo enumeramos alguns desses termos:<br />
Tipo agregado de dados: é um tipo de dado definido pelo programador. Agregado pelo fato de ser<br />
definido a partir da agregação de um ou mais tipos primitivos de dados em <strong>Java</strong>.<br />
Classe: A tradução em linguagem orientada a objetos dos tipos agregados de dados. O conceito de<br />
classe pode ser visto com uma superclasse do conceito de tipos agregados de dados. Além de agregar<br />
tipos primitivos de dados, uma classe provê outras funcionalidades, que serão apresentadas em detalhes<br />
ao longo do semestre.<br />
Objeto: Uma instância de uma classe. Podemos considerar uma classe como um gabarito, um modelo a<br />
partir do qual criamos objetos. Podemos declarar, por exemplo, uma classe que representa carteiras de<br />
estudantes. A classe representa um modelo de carteira de estudante. Já uma instância dessa classe é<br />
um conjunto de valores aplicados a esse modelo.<br />
Membro: Um membro de uma classe é um dos elementos que formam o modelo representado pela<br />
classe. No nosso exemplo, as variáveis nome e número são membros da classe CarteiraDeEstudante.<br />
Referência: Em <strong>Java</strong>, uma variável definida a partir de uma classe não contém as informações sobre um<br />
objeto dessa classe. Ao invés disso, a variável contém o endereço de memória no qual se encontram<br />
esses valores. Tais variáveis são chamadas de referências a um objeto. Um objeto pode ser referenciado<br />
por mais de uma variável. Porém, uma variável pode referenciar, no máximo, um objeto.
Abstração de dados<br />
Uma das vantagens da programação orientada a objetos é a capacidade de representar um objeto e o<br />
comportamento esperado por esse objeto em um único trecho de código, conhecido como tipo abstrato de<br />
dados, ou simplesmente classe.<br />
Tipos abstratos de dados<br />
Quando definimos um tipo agregado de dados, podemos também definir um conjunto de operações que<br />
podem incidir sobre esse tipo de dado. Este não é um conceito novo. Quando uma linguagem de<br />
programação define um tipo primitivo, tal como um inteiro, ele também define um número de operações<br />
que pode ser aplicada a dados desse tipo, como a adição, a subtração, a multiplicação, a divisão, etc.<br />
Algumas linguagens de programação, incluindo <strong>Java</strong>, permitem uma estreita associação entre a<br />
declaração de um tipo de dados e a declaração das operações que incidem sobre as variáveis deste tipo.<br />
Essa associação normalmente é descrita como um tipo abstrato de dados.<br />
Em <strong>Java</strong>, você pode criar um tipo abstrato de dados através da implementação de métodos:<br />
/**<br />
* Classe que modela o processo de avaliação dos alunos da disciplina<br />
* Sistemas Orientados a Objetos I<br />
* @author Felipe Gaúcho © 2002<br />
* @version exemplo<br />
*/<br />
public class Avaliacao<br />
{<br />
public float[] trabalhos = new float[4]; // quatro trabalhos<br />
public float[] provas = new float[2]; // duas provas<br />
/**<br />
* Método que permite a inclusão da nota de um trabalho<br />
* @param numeroDoTrabalho O número do trabalho a ser atribuído a nota<br />
* @param nota A nota que o aluno tirou no trabalho<br />
*/<br />
public void atualizarNotaDeTrabalho(int numeroDoTrabalho, float nota)<br />
{<br />
trabalhos[numeroDoTrabalho] = nota;<br />
}<br />
/**<br />
8
ABSTRAÇÃO DE DADOS<br />
}<br />
* Método que permite a inclusão da nota de uma prova<br />
* @param numeroDaProva O número da prova a ser atribuído a nota<br />
* @param nota A nota que o aluno tirou na prova<br />
*/<br />
public void atualizarNotaDeProva(int numeroDaProva, float nota)<br />
{<br />
provas[numeroDaProva] = nota;<br />
}<br />
/** @return A nota do aluno na primeira avaliação parcial */<br />
public float primeiraParcial()<br />
{<br />
float parcial = (2.0F * provas[0] + trabalhos[0] + trabalhos[1]) / 4.0F;<br />
return parcial;<br />
}<br />
/** @return A nota do aluno na segunda avaliação parcial */<br />
public float segundaParcial ()<br />
{<br />
float parcial = (2.0F * provas[1] + trabalhos[2] + trabalhos[3]) / 4.0F;<br />
return parcial;<br />
}<br />
/** @return A média final do aluno */<br />
public float media()<br />
{<br />
float media = (primeiraParcial() + segundaParcial() ) / 2.0F;<br />
return media;<br />
}<br />
não se preocupe com os modificadores public que aparecem na definição dos métodos da classe<br />
Avaliacao. No decorrer da disciplina iremos estudar os tipos de modificadores de métodos e classes.<br />
Definição de métodos<br />
Linguagens como <strong>Java</strong>, que comportam tipos abstratos de dados, criam uma estreita associação entre os<br />
dados e o código. Não descrevemos um método como uma operação sobre objetos do tipo Avaliação. Ao<br />
invés disso, consideramos que objetos do tipo Avaliação "já sabem" como processar as operações<br />
relativas à avaliação de um aluno. Por exemplo:<br />
// método inicial de um programa qualquer<br />
static public void main(String[] args)<br />
{<br />
// Cria uma instância da classe Avaliação e uma referência a esse objeto<br />
Avaliacao avaliacaoDoJoao = new Avaliacao();<br />
avaliacaoDoJoao.atualizarNotaDeTrabalho(0, 8.5F); // trabalho 1<br />
avaliacaoDoJoao.atualizarNotaDeTrabalho(1, 8.0F); // trabalho 2<br />
avaliacaoDoJoao.atualizarNotaDeTrabalho(2, 9.0F); // trabalho 3<br />
45
ABSTRAÇÃO DE DADOS<br />
}<br />
avaliacaoDoJoao.atualizarNotaDeTrabalho(3, 7.5F); // trabalho 4<br />
avaliacaoDoJoao.atualizarNotaDeProva( 0, 7.2F); // prova 1<br />
avaliacaoDoJoao.atualizarNotaDeProva( 1, 9.5F); // prova 2<br />
// note que, nesse trecho do código, não foi necessário calcular a média<br />
// propriamente dita. Bastou chamar o método média da classe Avaliação.<br />
// Por isso, podemos interpretar que os objetos da classe Avaliacao<br />
// "sabem como realizar a operação de cálculo da média das notas".<br />
float mediaDoJoao = avaliacaoDoJoao.media();<br />
System.out.println("Média do João = " + mediaDoJoao);<br />
C:\Felipe\fic\Avaliacao>java Avaliacao<br />
Média do João = 8.3<br />
A idéia de que métodos são uma propriedade dos dados ao invés de considerá-los como algo à parte<br />
é um passo importante para a construção de um sistema Orientado a objetos. Algumas vezes, você irá<br />
ouvir o termo message passing. Esse termo é usado em algumas linguagens para transmitir a noção de<br />
instrução para que um item de dados realize algo para si próprio. Na verdade, nas linguagens que usam<br />
convencionalmente essa terminologia, ela também reflete a natureza da implementação.<br />
Em <strong>Java</strong>, os métodos são definidos através de uma abordagem muito semelhante a que é usada em<br />
outras linguagens, em especial C e C++. A assinatura de um método tem a seguinte forma:<br />
modificador tipoDeRetorno identificador (listaDeArgumentos)<br />
{<br />
}<br />
// bloco de comandos<br />
A palavra static significa que o método não precisa estar associado a um objeto específico. O uso de<br />
static é opcional. Dizemos que métodos estáticos são métodos de classe, ou seja, podemos chama-lo a<br />
partir do nome da classe. Exemplo:<br />
System.out.println();<br />
System.exit(0);<br />
Note que os métodos out.println(); e exit(0); não estão associados a um objeto da classe System, apenas<br />
à definição da própria classe.<br />
A palavra final também é opcional e será discutida mais tarde.<br />
O identificador pode ser qualquer identificador legal, com algumas restrições baseadas nos nomes que<br />
já estão em uso. De acordo com o nosso padrão de codificação, o nome de um método sempre deve<br />
começar com uma letra minúscula e depois capitular a primeira letra das palavras que compõem o<br />
identificador.<br />
46
ABSTRAÇÃO DE DADOS<br />
O tipoDeRetorno indica o tipo de valor retornado pelo método. Se o método não retornar nenhum valor,<br />
ele deve ser declarado como void. O <strong>Java</strong> é rigoroso quanto aos valores retornados e, se a declaração<br />
estabelece que o método retorna um int, por exemplo, então você deve proceder assim em todos os<br />
caminhos possíveis de retorno.<br />
O modificador é uma palavra reservada pertencente ao grupo de modificadores suportados em <strong>Java</strong>:<br />
public, protected ou private. O modificador public de acesso indica que o método pode ser chamado a<br />
partir de qualquer outro código e o private indica que um método só pode ser chamado por outros<br />
métodos da mesma classe onde ele está sendo declarado. Discutiremos o protected mais adiante.<br />
A listaDeArgumentos permite que parâmetros sejam passados para um método. Os elementos da lista<br />
são separados por vírgula, enquanto cada elemento consiste em um tipo e um identificador. Por exemplo:<br />
public void atualizarNotaDeTrabalho(int numeroDoTrabalho, float nota)<br />
{<br />
trabalhos[numeroDoTrabalho] = nota;<br />
}<br />
a lista de argumentos do método atualizarNotaDeTrabalho é composta de dois argumentos:<br />
numeroDoTrabalho: um argumento do tipo inteiro (int)<br />
nota: um argumento do tipo ponto flutuante (float)<br />
Note também que esse método não retorna nenhum valor, ou seja, o tipo de retorno é void (nulo).<br />
todos os argumentos em <strong>Java</strong> são passados por valor. Ou seja, o valor original do argumento não<br />
será modificado após o retorno do método. Quando a instância de uma classe é usada como argumento<br />
de um método, o valor que está sendo passado ao método é a referência a esse objeto – o valor dos<br />
atributos desse objeto podem ser modificados no método invocado, mas a referência a esse objeto não.<br />
Exemplo:<br />
public class Valor<br />
{<br />
static public void main (String[] args)<br />
{<br />
// Aqui a variável 'valor' tem o valor 2<br />
int valor = 2;<br />
}<br />
somaDez(valor);<br />
// Aqui a variável 'valor' continua com o valor 2<br />
System.out.println("valor = " + valor);<br />
// Note que esse método é private, ou seja, só pode ser acessado por<br />
// objetos da classe Valor. O contexto 'static' será discutido mais tarde<br />
static private void somaDez(int valor)<br />
{<br />
// Aqui temos outra variável , que recebeu o conteúdo do argumento 'valor'<br />
47
ABSTRAÇÃO DE DADOS<br />
}<br />
}<br />
valor += 10;<br />
A referência this<br />
Note nos exemplos anteriores que sempre usamos o operador ponto (.) para acessar os membros de<br />
uma classe. Exemplo:<br />
// Cria uma instância da classe Avaliação e uma referência a esse objeto<br />
Avaliacao avaliacaoDoJoao = new Avaliacao();<br />
avaliacaoDoJoao.atualizarNotaDeTrabalho(0, 8.5F); // trabalho 1<br />
Mas note também que dentro da classe Avaliacao, os membros são manipulados sem estarem<br />
associados a um objeto:<br />
public void atualizarNotaDeTrabalho(int numeroDoTrabalho, float nota)<br />
{<br />
// Note que a variável trabalhos não está sendo<br />
// associada a nenhum objeto<br />
trabalhos[numeroDoTrabalho] = nota;<br />
}<br />
Isso é possível porque em <strong>Java</strong>, os membros de uma classe possuem, dentro da classe, uma referência<br />
implícita identificada pela palavra reservada this:<br />
public void atualizarNotaDeTrabalho(int numeroDoTrabalho, float nota)<br />
{<br />
// Referência ao objeto corrente<br />
this.trabalhos[numeroDoTrabalho] = nota;<br />
}<br />
Essa referência, na verdade, está associando o membro a um objeto do tipo da classe em que o método<br />
se encontra. Em <strong>Java</strong> não é necessário que se use a referência this pois, caso uma referência não seja<br />
digitada, a referência this será assumida pelo interpretador <strong>Java</strong> (JVM).<br />
Além disso, a referência this pode ser usada como argumento para representar o objeto corrente na<br />
chamada de um método:<br />
/**<br />
* Classe que modela o processo de avaliação dos alunos da disciplina<br />
*/<br />
public class Avaliacao<br />
{<br />
public float[] trabalhos = new float[4]; // quatro trabalhos<br />
public float[] provas = new float[2]; // duas provas<br />
48
ABSTRAÇÃO DE DADOS<br />
}<br />
public void toString ()<br />
{<br />
System.out.println("Objeto atual = " + this);<br />
}<br />
//... demais métodos ...<br />
Gerando a documentação de um programa - javadoc<br />
Dentre os programas que compõem o ambiente de desenvolvimento <strong>Java</strong>, um dos mais interessantes é<br />
o gerador automático de documentação, o javadoc. <strong>Java</strong>doc é um aplicativo que gera arquivos Html a<br />
partir de um ou mais códigos fonte <strong>Java</strong>. Essa documentação é útil para que outros desenvolvedores<br />
consultem a funcionalidade das classes implementadas por terceiros.<br />
Por exemplo: suponha que você tenha implementado as classes de um sistema de avaliação de alunos<br />
da FIC. E suponha que um outro programador deve usar essas classes para gerar uma interface gráfica<br />
que permita que os professores da faculdade atualizem as notas dos alunos via Internet. Esse outro<br />
programador deverá ser informado sobre a funcionalidade das classes implementadas por você, ou seja,<br />
qual a classe que representa uma avaliação, um aluno, etc. e quais os métodos dessas classes devem<br />
ser usados para atualizar as notas, os dados cadastrais, etc.<br />
Essa informação é encontrada na documentação das classes, gerada automaticamente pelo <strong>Java</strong><br />
conforme mostra exemplo abaixo:<br />
C:\Felipe\fic\Avaliacao>md docs<br />
C:\Felipe\fic\Avaliacao>javadoc -d docs Avaliacao.java<br />
Loading source file Avaliacao.java...<br />
Constructing <strong>Java</strong>doc information...<br />
Building tree for all the packages and classes...<br />
Building index for all the packages and classes...<br />
Generating docs\overview-tree.html...<br />
Generating docs\index-all.html...<br />
Generating docs\deprecated-list.html...<br />
Building index for all classes...<br />
Generating docs\allclasses-frame.html...<br />
Generating docs\index.html...<br />
Generating docs\packages.html...<br />
Generating docs\Avaliacao.html...<br />
Generating docs\serialized-form.html...<br />
Generating docs\package-list...<br />
Generating docs\help-doc.html...<br />
Generating docs\stylesheet.css...<br />
C:\Felipe\fic\Avaliacao><br />
Os comandos acima executam as seguintes operações:<br />
A primeira linha gera um novo diretório onde serão armazenados os arquivos da documentação: md<br />
(em sistemas operacionais UNIX/Linux, o comando é o mkdir )<br />
49
ABSTRAÇÃO DE DADOS<br />
Depois é chamado o aplicativo javadoc, passando como parâmetro da linha de comando o nome do<br />
código-fonte (ou dos códigos-fonte) a serem documentados.<br />
Para ver a documentação recém criada, basta usar um browser para ler o arquivo que foi gerado<br />
automaticamente dentro do diretório de documentação: ./docs/index.html<br />
Note que a documentação realça partes da documentação, como o nome do autor, a versão da classe,<br />
etc. Esse destaque pode ser definido durante a implementação do código, através de TAGS reservadas<br />
pelo javadoc.<br />
Algumas dessas tags, que devem ser usadas dentro de comentários delimitados por /** */, são<br />
enumeradas abaixo:<br />
@author<br />
@version<br />
@param<br />
@return<br />
@exception<br />
o nome de quem implementou a clase (ou da empresa).<br />
a versão da classe<br />
o nome de um argumento usado por um método<br />
o retorno de um método<br />
o tipo de exceção ao qual um método está sujeito<br />
O aplicativo javadoc tem várias opções que permitem ao programador personalizar a documentação de<br />
seus sistemas. Para aprender mais sobre o javadoc, consulte a documentação que acompanha o<br />
ambiente de desenvolvimento <strong>Java</strong>. (normalmente, essa documentação se encontra no diretório:<br />
../jdk1.3.1/docs/tooldocs/javadoc/index.html, mas pode variar conforme o sistema<br />
operacional e as opções utilizadas durante a instalação do jdk).<br />
Documentação da API <strong>Java</strong><br />
Agora que você está familiarizado com o formato de documentação usado pelo <strong>Java</strong>, podemos investigar<br />
a API <strong>Java</strong> (Application Programming Interface – interface de programação de aplicativo).<br />
A API <strong>Java</strong> é um conjunto de classes disponíveis aos programadores, desenvolvidas pela SUN e que<br />
visam agilizar a produção de softwares compatíveis com a linguagem <strong>Java</strong>.<br />
Essas classes se encontram no diretório ../jdk1.3.1/jre/lib/rt.jar. (Os arquivos *.jar podem ser<br />
"investigados" com aplicativos de compactação de arquivos, tais como o WinZIP)<br />
A documentação dessas classes está publicada na documentação do ambiente de desenvolvimento <strong>Java</strong><br />
(normalmente no diretório ../jdk1.3.1/docs/api/index.html). Note que essa documentação foi criada<br />
também pelo javadoc, e que tem o mesmo formato da documentação das classes<br />
é vital que o aluno se habitue a consultar essa documentação durante o desenvolvimento de<br />
programas em <strong>Java</strong>. Nesse primeiro momento, procure navegar pela documentação da API <strong>Java</strong>,<br />
identificando o formato da informação, os termos,etc. Outra boa iniciativa é consultar também a<br />
documentação do ambiente de desenvolvimento <strong>Java</strong>, que normalmente se encontra no diretório<br />
../jdk1.3.1/docs/index.html. Conhecer amplamente a API <strong>Java</strong> é prerrogativa para todo bom<br />
programador <strong>Java</strong>.<br />
Exercícios<br />
1. Implemente um classe que represente um aluno da FIC, contendo três membros:<br />
a. O nome do aluno<br />
b. O número de matrícula do aluno<br />
c. Um objeto da classe Avaliacao<br />
50
ABSTRAÇÃO DE DADOS<br />
2. Depois implemente o método principal dessa classe com o seguinte comportamento:<br />
/**<br />
* Perguntar ao usuário o nome, número de matrícula e as<br />
* seis notas de um aluno. Depois, imprima a identificação e a média do aluno,<br />
* dizendo se ele foi:<br />
* aprovado (média > 6.9)<br />
* dependente de prova final (3.9 < média
Encapsulamento e sobrecarga de<br />
métodos<br />
Esta seção apresenta o controle sobre a acessibilidade dos membros de uma classe e a<br />
implementação de classes com comportamento dinâmico através da sobrecarga de<br />
métodos.<br />
Acessibilidade dos membros de uma classe<br />
Considere a criação de uma classe, por exemplo, a classe Avaliacao vista na aula passada. Quando<br />
instanciamos um objeto da classe Avaliacao, seus membros podem acessados por quaisquer outros<br />
objetos presentes na Máquina Virtual <strong>Java</strong> (JVM). Isso é definido pelo modificador public que antecede a<br />
declaração desses membros.<br />
// Acessando os membros da classe carteira de estudante:<br />
Avaliacao notas = new Avaliacao();<br />
// Note que um valor inválido pode ser atribuído<br />
// a um membro público de uma classe<br />
notas.trabalhos[0] = -4.5F; // Nota menor que zero<br />
notas.provas[1] = 20F; // Nota maior que 10<br />
9<br />
Algumas vezes, entretanto, precisamos evitar que valores inválidos ou comportamentos inválidos sejam<br />
atribuídos aos membros de uma classe. No exemplo acima, uma nota jamais poderia ser negativa ou<br />
maior que dez. Como os membros da classe Avaliacao são todos públicos, a consistência desses<br />
membros deve ser garantida externamente ao código da classe, o que torna a consistência dos objetos<br />
dessa classe muito frágil.<br />
Para contornar isso, a Orientação a Objetos define outras formas de acessibilidade aos membros de uma<br />
classe, através dos modificadores final, protected e private. Através desses modificadores, o<br />
programador pode ENCAPSULAR os membros de uma classe, de forma a garantir a sua consistência. O<br />
membro de uma classe pode ser:<br />
o Público – declarado com o modificador public, e acessível por qualquer objeto instanciado na<br />
máquina virtual. Normalmente declaramos públicos apenas alguns métodos de uma classe. Os<br />
membros variáveis são todos declarados privados, salvo raras exceções.<br />
o Privado – declarado com o modificador private, e acessível somente por métodos da de objetos<br />
da própria classe onde o membro foi declarado.
ENCAPSULAMENTO E SOB RECARGA DE MÉTODOS<br />
o Protegido – declarado com o modificador protected, e acessível somente por métodos de<br />
subclasses da classe onde o membro foi declarado ou da própria classe onde o membro foi<br />
declarado. Esse modificador será estudado junto com os conceitos de herança e polimorfismo,<br />
mais tarde em nossa disciplina.<br />
o Constante – um membro declarado com o modificador final não pode ter a sua definição<br />
modificada por nenhum objeto, nem mesmo um objeto da classe onde o membro foi definido.<br />
Dizemos que um membro final é uma constante. No caso de herança, uma subclasse não pode<br />
sobrecarregar um método declarado como final na superclasse. (isso será estudado mais<br />
adiante).<br />
O modificador final<br />
Membros declarados com o modificador final são membros constantes, ou seja, cujo valor não pode ser<br />
mais modificado.<br />
Exemplo:<br />
/**<br />
* Classe que modela o processo de avaliação dos alunos da disciplina<br />
* Sistemas Orientados a Objetos I<br />
* @author Felipe Gaúcho © 2002<br />
* @version exemplo<br />
*/<br />
public class Avaliacao<br />
{<br />
final int numeroDeProvas=2; // um membro final deve ser inicializado na declaração<br />
final int numeroDeTrabalhos=4; // ou no construtor (veremos construtores mais tarde)<br />
}<br />
public float[] trabalhos = new float[numeroDeTrabalhos];<br />
public float[] provas = new float[numeroDeProvas];<br />
// Um método declarado FINAL não poderá ser sobrescrito em subclasses da<br />
// classe Avaliacao (veremos isso mais adiante)<br />
final public void teste()<br />
{<br />
// Essa linha causará um erro de compilação<br />
numeroDeProvas = 5;<br />
}<br />
C:\Felipe\fic\Avaliacao>javac Avaliacao.java<br />
Avaliacao.java:48: cannot assign a value to final variable numeroDeProva s<br />
numeroDeProvas = 5;<br />
^<br />
1 error<br />
C:\Felipe\fic\Avaliacao><br />
53
ENCAPSULAMENTO E SOB RECARGA DE MÉTODOS<br />
O modificador private<br />
Membros declarados com o modificador private são acessíveis somente por métodos da própria classe<br />
onde foram definidos.<br />
Aqui está o segredo da consistência: nos métodos públicos o programador codifica um a série de<br />
verificações à validade das operações e das modificações dos valores dos membros variáveis da classe<br />
(crítica). Caso a chamada ao método não agrida à consistência do objeto, o método atualiza os valores<br />
ou executa a requerida operação. Por exemplo:<br />
public class Avaliacao<br />
{<br />
final int numeroDeProvas = 2;<br />
final int numeroDeTrabalhos = 4;<br />
// Observe que os membros agora são privados. Só podem ser acessados<br />
// pelo objeto que os possui.<br />
private float[] trabalhos = new float[numeroDeTrabalhos];<br />
private float[] provas = new float[numeroDeProvas];<br />
/**<br />
* Método que permite a inclusão da nota de um trabalho<br />
* @param numeroDoTrabalho O número do trabalho a ser atribuído a nota<br />
* @param nota A nota que o aluno tirou no trabalho<br />
*/<br />
public void atualizarNotaDeTrabalho(int numeroDoTrabalho, float nota)<br />
{<br />
// Verifica se o índice da nota é válido<br />
if(numeroDoTrabalho>-1<br />
&& numeroDoTrabalho=0F && nota
ENCAPSULAMENTO E SOB RECARGA DE MÉTODOS<br />
}<br />
Encapsulamento<br />
O acesso exclusivo por métodos aos membros variáveis de uma classe e a implementação da verificação<br />
de consistência nesses métodos formam o conceito de encapsulamento.<br />
Encapsulamento é a garantia que os objetos de uma classe sempre serão consistentes quanto aos<br />
valores e comportamento esperados para esse objeto. O nome vem da idéia de que o objeto é protegido<br />
por uma cápsula, acessível somente através de métodos que possuem um controle rígido de<br />
consistência. Isso é muito importante na distribuição de classes, pois um programador pode declarar<br />
objetos de uma determinada classe em seus programas, confiando que o comportamento desse objeto<br />
obedecerá à documentação da classe.<br />
Sobrecarga de métodos<br />
Algumas vezes, um problema pode exigir que uma classe permita que uma mesma operação seja<br />
aplicada a diversos tipos de dados. Por exemplo: suponha que você esteja implementando uma classe<br />
chamada Matematica, com o intuito de prover as quatro operações aritméticas: soma, subtração,<br />
multiplicação e divisão. Note que essas operações podem ser aplicadas a números inteiros ou<br />
fracionários, e seria tedioso para o programador ficar diferenciando os métodos para cada tipo de<br />
argumento (ex: somaDeInteiros(), somaDeDouble(), etc..)<br />
A solução para isso é conhecida em Orientação a Objetos como sobrecarga de métodos, exemplo:<br />
public class Matematica<br />
{<br />
/**<br />
* Método que soma dois inteiros<br />
* @param operando1 um número inteiro<br />
* @param operando2 um número inteiro<br />
* @return a soma dos dois operandos<br />
*/<br />
public int soma (int operando1, int operando2)<br />
{<br />
return operando1 + operando2;<br />
}<br />
/**<br />
* Método que soma um número com ele mesmo<br />
* @param operando um número inteiro<br />
* @return a soma do numero com ele mesmo<br />
*/<br />
public int soma (int operando)<br />
{<br />
return operando + operando;<br />
}<br />
/**<br />
55
ENCAPSULAMENTO E SOB RECARGA DE MÉTODOS<br />
* Método que soma dois número de ponto flutuante<br />
* @param operando1 um número de ponto flutuante<br />
* @param operando2 um número de ponto flutuante<br />
* @return a soma dos dois operandos<br />
*/<br />
public float soma (float operando1, float operando2)<br />
{<br />
return operando1 + operando2;<br />
}<br />
algumas linguagens, como C++, permitem a sobrecarga de operadores, mas <strong>Java</strong> não permite a<br />
sobrecarga de operadores.<br />
Os critérios para a sobrecarga de métodos são os seguintes:<br />
o A lista de argumentos deve ser diferente o suficiente para evitar a ambigüidade entre os<br />
métodos. O programador deve prever o uso de promoções e casting, que podem causar<br />
confusão na chamada dos métodos.<br />
o O tipo de retorno dos métodos pode ser diferente, mas não basta para que seja caracterizada<br />
a sobrecarga. A lista de argumentos deve apresentar alguma diferença, como o número de<br />
argumentos ou o tipo desses argumentos.<br />
Exercícios<br />
7. Reescreva a classe Avaliacao.java da aula passada, garantindo que os valores das notas e o<br />
cálculo das médias seja sempre consistente. Discuta com seus colegas quais modificadores<br />
devem ser aplicados aos membros dessa classe para que os objetos sejam sempre consistentes.<br />
8. Reescreva a classe Aluno.java da aula passada, realizando testes de consistência a partir da<br />
classe Avaliacao.java que você reescreveu acima<br />
9. Generalize a classe Avaliacao.java permitindo que uma disciplina possa também adotar valores<br />
inteiros para as médias de seus alunos. Teste com a classe Alunos.java.<br />
56
Construtores<br />
Conforme avançamos no estudo de programação orientada a objetos, aumentamos o<br />
grau de abstração de nosso discurso - passamos a citar objetos como se eles fossem<br />
palpáveis dentro de nosso sistema. Entretanto, uma visão mais atenta nos lembra que<br />
os objetos continuam sendo abstrações de bits armazenados na memória RAM do<br />
computador. Nesta seção o aluno é orientado a pensar na forma como os sistemas<br />
criam objetos e como os membros de suas classes são organizados na memória em tempo<br />
de execução.<br />
Processo de instanciação de objetos<br />
Quando declaramos uma variável de um tipo abstrato de dado, uma classe, estamos apenas criando um<br />
a referência a um objeto dessa classe. Esse objeto só passará a existir quando o comando new for<br />
interpretado pela JVM. Esse comando instancia um objeto a partir do construtor definido para a classe<br />
em questão.<br />
A instanciação de um objeto em <strong>Java</strong> obedece ao seguinte roteiro:<br />
1. O espaço para o novo objeto é alocado na memória da máquina virtual e inicializado com valores<br />
padrão. Em <strong>Java</strong>, nenhum objeto pode ser instanciado com os valores de seus membros<br />
aleatórios (tal qual ocorre em C++). Por exemplo, quando declaramos um array de inteiros de 30<br />
posições em <strong>Java</strong>, esse array será preenchido com o valor ZERO no momento de sua alocação<br />
na memória .<br />
2. A seguir é executada a inicialização explicita dos membros do objeto, caso essa inicialização<br />
faça parte do código da classe desse objeto.<br />
3. Finalmente, o construtor da classe é executado.<br />
Inicialização explícita de membros variáveis<br />
Quando instanciamos um objeto de um determinada classe, os membros variáveis desse objeto recebem<br />
os seguintes valores padrão:<br />
Tipo: Valor inicial:<br />
int, byte ou long 0<br />
float, double 0.0<br />
Char '' (valor 0)<br />
String ""<br />
Demais classes Null<br />
10
CONSTRUTORES<br />
Muitas vezes, porém, pretendemos definir os valores padrão para os membros dos objetos de uma<br />
classe. Tal definição é feita através da inicialização explícita de membros variáveis. Exemplo:<br />
/**<br />
* Classe que modela o processo de avaliação dos alunos da disciplina<br />
* Sistemas Orientados a Objetos I<br />
* @author Felipe Gaúcho © 2002<br />
* @version exemplo<br />
*/<br />
public class Avaliacao<br />
{<br />
// Membros de classe, constantes, normalmente são declarados<br />
// estáticos (static), pois a referência de todas as instâncias<br />
// dessa classe farão referência ao mesmo espaço de memória alocado<br />
// para esses membros. Padrão de codificação: variáveis finais são<br />
// declaradas sempre com todas as letras maiúsculas, e as palavras<br />
// são separadas por subscritos (_)<br />
static final int NUMERO_DE_PROVAS;<br />
static final int NUMERO_DE_TRABALHOS;<br />
private String nomeDaDisciplina = "SOO-I";<br />
// O uso do modificador private faz com que esse<br />
// membros variáveis sejam membros do objeto e não da classe<br />
private float[] trabalhos;<br />
private float[] provas;<br />
// Note que isso é um bloco inicializador e não um método, pois não<br />
// tem assinatura.<br />
{<br />
try<br />
{<br />
trabalhos = new float[NUMERO_DE_TRABALHOS];<br />
provas = new float[NUMERO_DE_PROVAS];<br />
}<br />
catch(Exception erro)<br />
{<br />
// O objetivo do bloco inicializador é evitar que eventuais<br />
// erros de inicialização explícita fiquem sem tratamento.<br />
erro.printStackTrace();<br />
System.exit(0);<br />
}<br />
}<br />
// Por ser estática, essa inicialização será executada antes de outras<br />
// inicialização que não sejam estáticas, como a inicialização acima.<br />
static<br />
{<br />
NUMERO_DE_PROVAS = 2;<br />
NUMERO_DE_TRABALHOS = 4;<br />
}<br />
58
CONSTRUTORES<br />
}<br />
// restante do corpo da classe....<br />
Construtores<br />
Algumas vezes, os valores dos membros de um objeto só serão conhecidos no momento de sua<br />
instanciação. Esse comportamento dinâmico é previsto em Orientação a Objetos através da<br />
implementação de construtores. Em <strong>Java</strong>, um construtor é um método, identificado com o nome da<br />
classe a que pertence, podendo conter argumentos. Exemplo:<br />
/**<br />
* Classe que modela o processo de avaliação dos alunos da disciplina<br />
* Sistemas Orientados a Objetos I<br />
* @author Felipe Gaúcho © 2002<br />
* @version exemplo<br />
*/<br />
public class Avaliacao<br />
{<br />
// inicializadores (vide exemplo anterior)<br />
// Construtor padrão, sem argumentos<br />
Avaliacao()<br />
{<br />
System.out.println("objeto da classe Avaliacao");<br />
}<br />
// Construtor com argumentos<br />
Avaliacao(int[] notas)<br />
{<br />
if(notas.length != (numeroDeProvas + numeroDeTrabalhos))<br />
{<br />
System.out.println("erro nos parâmetros do construtor");<br />
// O comando return pode ser usado para encerrar a<br />
// execução de um construtor<br />
return;<br />
}<br />
else<br />
{<br />
// A referência this é opcional (implícita)<br />
this.atualizarNotaDeProva(0, notas[0]);<br />
atualizarNotaDeProva(1, notas[1]);<br />
}<br />
}<br />
this.atualizarNotaDeTrabalho(0, notas[2]);<br />
this.atualizarNotaDeTrabalho(1, notas[3]);<br />
this.atualizarNotaDeTrabalho(2, notas[4]);<br />
this.atualizarNotaDeTrabalho(3, notas[5]);<br />
59
CONSTRUTORES<br />
}<br />
quando não declaramos nenhum construtor para uma classe, o interpretador da JVM considera um<br />
construtor padrão, onde todos os membros dos objetos criados serão inicializados com seus respectivos<br />
valores padrão.<br />
Exercícios<br />
1. Construa um sistema de cadastro de avaliações de alunos, utilizando os conceitos das aulas 8, 9<br />
e 10. Lembre-se que os objetos do seu sistema devem ser todos consistentes, e que o sistema<br />
deve obedecer ao padrão de codificação. Seu programa deve ter pelo menos as três primeiras<br />
classes, identificadas abaixo (nomes sugeridos):<br />
a. Aluno.java – uma classe que representa a classe dos alunos da FIC. Um aluno da FIC,<br />
para o nosso exercício, é um objeto que dentre os seus membros possui:<br />
i. Carteira de estudante<br />
ii. Um relatório de avaliação<br />
b. Avaliacao.java – uma classe que representa uma avaliação padrão dos alunos da FIC,<br />
composta pela nota de duas provas e quatro trabalhos. (você pode definir seus próprios<br />
critérios de avaliação, mas eles tem que ser claros).<br />
c. CarteiraDeEstudante.<strong>Java</strong> – uma classe que representa uma carteira de estudante,<br />
contendo pelo menos o nome e o número de matrícula do aluno. Você pode definir outros<br />
membros dessa classe, como o número do curso, o nome da faculdade, etc. Dica: crie<br />
um construtor que lhe permita preencher os dados da carteira de estudante do aluno no<br />
ato da instanciação dos objetos dessa classe.<br />
d. CadastroDeAvaliacao.java – é o sistema propriamente dito (a única classe que precisa<br />
ter método static public void main(String[] args){}), essa classe identifica e<br />
executa comandos digitados pelo usuário. Essa classe pode usar um array de tamanho<br />
pré-estabelecido para guardar os objetos que representam os alunos. Uma forma mais<br />
elegante é que esse array seja dimensionado no momento da instanciação dessa classe.<br />
Dica: crie um construtor que lhe permita dimensionar o array que armazena os objetos.<br />
2. Gere a documentação do seu sistema com o javadoc.<br />
60
Herança e polimorfismo<br />
A relação entre as classes de um sistema orientado a objetos.<br />
O conceito de classificação<br />
Quando implementamos uma classe, estamos modelando alguma coisa, um empregado, um automóvel,<br />
um animal, etc. Muitas vezes, porém, precisamos refinar esse modelo para representar subgrupos de<br />
objetos dentro dessa classe.<br />
Por exemplo, suponha que você crie uma classe para representar os funcionários da FIC:<br />
public class Funcionario<br />
{<br />
private String nome;<br />
private String funcao;<br />
private Date dataDeAdmissao;<br />
private double salario;<br />
}<br />
public void decimoTerceiro(Date dataAtual)<br />
{<br />
// cálculo do décimo terceiro salário<br />
}<br />
public void alterarSalario(double novoSalario)<br />
{<br />
salário = novoSalario;<br />
}<br />
// restante do corpo da classe<br />
11<br />
Agora imagine que você deseja representar os seguintes tipos de funcionários da FIC: um coordenador<br />
de curso, um professor e um auxiliar de limpeza. Note que essas três classes de funcionários possuem<br />
muitos membros variáveis em comum, como data de admissão, salário, função, nome, etc. E possuem<br />
também alguns métodos em comum, como o cálculo do décimo terceiro, alteração de salário, etc. Porém,<br />
cada uma dessas classes possui um conjunto de métodos exclusivos, ou seja, que só se aplicam a seus<br />
objetos e não a objetos de outras classes. Por exemplo: um auxiliar de limpeza tem o método public<br />
void limpar();, o coordenador tem o método public void criarTurma(Turma aula); e o<br />
professor tem o método public void lecionar();. Observe também que um coordenador é por sua<br />
vez um professor, e portanto possui todos os métodos de um professor e mais alguns exclusivos da<br />
classe Coordenador.
HERANÇA E POLIMORFIS MO<br />
Se fôssemos criar uma classe para cada uma dessas categorias de funcionários, acabaríamos repetindo<br />
muito código, além do quê, perderíamos a importante relação existente entre os objetos dessas classes:<br />
todos são funcionários.<br />
Para resolver isso, a Orientação a Objetos define o conceito de herança, permitindo que subclasses<br />
sejam criadas a partir de uma classe original, mais genérica e conhecida como superclasse.<br />
No nosso exemplo, teremos a seguinte hierarquia de classes:<br />
nível 0<br />
nível 1<br />
nível 2<br />
SUPERCLASSE<br />
SUBCLASSE<br />
SUB SUBCLASSE<br />
Uma das grandes vantagens do uso de subclasses é o aumento da confiabilidade e facilidade de<br />
manutenção dos sistemas. Se, por acaso, precisarmos mudar o comportamento da classe Funcionario,<br />
as demais classes refletirão essa mudança sem a necessidade de serem todas reimplementadas. Por<br />
exemplo, se a regra para o cálculo do décimo terceiro salário for alterada pelo governo, bastará ao<br />
desenvolvedor alterar um único trecho de código, justamente o método "public void<br />
decimoTerceiro(Date dataAtual)" da classe Funcionário. Caso contrário o desenvolvedor teria que alterar<br />
todas as classes que implementassem o cálculo de décimo terceiro salário.<br />
Observe também que quanto mais abaixo na hierarquia, mais especializado se torna uma classe. A idéia<br />
é que as classes superiores sejam bastante genéricas, permitindo a representação mais abrangente<br />
possível das entidades que inspiraram o projeto dessa classe.<br />
A palavra reservada extends<br />
Em <strong>Java</strong> usamos a palavra reservada extends para definir uma subclasse. Essa palavra traduz a idéia<br />
de que uma classe está estendendo as características e a funcionalidade de sua superclasse. No nosso<br />
exemplo teríamos:<br />
// Essa classe herda todos os atributos e a funcionalidade da classe Funcionario<br />
public class AuxiliarDeLimpeza extends Funcionario<br />
{<br />
public void limpar(Sala aula)<br />
{<br />
// método que limpa uma sala de aula<br />
}<br />
62<br />
Professor<br />
Coordenador<br />
Funcionário<br />
Auxiliar<br />
de limpeza
HERANÇA E POLIMORFIS MO<br />
}<br />
// restante do corpo da classe<br />
// Essa classe herda todos os atributos e a funcionalidade da classe Funcionario<br />
public class Professor extends Funcionario<br />
{<br />
public void lecionar (Disciplina aula)<br />
{<br />
// método que processa o ensino da disciplina referenciada por 'aula'<br />
}<br />
// restante do corpo da classe<br />
}<br />
// Essa classe herda todos os atributos e a funcionalidade da classe Funcionário e também<br />
// da classe Professor<br />
public class Coordenador extends Professor<br />
{<br />
public void criarTurma(Turma turma, Curso curso)<br />
{<br />
// método que cria uma nova turma de alunos<br />
}<br />
// restante do corpo da classe<br />
}<br />
Note que as classes estendidas de Funcionário herdam todas as características dessa classe. Por<br />
exemplo, um objeto da classe Professor tem os membros variáveis nome, função, etc. bem como os<br />
métodos alterarSalario e decimoTerceiro:<br />
// Fragmento de código<br />
{<br />
Coordenador sid = new Coordenador();<br />
Professor pardal = new Professor();<br />
}<br />
// Esses seriam comandos válidos, considerando que o valor dos membros<br />
// já tenham sido inicializados<br />
System.out.println(pardal.decimoTerceiro());<br />
System.out.println(sid.funcao);<br />
Herança em <strong>Java</strong><br />
Em Orientação a Objetos usamos a palavra herança para expressar a idéia de que uma classe herda<br />
todas as características e funcionalidade de uma outra classe. No nosso exemplo, as classes Professor e<br />
AuxiliarDeLimpeza herdam os membros da classe Funcionário. A classe Coordenador herda os membros<br />
da classe Funcionário e também herda os membros da classe Professor.<br />
Um detalhe importante é que na linguagem <strong>Java</strong> não existe herança múltipla. Não que a<br />
herança múltipla seja um conceito errado, pelo contrário, linguagens como C++ implementam herança<br />
63
HERANÇA E POLIMORFIS MO<br />
múltipla e esse é um conceito bastante polêmico e discutido em congressos sobre linguagens de<br />
programação. Os criadores da <strong>Java</strong> optaram por exigir herança simples por acreditarem que assim o<br />
código de torna mais legível e confiável, embora reconheçam que, às vezes, isso causa um trabalho<br />
extra aos desenvolvedores <strong>Java</strong>. A modelagem de alguns sistemas se torna redundante sem herança<br />
múltipla, mas na grande maioria dos casos isso não ocorre. Uma alternativa diante da ausência de<br />
herança múltipla é o uso de interfaces, um recurso de modelagem que será discutido mais adiante na<br />
disciplina.<br />
Outro aspecto importante é o fato de que os construtores não são herdados pelas subclasses. Uma<br />
classe estendida de outra herda todos os membros dessa superclasse menos os seus construtores.<br />
A superclasse Object<br />
Em <strong>Java</strong>, toda a classe é implicitamente estendida da classe Object, definida na API da linguagem<br />
como:<br />
public class Object;<br />
A superclasse do nosso exemplo poderia ter sido declarada assim:<br />
public class Funcionario extends Object;<br />
é o mesmo que<br />
public class Funcionario<br />
Polimorfismo<br />
Descrever um professor como um funcionário não é apenas uma forma conveniente de expressar a<br />
relação entre essas duas classes, é também uma forma de reaproveitamento de modelos. Quando<br />
criamos um modelo de funcionários da FIC, dizemos que esses funcionários tem um método chamado<br />
decimoTerceiro(). Quando depois criamos um modelo de professores da FIC baseado no modelo dos<br />
funcionários, assumimos que os professores também são capazes de processar o método<br />
decimoTerceiro(), sem que para isso tenhamos que reimplementar esse método.<br />
Essa relação leva à idéia de polimorfismo (do grego poli + morfos = várias formas). Um objeto polimórfico<br />
é um objeto que pode ter formas diferentes, funcionalidades diferentes mas continua a pertencer a uma<br />
única classe.<br />
No nosso exemplo, um professor e um auxiliar de limpeza são entidades com características e<br />
comportamentos diferentes, porém ambos continuam sendo funcionários. Por esse motivo, dizemos que<br />
a classe Funcionário é polimórfica, ou seja, não existe um formato único para os indivíduos que<br />
constituem a classe Funcionário.<br />
<strong>Java</strong>, como a maioria das linguagens orientadas a objeto, permite que um objeto seja referido com uma<br />
variável que é de um dos tipos da classe-pai:<br />
// fragmento de código<br />
{<br />
// Instanciação válida de objetos<br />
private Funcionario cleto = new Coordenador();<br />
private Funcionario gaucho = new Professor();<br />
private Object robo = new AuxiliarDeLimpeza();<br />
}<br />
64
HERANÇA E POLIMORFIS MO<br />
quando criamos uma referência a um tipo abstrato de dados, uma classe, o compilador <strong>Java</strong><br />
considera que o objeto atribuído a essa referência será do tipo declarado e não do tipo usado para criar o<br />
objeto. No exemplo acima, os objetos atribuídos às referências terão as seguintes características:<br />
cleto terá as características de um funcionário e não de um coordenador.<br />
gaucho terá as características de um funcionário e não de um professor.<br />
robo terá as características de um objeto e não de um auxiliar de limpeza.<br />
Argumentos e coleções heterogêneas<br />
Parece estranho criar uma referência para um objeto da classe Funcionario e deliberadamente atribuirlhe<br />
uma instância da classe Coordenador. É verdade, mas há motivos para que você queira alcançar<br />
esse efeito.<br />
Ao usar essa abordagem, você pode escrever métodos que aceitem um objeto "genérico", nesse caso,<br />
um Funcionario, e trabalhar adequadamente em qualquer subclasse dele. Assim, em uma determinada<br />
aplicação, você pode implementar um método que tem um objeto do tipo Funcionario como argumento e<br />
realiza operações sobre ele, independente da forma como esse objeto foi instanciado:<br />
// fragmento de código<br />
{<br />
static public void main(String[] args)<br />
{<br />
Funcionario[] funcionarios = new Funcionario[3];<br />
}<br />
}<br />
// coleção heterogênea<br />
funcionarios[0] = new Coordenador();<br />
funcionarios[1] = new Professor();<br />
funcionarios[2] = new AuxiliarDeLimpeza();<br />
// chamando um método com argumentos heterogêneos<br />
emitirRelatorio(new Professor()); // emitirRelatorio(Professor)<br />
emitirRelatorio(funcionarios[0]); // emitirRelatorio(Funcionario)<br />
// Método que usa uma superclasse como argumento:<br />
// Note que esse método pode receber o argumento da classe<br />
// Funcionário ou de qualquer uma de suas subclasses.<br />
static public void emitirRelatorio(Funcionário empregado)<br />
{<br />
System.out.println(empregado.nome + ", " + empregado.salario);<br />
}<br />
Uma coleção heterogênea é um agrupamento de coisas diferentes. Em Orientação a Objetos, você pode<br />
criar coleções de quaisquer objetos que tenham em comum uma classe ancestral. No exemplo acima, o<br />
array funcionarios é uma coleção heterogênea de objetos que compartilham a superclasse Funcionario.<br />
Podemos imaginar outras aplicações do uso de coleções heterogêneas, por exemplo, escrever um<br />
método que coloca os funcionários em ordem por salário ou data de admissão sem a preocupação com a<br />
função desses funcionários, se eles são coordenadores, professores ou auxiliares de limpeza.<br />
65
HERANÇA E POLIMORFIS MO<br />
Como em <strong>Java</strong> toda classe é uma subclasse de Objeto, você pode usar um array de objetos da classe<br />
Object como uma coleção de quaisquer outros objetos. Os únicos membros variáveis que não podem ser<br />
colocadas em uma coleção de objetos do tipo Object são membros declarados como tipos primitivos de<br />
dados.<br />
Mais tarde estudaremos em detalhes outras formas de usar coleções em <strong>Java</strong>. Veremos que a<br />
linguagem provê algumas classes específicas para o tratamento de coleções, cuja superclasse é a<br />
java.util.AbstractCollection.<strong>Java</strong> é especialmente elegante no tratamento de coleções, o que<br />
justifica uma (futura) aula específica sobre esse assunto.<br />
O operador instaceof<br />
Sabendo-se que você pode circular objetos usando referências às suas classes-pai, muitas vezes você<br />
pode querer saber o que você realmente tem. Esse é o objetivo do operador instanceof. Imagine que<br />
nossa classe hierárquica seja estendida, de modo que temos:<br />
// fragmento de código<br />
{<br />
static public void main(String[] args)<br />
{<br />
cumprimentar(new Professor());<br />
cumprimentar(new Coordenador());<br />
cumprimentar(new AuxiliarDeLimpeza());<br />
cumprimentar(new Funcionario());<br />
}<br />
// Note que esse método usa a instância do objeto recebido<br />
// como argumento para adotar um comportamento diferenciado<br />
// para cada subclasse de Funcionário. Ou seja,<br />
// o operador instanceof permite a tomada de decisões<br />
// a partir da classificação dos objetos.<br />
static public void cumprimentar(Funcionario empregado)<br />
{<br />
if(empregado instanceof Coordenador)<br />
{<br />
System.out.println(<br />
"Seja bem vindo Sr. " + empregado.nome);<br />
}<br />
else if(empregado instanceof Professor)<br />
{<br />
System.out.println(<br />
"Bom dia professor " + empregado.nome);<br />
}<br />
else if(empregado instanceof AuxiliarDeLimpeza)<br />
{<br />
System.out.println(<br />
"Fala aí, grande " + empregado.nome);<br />
}<br />
else<br />
{<br />
// Funcionário genérico<br />
66
HERANÇA E POLIMORFIS MO<br />
}<br />
Exercícios<br />
}<br />
}<br />
System.out.println("olá.");<br />
1. Implemente uma superclasse Animal, e depois três subclasses: Mamíferos, Peixes e Aves.<br />
Depois crie um programa de testes que lhe permita criar instâncias dessas classes.<br />
2. Modifique as classes do exercício anterior, implementando um método que imprime na tela o som<br />
de cada animal criado a partir dessas classes. Por exemplo, um cachorro deve latir, uma ave<br />
deve gralhar e um peixe não deve fazer barulho.<br />
3. Altere o programa de testes do primeiro exercício para que ele crie aleatoriamente 10 animais, e<br />
depois imprima na tela o som de cada um deles, identificando se é um mamífero, um peixe ou<br />
uma ave.<br />
4. Aumente o número de classes de seu exercício, especializando ainda mais os tipos de animais<br />
representáveis por elas. Por exemplo, inclua uma classe Cachorro, subclasse de Mamífero, ou<br />
uma classe Acara, subclasse de Peixe. Depois vá especializando ainda mais, criando as classes<br />
Poodle e AcaraBandeira.<br />
5. Desenhe em um papel a hierarquia entre as classes que você criou no exercício anterior.<br />
67
Classes abstratas e interfaces<br />
Uma das contribuições maiores da Orientação a Objetos foi a estratificação funcional<br />
da mão de obra envolvida no desenvolvimento de um programa de computador. Hoje<br />
em dia os programadores não mais concebem e implementam seus códigos a partir de<br />
sua livre intuição - são orientados por sofisticados mecanismos de análise e modelagem<br />
de sistemas que freqüentemente geram esboços (esqueletos) de programas. Esses códigos<br />
incompletos representam a visão abstrata do sistema e dependendo do padrão de projeto<br />
utilizado podem ser fortemente baseados em interfaces.<br />
Classes abstratas e concretas<br />
Quando descrevemos uma hierarquia de classes, nem todas as classes são previstas de serem<br />
instanciadas. Por exemplo, observe a hierarquia de classes das figuras geométricas (vide a 2ª lista de<br />
exercícios):<br />
ABSTRATA<br />
CONCRETA<br />
Retangulo<br />
Quadrado<br />
Note que, apesar de Geometria ser uma classe, não faz muito sentido imaginar um "objeto geometria".<br />
No mundo real, sempre identificamos uma forma geométrica através de uma subclasse de Geometria:<br />
um quadrado, um triângulo, etc. Por isso dizemos que a classe Geometria é abstrata. De fato, o mais<br />
correto é dizer superclasse abstrata, pois a existência de objetos dessa classe sempre dependerá da<br />
implementação de alguma subclasse (concreta).<br />
Uma superclasse abstrata possui as seguintes características:<br />
Geometria<br />
Circulo<br />
12<br />
Triangulo<br />
Equilatero<br />
?? Não pode ser instanciada, mesmo que possua construtor (algumas linguagens, como <strong>Java</strong>,<br />
permitem a implementação de construtores em classes abstratas, mas não faz sentido, pois<br />
esses construtores não serão acessíveis).
TRATAMENTO DE EXCEÇÕ ES<br />
?? Pode possuir membros abstratos, ou seja, métodos cujo comportamento só será definido em<br />
suas subclasses.<br />
?? Pode possuir membros concretos, ou seja, métodos comuns - com o comportamento já previsto<br />
na própria classe abstrata.<br />
métodos abstratos só podem ser implementados em classes abstratas. Por outro lado, métodos<br />
concretos também podem ser implementados em classes abstratas.<br />
O motivo de implementarmos uma superclasse abstrata é permitirmos a representação correta da relação<br />
e hierarquia entre outras classes: concretas e/ou abstratas. No exemplo acima, se retirarmos a classe<br />
Geometria do diagrama, as demais classes continuarão a existir, mas não terão uma relação definida<br />
entre elas. Ou seja, poderemos construir objetos Quadrados, Triângulos, etc., mas não teremos tratar<br />
esses objetos como pertencentes a uma mesma superclasse.<br />
O modificador abstract<br />
Em <strong>Java</strong>, usa-se o modificador abstract para definir uma classe abstrata:<br />
/** Superclasse abstrata. */<br />
abstract class Geometria<br />
{<br />
abstract public double area();<br />
public abstract double perimetro();<br />
}<br />
Note que os métodos da classe Geometria foram declarados também abstratos. Isso significa que as<br />
subclasses de Geometria deverão implementar esses métodos ou então serem igualmente<br />
abstratas.<br />
não existe uma ordem rígida entre os modificadores de uma classe ou método. No exemplo acima,<br />
por exemplo, a ordem dos modificadores public e abstract foi digitada propositalmente diferente nos dois<br />
métodos. Isso foi apenas para exemplificar a liberdade que existe na ordem dos modificadores.<br />
Entretanto, é fundamental que o programador escolha uma única ordem de aplicar os modificadores,<br />
tornando o código mais elegante e legível.<br />
Existem subclasses abstratas ? sim, existem. Em alguns casos, subclasses de uma superclasse abstrata<br />
serão também abstratas. Por exemplo, imagine que a hierarquia Geometria vista anteriormente seja uma<br />
generalização entre geometria plana e geometria espacial:<br />
Plana<br />
Geometria<br />
69<br />
Espacial<br />
Retangulo Elipse Triangulo Cubo Esfera
TRATAMENTO DE EXCEÇÕ ES<br />
Nessa nova hierarquia, as classes Plana e Espacial também deverão ser abstratas, apesar de serem<br />
subclasses da classe Geometria.<br />
* O aluno deve exercitar bastante a idéia de generalização e especialização proposta pela hierarquia de<br />
classes. Essa é a idéia central do paradigma de Orientação a Objetos e é inspirada no que se acredita<br />
ser a base do pensamento humano.<br />
Restrição de herança pelo modificador final<br />
Nas seções anteriores, vimos o uso do modificador final para definirmos membros variáveis com valores<br />
constantes. Além de definir valores constantes, o modificador final pode ser usado para restringir a<br />
herança de classes e métodos:<br />
/** Classe final – que não pode ser extendida. */<br />
final class Quadrado extends Retângulo<br />
{<br />
// O construtor Quadrado é implicitamente final<br />
Quadrado(double lado)<br />
{<br />
super(lado, lado);<br />
}<br />
}<br />
O exemplo acima define a classe Quadrado como final, ou seja, nenhuma outra classe opderá estender a<br />
classe Quadrado. O construtor da classe não foi implementado com o modificador final. Apesar disso, o<br />
construtor é final porque todos os métodos de uma classe final são implicitamente final. Também<br />
podemos ter métodos final, ou seja, uma classe normal possuir um dos seus métodos modificados como<br />
final. Nesse caso, essa classe pode ser estendida, mas a subclasse não poderá sobrecarregar esse<br />
método final.<br />
As referências this e super<br />
Até aqui acessamos objetos através de referências explicitamente criadas no código dos programas.<br />
Entretanto, quando um objeto é instanciado na memória, são criadas duas referências implícitas: super e<br />
this. A referência super diz respeito à superclasse de um objeto e a referência this referencia o próprio<br />
objeto.<br />
Para referenciarmos a superclasse de um objeto usamos a palavra reservada super. Note no exemplo<br />
anterior, que o construtor da classe Quadrado chama um método super(lado, lado);. Isso significa que o<br />
construtor da classe Quadrado está chamando o construtor de sua superclasse Retângulo, e passando<br />
os argumentos exigidos por esse construtor.<br />
Outro exemplo de utilização da referência super é com o operador ponto:<br />
/** Classe final – que não pode ser extendida. */<br />
final class Quadrado extends Retângulo<br />
70
TRATAMENTO DE EXCEÇÕ ES<br />
{<br />
}<br />
Quadrado(double lado)<br />
{<br />
// Chamando o construtor da superclasse<br />
super(lado, lado);<br />
}<br />
public double area ()<br />
{<br />
// Chamando um método da superclasse com o uso do operador ponto<br />
return super.area();<br />
}<br />
O uso da referência this é justificado para definir o contexto de uma variável e também para permitir<br />
sobrecarga de construtores:<br />
/** Exemplo inspirado na hierarquia de classes apresentada na aula 11 */<br />
class Professor extends Funcionario<br />
{<br />
String nome;<br />
double salário;<br />
}<br />
Exercícios<br />
// Semi default<br />
Professor ()<br />
{<br />
// Acessando o construtor principal, passando parâmetros padrão<br />
this ("", 0.0);<br />
}<br />
// Construtor com argumentos<br />
Professor (String nome, double salario)<br />
{<br />
// A referência this é usada para distinguir o escopo das variáveis<br />
this.nome = nome;<br />
this.salario = salario;<br />
}<br />
h. Desenhe uma hierarquia de classes capaz de representar os animais de um zoológico,<br />
considerando as seguintes restrições:<br />
1. O zoológico deve ter pelo menos três zonas diferentes, por exemplo: zona dos felinos,<br />
das aves, dos répteis, etc.<br />
2. Cada seção deve ter pelo menos três tipos diferentes de animais.<br />
71
TRATAMENTO DE EXCEÇÕ ES<br />
3. Defina uma hierarquia de classes que inclua métodos relativos à alimentação dos<br />
animais, à locomoção, etc. Que permita a modelagem fiel das características de cada<br />
animal.<br />
i. Implemente as classes da hierarquia acima, incluindo o uso dos conceitos de classe abstrata, e<br />
as referências this e super.<br />
j. Implemente um programa de testes, que lhe permita criar um zoológico a partir da instanciação<br />
dos vários animais presentes na hierarquia anterior.<br />
72
Tratamento de exceções<br />
Quando usamos um software, esperamos que ele seja tolerante à falhas – pelo menos às falhas mais<br />
comuns relativas à sua função. Quando um programador identifica uma falha possível de ocorrer<br />
durante a execução de seus programas, ele pode codificar estratégias de tolerância e/ou correção desta<br />
falha, em um processo conhecido como tratamento de exceções.<br />
O que são exceções ?<br />
Uma exceção é um evento ou valor ilegal, porém previsto na modelagem de um sistema. Por exemplo,<br />
quando criamos um programa que pede ao usuário a digitação de um valor inteiro e esse usuário digita<br />
um valor fracionário, dizemos que o valor digitado pelo usuário irá gerar uma exceção. Entretanto, essa<br />
exceção pode, e deve, ser previsto na implementação desse programa.<br />
Diferença entre exceções e erros<br />
A diferença entre exceção e erro é que uma exceção é prevista pelo programador durante a<br />
implementação de um sistema, enquanto o erro é algo inesperado, não tratado e que, na maioria das<br />
vezes, aborta o sistema.<br />
Exceção:<br />
O usuário digitou um valor inválido. (previsível)<br />
Solução: a implementação de um tratamento<br />
para a exceção gerada pela digitação de um<br />
valor inválido.<br />
Erro:<br />
13<br />
O teclado sofreu algum avaria física e parou<br />
de enviar sinais ao computador. O usuário não<br />
pode mais digitar valores com o teclado.<br />
(imprevisível)<br />
Solução: não tem solução, pois um programa<br />
comum de computador não pode reparar um<br />
periférico de um computador – um teclado em<br />
curto, por exemplo. Um erro, caso seja<br />
detectado, geralmente causa o encerramento<br />
do programa que o detectou.<br />
os erros são sinalizados pelo sistema operacional no qual a máquina virtual <strong>Java</strong> está rodando. Ou<br />
seja, geralmente, a máquina virtual não permite o acesso de um código <strong>Java</strong> à origem de um erro (uma<br />
das funções do sandbox), tornando a tentativa de tratamento dos erros impossível.<br />
Um exemplo de exceção pode ser visto no fragmento de código abaixo, onde o programa tenta acessar<br />
um índice inexistente no array alunos:
TRATAMENTO DE EXCEÇÕ ES<br />
public class Sistema<br />
{<br />
}<br />
// Um cadastro com no máximo cinqüenta alunos<br />
private Aluno[] alunos = new Aluno[50];<br />
public void relatorio(int indice)<br />
{<br />
// Suponha que o argumento usado na chamado desse método<br />
// tenha sido maior do que 50. Isso geraria uma exceção.<br />
System.out.println(alunos[indice]);<br />
}<br />
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException<br />
at Sistema.relatorio(Sistema.java:10)<br />
No exemplo acima, o método relatório(); contém apenas um comando, o que tornaria possível a<br />
prevenção de exceções através de um comando de decisão:<br />
public void relatorio(int indice)<br />
{<br />
// Prevenção de falhas através de comandos de decisão<br />
if(indice>-1 && índice
TRATAMENTO DE EXCEÇÕ ES<br />
Tratamento de exceções em <strong>Java</strong><br />
O tratamento de exceções em <strong>Java</strong> segue o mesmo estilo de C++, visando um código mais tolerante a<br />
falhas. Existem três comandos básicos que permitem esse tratamento de exceções: try, catch e finally.<br />
Esses comandos são usados em conjunto, suprindo o programador de recursos que garantam o<br />
desenvolvimento de códigos robustos:<br />
?? try: é o bloco de comandos que são passíveis de gerarem uma exceção. Quando o programador<br />
sabe que um determinado conjunto de instruções pode gerar algum tipo de exceção, ele agrega<br />
esses comandos dentro de um bloco try. O código contido dentro de um bloco try é chamado de<br />
código protegido.<br />
?? catch: é o bloco de comandos alternativos ao try, ou seja, se os comandos do bloco try gerarem<br />
uma exceção então os comandos do bloco catch serão executados no lugar deles. O bloco catch<br />
é opcional, mas normalmente é implementado junto a um bloco try.<br />
?? finally: é um comando utilizado para delimitar um grupo de instruções que será sempre<br />
executada ao final de um bloco try-catch, independente de qual dos blocos anteriores foi<br />
processado pela máquina virtual.<br />
A lógica dos blocos acima descritos é simples:<br />
1. Tenta executar os comandos do bloco try<br />
2. Se os comandos do try geraram uma exceção, então executa os comandos do bloco catch. Um<br />
bloco de comandos catch sempre deve seguir um bloco de comandos try, e o programador deve<br />
ter o cuidado de garantir que o bloco catch não irá gerar uma exceção.<br />
3. Independente das exceções geradas na execução dos blocos try ou catch, os comandos do bloco<br />
finally serão executados.<br />
public void relatorio(int indice)<br />
{<br />
// Tratamento de exceções<br />
try<br />
{<br />
// Tenta executar esse comando<br />
System.out.println(alunos[indice]);<br />
}<br />
catch(ArrayIndexOutOfBoundsException erro)<br />
{<br />
// Se houver algum erro no bloco anterior,<br />
// esse comando será executado<br />
System.out.println("aluno inexistente");<br />
}<br />
catch(Exception erro)<br />
{<br />
// Pode-se usar um catch para cada tipo de erro<br />
// possível no bloco try<br />
System.out.println("exceção inesperada");<br />
}<br />
finally<br />
{<br />
// Esse comando será executado sempre<br />
System.out.println("executou finally");<br />
}<br />
}<br />
75
TRATAMENTO DE EXCEÇÕ ES<br />
Um exemplo mais comum do uso do bloco finally é quando o código do bloco try acessa recursos de<br />
entrada e saída do sistema, como uma conexão a um servidor, por exemplo. Nesse caso, se o bloco try<br />
abrisse uma sessão no servidor e depois gerasse uma exceção antes de fechar a sessão, essa sessão<br />
permaneceria aberta até algum timeout. No caso, o bloco finally teria o comando de encerramento de<br />
sessão http, garantindo que a sessão seria encerrada, independente de ter havido algum erro no bloco<br />
try ou não.<br />
o único caso em que os comandos do bloco finally não serão executados é quando um comando<br />
System.exit(); for executado em um dos blocos try ou catch.<br />
A hierarquia das exceções<br />
Observe que, no exemplo acima, que a exceção "capturada" no bloco catch é uma referência a um objeto<br />
da classe ArrayIndexOutOfBoundsException. De fato, todas as exceções em <strong>Java</strong> são objetos<br />
instanciados a partir da seguinte hierarquia:<br />
Onde:<br />
Throwable<br />
Exception Error<br />
RuntimeException<br />
Exemplo: hierarquia da classe ArrayIndexOutOfbounds.<br />
java.lang.Object<br />
|<br />
+--java.lang.Throwable<br />
|<br />
+--java.lang.Exception<br />
|<br />
+--java.lang.RuntimeException<br />
|<br />
+--java.lang.IndexOutOfBoundsException<br />
|<br />
+-java.lang.ArrayIndexOutOfBoundsException<br />
?? Error: Indica uma falha de difícil recuperação durante a execução de um programa. Um exemplo<br />
é a tentativa de instanciação de uma classe em uma máquina virtual que não possui mais<br />
memória livre. Não é previsto que um programa recupere esse tipo de falha, embora seja<br />
possível.<br />
?? RuntimeException: é usado para representar um problema de implementação ou de projeto de<br />
um sistema. Ou seja, é usado para indicar condições que jamais deveriam correr durante a<br />
execução normal do programa. Pelo fato de que esses erros jamais devem ocorrer em condições<br />
normais, o seu tratamento é feito em tempo de execução – a decisão sobre o melhor tratamento<br />
é deixado a cargo do usuário. Por exemplo, a tentativa de alocação de um objeto em uma<br />
máquina virtual cuja memória está totalmente ocupada retorna um erro de execução. O sistema<br />
normalmente avisa ao usuário que não há memória disponível, dando-lhe a chance de fechar<br />
outros aplicativos ou optar por outros comandos do programa.<br />
?? Exception: são erros plausíveis, previsíveis. Erros causados normalmente pelo contexto em que<br />
o programa será rodado, por exemplo: arquivo não encontrado, URL mal digitada, etc. Esses<br />
erros são normalmente gerados por falhas do usuário. Muitas vezes é complicado imaginar todas<br />
76
TRATAMENTO DE EXCEÇÕ ES<br />
as exceções possíveis quando o correto comportamento de um programa depende do usuário,<br />
mas tratar o maior número possível dessas exceções é uma obrigação do bom programador.<br />
se os métodos de um programa tratarem todas as exceções possíveis, dizemos que o programa é<br />
robusto. Evidentemente, nem sempre é possível prevenir todas as exceções, mas o grau de robusteza de<br />
um código tem uma relação direta com a qualidade desse código. O programador deve sempre<br />
programar pensando em "quanto mais robusto melhor".<br />
Tratamento pendente de exceções<br />
Caso um método possa gerar uma exceção, esse método deve deixar claro o tipo de ação que deve ser<br />
tomada para contornar essa exceção.<br />
Existem duas formas de fazer isso: um bloco try-catch, conforme visto anteriormente ou então a simples<br />
sinalização das exceções, deixando o seu tratamento para fora do trecho de código que gerou essa<br />
exceção.<br />
Para não misturar o tratamento de exceções com a lógica de um programa, uma técnica muito utilizada é<br />
a declaração de métodos com exceções pendentes, ou seja, cujo tratamento deverá ser feito pelo<br />
programa que chamar esse método.<br />
Em <strong>Java</strong>, essa pendência do tratamento de uma exceção é definida pela palavra reservada throws:<br />
// A palavra reservada throws é seguida pela lista de exceções que podem<br />
// ocorrer na execução do código do método. Esse método só poderá ser<br />
// chamado de dentro de um bloco try-catch, pois a pendência deverá ser<br />
// tratada no código que está chamando esse método.<br />
public void relatorio(int indice) throws ArrayIndexOutOfBoundsException<br />
{<br />
// A vantagem do tratamento de exceções pendentes é que o código dos<br />
// métodos fica restrito à lógica do programa.<br />
System.out.println(alunos[indice]);<br />
}<br />
Exceções implementadas pelo programador<br />
Como as exceções em <strong>Java</strong> são instâncias de alguma subclasse de Throwable, é possível implementar<br />
outras classes estendidas de Throwable ou de uma de suas subclasses. Essas subclasses<br />
implementadas pelo programador serão igualmente exceções.<br />
Tal qual vimos na aula 11, usaremos a palavra reservada extends para implementarmos novas exceções:<br />
public class AlunoInexistente extends Exception<br />
{<br />
// Esse método sobrecarrega o método homônimo da classe Exception<br />
public String getMessage()<br />
{<br />
return "indice do aluno não existe";<br />
}<br />
}<br />
77
TRATAMENTO DE EXCEÇÕ ES<br />
Após compilarmos o código acima, poderemos usar a nova exceção normalmente em outros programas:<br />
public void relatorio(int indice)<br />
{<br />
// Tratamento de exceções<br />
try<br />
{<br />
// Tenta executar esse comando<br />
System.out.println(alunos[indice]);<br />
}<br />
catch(AlunoInexistente erro)<br />
{<br />
// Se houver algum erro no bloco anterior,<br />
// esse comando será executado<br />
System.out.println(erro.getMessage());<br />
}<br />
}<br />
Sinalizando uma exceção (throw)<br />
O programador pode optar em alguns casos por "forçar" uma exceção, ou seja, sinalizar de uma exceção<br />
em determinados casos previamente conhecidos. Essa sinalização programada deve ser feita em<br />
métodos definidos com tratamento de exceções pendentes:<br />
public void relatorio(int indice) throws AlunoInexistente<br />
{<br />
if(indice>-1 && índice
TRATAMENTO DE EXCEÇÕ ES<br />
?? printStackTrace(): esse método imprime na tela as informações de depuração da exceção: o tipo<br />
de exceção, em que linha, em qual método e em qual classe a exceção foi gerada.<br />
?? getMessage(): é uma versão simplificada do printStackTrace(). Imprime na tela apenas a<br />
mensagem padrão da exceção que foi gerada. No caso da implementação de uma nova exceção<br />
pelo programador, é possível definir qual mensagem será gerada.<br />
public class Teste<br />
{<br />
static public void main(String[] args)<br />
{<br />
new Teste();<br />
}<br />
}<br />
Teste()<br />
{<br />
}<br />
Sistema s = new Sistema();<br />
// Tratamento de exceções<br />
try<br />
{<br />
s.relatório();<br />
}<br />
catch(AlunoInexistente erro)<br />
{<br />
// O método getMessage retorna uma String<br />
System.out.println(erro.getMessage());<br />
}<br />
catch(Exception erro)<br />
{<br />
// O método printStackTrace imprime direto na tela<br />
erro.printStackTrace();<br />
}<br />
finally<br />
{<br />
// Descartando o objeto Sistema<br />
s = null;<br />
}<br />
Exceções mais comuns<br />
À medida que o aluno for praticando o tratamento de exceções, e principalmente a herança de classe da<br />
API <strong>Java</strong>, ele passará a conhecer a série de exceções mais comuns da linguagem <strong>Java</strong>. Para facilitar<br />
essa familiaridade, algumas dessas exceções são descritas abaixo:<br />
?? ArithmeticException – problemas com operações numéricas, tipicamente divisão por zero:<br />
int i = 10 / 0;<br />
79
TRATAMENTO DE EXCEÇÕ ES<br />
?? NullPointerException – gerado pela tentativa de acessar um objeto através de uma<br />
referência nula, ou seja, antes do objeto ser instanciado.<br />
Image[] imagens = new Image[100];<br />
System.out.println(imagens[1]);<br />
?? NegativeArraySizeException – gerado pela tentativa de criar um array de tamanho<br />
negativo:<br />
Image[] imagens = new Image[-100];<br />
?? ArrayIndexOutOfBoundsException – gerado pela tentativa de acesso a um índice fora dos<br />
limites de um array:<br />
Image[] imagens = new Image[100];<br />
System.out.println(imagens[200]);<br />
?? SecurityException – gerado pelo sandbox, quando um programa tenta acessar um recurso<br />
que a máquina virtual protege. Por exemplo, o gerenciador de segurança da JVM gera uma<br />
exceção desse tipo para applets* que tentam:<br />
o Acessar um arquivo local<br />
o Abrir uma conexão socket diferente da conexão a qual o applet está vinculado<br />
o Executar outro programa dentro do ambiente de execução (Runtime).<br />
* veremos applets mais tarde na disciplina.<br />
Exercícios<br />
k. Reescreva os exercícios sobre OO, usando o tratamento de exceções para garantir a robusteza<br />
dos programa.<br />
80
14<br />
Interface gráfica com o usuário<br />
Controlando o posicionamento de componentes visuais em programas que possuem interface gráfica com o<br />
A API <strong>Java</strong> conta com um pacote chamado AWT – Abstract Window Toolkit, que permite ao<br />
desenvolvedor criar interfaces gráficas com o usuário (GUI – Graphical User Interface) a partir da<br />
instanciação ou especialização de classes de componentes gráficos. As versões atuais do jdk contém um<br />
grande número de componentes gráficos que serão enumerados em sala de aula. Nesta seção, o aluno<br />
encontra as diretrizes básicas da construção de interfaces gráficas com <strong>Java</strong>.<br />
Componentes gráficos – o pacote AWT<br />
Componentes gráficos são classes que contém membros associados a informações visuais, como cores,<br />
dimensões e bordas. Quando pensamos em componentes gráficos, estamos pensando no usuário – não<br />
faz sentido criar um programa com interface gráfica se ele não tiver interação com um ser humano.<br />
Aspectos de ergonomia de software são importantes, tais como combinação de cores e fontes utilizadas<br />
na interface gráfica. As principais classes do pacote AWT são apresentadas na figura abaixo:<br />
Dimension<br />
Font<br />
Insets<br />
FontMetrics<br />
Point<br />
Graphics<br />
Image<br />
Color<br />
MenuComponent<br />
package java.awt<br />
Component<br />
BorderLayout<br />
GridBagConstraints<br />
FlowLayout<br />
Toolkit<br />
GridLayout<br />
GridBagLayout<br />
CardLayout<br />
Polygon<br />
Rectangle
INTERFACE GRÁFICA CO M O USUÁRIO<br />
A classe java.awt.Component, representada na elipse mais abaixo do diagrama acima, representa<br />
um importante papel na construção de interfaces gráficas: é a superclasse dos componentes visuais<br />
AWT. O próximo diagrama mostra a hierarquia da classe Component e suas especializações.<br />
Button<br />
Frame<br />
Canvas<br />
Checkbox<br />
Window<br />
Dialog<br />
Container<br />
java.awt.Component<br />
Panel<br />
Os detalhes do uso de cada um desses componentes serão apresentados em aula a partir do exemplo<br />
abaixo. Observe que alguns componentes têm duas ou mais classificações. Isto ocorre porque o<br />
componente pertence à todas as classes de sua hierarquia, a partir da raiz – lembre-se que todos as<br />
classes em <strong>Java</strong> são especializações de Object.<br />
Button<br />
TextArea<br />
Scrollbar<br />
Container<br />
FileDialog<br />
Window<br />
Label<br />
82<br />
Applet<br />
Choice<br />
Frame<br />
Label TextComponent<br />
TextField<br />
TextComponent<br />
List<br />
Scrollbar<br />
TextArea TextField<br />
Choice<br />
List<br />
Checkbox<br />
Panel<br />
CheckboxGroup
INTERFACE GRÁFICA CO M O USUÁRIO<br />
Código do exemplo de componentes AWT<br />
import java.awt.*;<br />
import java.awt.event.*;<br />
/**<br />
* Classe demonstrativa de componente gráficos AWT<br />
* XI Semana da Tecnologia da Informação - INSOFT<br />
*<br />
* @author Felipe Gaúcho<br />
* @version demo<br />
*/<br />
public class ComponentesAWT extends Frame implements WindowListener {<br />
/** Botão */<br />
private Button botao = new Button("INSOFT");<br />
/** Checkbox - botão de seleção */<br />
private Checkbox checkbox = new Checkbox("Selecione esta caixa");<br />
/** Os botões de opção podem ser organizados em grupo */<br />
private CheckboxGroup grupoDeOpcoes = new CheckboxGroup();<br />
/**<br />
* Choice - Caixa de seleção. Observe que o choice será inicializados<br />
* posteriormente, pois depende de vários comandos para ser construído.<br />
*/<br />
private Choice choice = null;<br />
/**<br />
* List - uma lista de opções, semelhante ao Choice porém com a caixa de<br />
* opções fixa. Este componente também não pode ser totalmente construído<br />
* totalmente com uma linha de código.<br />
*/<br />
private List list = null;<br />
/**<br />
* Label - um componente não editável de texto, usado normalmente como<br />
* título de outros componentes.<br />
*/<br />
private Label label = new Label("digite o seu nome: ");<br />
/** TextField - campo de entrada de textos. */<br />
private TextField nome = new TextField();<br />
/** TextArea - área para edição de textos. */<br />
private TextArea texto = new TextArea("");<br />
/** Panel - um painel que pode conter componentes gráficos */<br />
private Panel painel1 = new Panel();<br />
/** Panel - um painel que pode conter componentes gráficos */<br />
private Panel painel2 = new Panel();<br />
83
INTERFACE GRÁFICA CO M O USUÁRIO<br />
static public void main(String[] args) {<br />
ComponentesAWT gui = new ComponentesAWT();<br />
gui.setSize(400, 300);<br />
gui.setVisible(true);<br />
gui.addWindowListener(gui);<br />
}<br />
/** Construtor da interface gráfica */<br />
ComponentesAWT() {<br />
super("XI Semana da Tecnologia - INSOFT");<br />
// Inicializando um componente Choice:<br />
choice = new Choice();<br />
choice.add("segunda");<br />
choice.add("terça");<br />
choice.add("quarta");<br />
choice.add("quinta");<br />
choice.add("sexta");<br />
// Inicializando um componente List: você diz o número de opções<br />
// que devem aparecer e se o componente aceita seleção múltipla<br />
list = new List(3, false);<br />
list.add("Brazil");<br />
list.add("Canada");<br />
list.add("Chile");<br />
list.add("Dinamarca");<br />
list.add("Espanha");<br />
painel1.setLayout(new GridLayout(2, 2, 3, 3));<br />
painel1.add(botao);<br />
painel1.add(list);<br />
painel1.add(texto);<br />
// Exemplo de aninhamento de containers:<br />
Panel container1 = new Panel();<br />
container1.setLayout(new FlowLayout());<br />
container1.add(new Checkbox("iniciante", grupoDeOpcoes, true));<br />
container1.add(new Checkbox("avançado", grupoDeOpcoes, false));<br />
Panel container2 = new Panel();<br />
container2.setLayout(new GridLayout(2, 1));<br />
container2.add(checkbox);<br />
container2.add(container1); // Veja, um container dentro de outro<br />
painel1.add(container2);<br />
painel2.setLayout(new BorderLayout(2, 2));<br />
painel2.add(label, BorderLayout.WEST);<br />
painel2.add(nome, BorderLayout.CENTER);<br />
84
INTERFACE GRÁFICA CO M O USUÁRIO<br />
}<br />
}<br />
this.setLayout(new BorderLayout(2, 2));<br />
add(choice, BorderLayout.NORTH);<br />
add(painel2, BorderLayout.SOUTH);<br />
add(painel1, BorderLayout.CENTER);<br />
/**<br />
* Executado quando a janela se torna ativa no contexto do gerenciador de janelas<br />
* do sistema operacional, ou seja, a janela se tornou o foco de interação com<br />
* o usuário. Isto acontece no Windows quando uma janela é selecionada pelo<br />
* usuário.<br />
*/<br />
public void windowActivated(WindowEvent e) { }<br />
/** Executado após o fechamento da janela */<br />
public void windowClosed(WindowEvent e) { }<br />
/**<br />
* Executado quando o usuário requisita o fechamento da janela<br />
*/<br />
public void windowClosing(WindowEvent e) {<br />
// Esta linha representa a saída padrão de um programa.<br />
System.exit(0);<br />
}<br />
/**<br />
* Executado quando a janela deixa de ser o foco de interação<br />
* com o usuário.<br />
*/<br />
public void windowDeactivated(WindowEvent e) { }<br />
/**<br />
* Executado quando a janela recupera o foco de interação<br />
* com o usuário, normalmente quando o usuário retorna a aplicação de<br />
* minimizada para normal.<br />
*/<br />
public void windowDeiconified(WindowEvent e) { }<br />
/** Executado quando a janela for minimizada */<br />
public void windowIconified(WindowEvent e) { }<br />
/** Executadoapós a abertura da janela */<br />
public void windowOpened(WindowEvent e) { }<br />
85
INTERFACE GRÁFICA CO M O USUÁRIO<br />
Gerenciadores de Layout<br />
Uma das principais características da linguagem <strong>Java</strong> é a sua portabilidade, que dispensa os<br />
desenvolvedores de preocupações com aspectos de hardware. Interfaces gráficas, entretanto, possuem<br />
dependência dos dispositivos nos quais serão exibidas – a resolução, cores e suporte a eventos são<br />
exemplos de aspectos relevantes em um projeto que envolve interface gráfica com o usuário (GUI). Na<br />
maioria das linguagens, o programador define previamente a aparência da GUI, incluindo o tamanho e<br />
posicionamento dos componentes, e este aspecto é fixo e imutável a menos que haja uma mudança no<br />
código.<br />
Imagine um programa codificado para rodar em um monitor com resolução de 800 x 600 pixels sendo<br />
executado em um monitor de apenas 640 x 400 pixels. Provavelmente isto acarretará problemas de<br />
posicionamento dos componentes ou eventualmente a perda de visibilidade destes componentes.<br />
Linguagens compiladas como C++ ou Delphi exigem que o programador saiba de antemão as<br />
características de hardware para os quais ele está programando, ou então adotar estratégias de<br />
verificação destas características no momento da abertura ou instalação dos programas – o que agrega<br />
complexidade ao algoritmo e reduz a a portabilidade dos programas.<br />
Em <strong>Java</strong> não tratamos o posicionamento e dimensionamento dos componentes gráficos rígidamente,<br />
mas sim através de processos independentes chamados de gerenciadores de layout. Isto permite que um<br />
código gerado no sistema operacional Windows, em uma resolução alta, seja executado sem perda de<br />
forma ou função em outros sistemas operacionais, como Linux ou Machintosh – ou até mesmo em<br />
dispositivos especiais, como Palms ou telefones celulares.<br />
Containers<br />
Interfaces gráficas em <strong>Java</strong> são construídos a partir da idéia de containers, repositórios de componentes<br />
gráficos que possuem um processo gerenciando a disposição e dimensão destes componentes. O<br />
interpretador <strong>Java</strong> requisita ao sistema operacional uma região de vídeo e, a partir desta região, calcula a<br />
dimensão que cada componente deve assumir em tempo de execução – esta organização é chamada de<br />
Layout (disposição em inglês). Este processo de gerenciamento é transparente ao usuário e<br />
normalmente só é executado na primeira vez em que o programa entra em execução ou após um<br />
redimensionamento da janela. Abaixo exemplificaremos os principais gerenciadores de layout para<br />
conteiners AWT:<br />
o FlowLayout<br />
o CardLayout<br />
o GridLayout<br />
o BorderLayout<br />
o GridBagLayout<br />
86
INTERFACE GRÁFICA CO M O USUÁRIO<br />
Flow layout<br />
Exemplo de gerenciador de componentes baseado em fluxo:<br />
java.awt.FlowLayout<br />
idéia: adicionar componentes da esquerda para a direita, centralizados em<br />
realção à largura do container. Caso os componentes ultrapassem a largura<br />
do container, então o gerenciador adiciona os comopnentes excedentes na<br />
linha de baixo, e assim sucessivamente. É como se a interface fosse<br />
formada por várias linhas, e existisse uma corrente formada por<br />
componentes (uma fila). Cada novo componente adicionado à interface é<br />
um novo elo dessa corrente, que fica distribuída sobre as linhas da<br />
interface. Componente1 Componente2 Componente3 ...<br />
/**<br />
* @author Felipe Gaúcho<br />
* @version exemplo.SOO-I<br />
*/<br />
public class Fluxo extends Frame<br />
{<br />
Fluxo()<br />
{<br />
// Ajusta o gerenciador de layouts baseado em corrente de componentes<br />
setLayout(new FlowLayout());<br />
}<br />
}<br />
// Criando três componentes gráficos (botões)<br />
Button botao1 = new Button("botão 1");<br />
Button botao2 = new Button("botão 2");<br />
Button botao3 = new Button("botão 3");<br />
// adicionando os componentes<br />
add(botao1);<br />
add(botao2);<br />
add(botao3);<br />
// Tornando a janela (o Frame) visível<br />
setSize(200,200);<br />
setVisible(true);<br />
/** disparador da aplicação */<br />
static public void main(String[] args)<br />
{<br />
new Fluxo();<br />
}<br />
87
INTERFACE GRÁFICA CO M O USUÁRIO<br />
CardLayout<br />
Exemplo de gerenciador de cartões:<br />
java.awt.CardLayout<br />
idéia:<br />
import java.awt.*;<br />
import java.awt.event.*;<br />
Cartão 1<br />
Cartão 2<br />
Cartão 3<br />
/** * O gerenciador de cartões atua como se existisse uma pilha de cartões, cada cartão sendo<br />
* um container. O usuário somente pode observar/manipular o cartão<br />
* do topo da pilha. Cada cartão tem um nome, definido pelo programador<br />
* Para se trocar de cartão, usa-se o comando 'show()', conforme mostra o código abaixo.<br />
*<br />
* @author Felipe Gaúcho<br />
* @version exemplo.SOO-I<br />
*/<br />
public class Cartao extends Frame<br />
{<br />
private String[] paineis = {"first", "second", "third"};<br />
int painelAtual = 0;<br />
private Button proximo = new Button("próximo");<br />
private Button anterior = new Button("anterior");<br />
private CardLayout gerenciador = new CardLayout();<br />
private Panel painelCentral = new Panel();<br />
Cartao()<br />
{<br />
// chamando o construtor da superclasse Frame<br />
super("exemplo de CardLayout");<br />
painelCentral.setLayout(gerenciador);<br />
Panel painel1 = new Panel();<br />
Label textoDoPainel1 = new Label("PAINEL 1", Label.CENTER);<br />
painel1.setBackground(new Color(0, 200, 148));<br />
painel1.add(textoDoPainel1);<br />
88
INTERFACE GRÁFICA CO M O USUÁRIO<br />
Panel painel2 = new Panel();<br />
Label textoDoPainel2 = new Label("PAINEL 2", Label.CENTER);<br />
painel2.setBackground(Color.blue);<br />
painel2.add(textoDoPainel2);<br />
painel2.add(new Button("outro componente"));<br />
painel2.add(textoDoPainel2);<br />
Panel painel3 = new Panel();<br />
Label textoDoPainel3 = new Label("PAINEL 3", Label.CENTER);<br />
painel3.setBackground(Color.green);<br />
painel3.add(textoDoPainel3);<br />
painel3.add(new Choice());<br />
painelCentral.add(painel1, "first");<br />
painelCentral.add(painel2, "second");<br />
painelCentral.add(painel3, "third");<br />
Panel controles = new Panel();<br />
controles.add(anterior);<br />
controles.add(proximo);<br />
setLayout(new BorderLayout());<br />
add(painelCentral, BorderLayout.CENTER);<br />
add(controles, BorderLayout.SOUTH);<br />
// Programação inline é altamente desaconselhável. Só foi usada aqui<br />
// para o exemplo não ficar muito grande. O ideal é declarar uma classe<br />
// que implemente o controlador de eventos.<br />
proximo.addActionListener(<br />
new ActionListener()<br />
{<br />
public void actionPerformed(ActionEvent evento)<br />
{<br />
mostrarProximoPainel();<br />
}<br />
}<br />
);<br />
// Programação inline é altamente desaconselhável. Só foi usada aqui<br />
// para o exemplo não ficar muito grande. O ideal é declarar uma classe<br />
// que implemente o controlador de eventos.<br />
anterior.addActionListener(<br />
new ActionListener()<br />
{<br />
public void actionPerformed(ActionEvent evento)<br />
{<br />
mostrarPainelAnterior();<br />
}<br />
}<br />
);<br />
// Tornando a janela (o Frame) visível<br />
89
INTERFACE GRÁFICA CO M O USUÁRIO<br />
}<br />
}<br />
setSize(300, 275);<br />
setVisible(true);<br />
public void mostrarProximoPainel()<br />
{<br />
if(painelAtual< 2)<br />
{<br />
painelAtual++;<br />
gerenciador.show(painelCentral, paineis[painelAtual]);<br />
}<br />
}<br />
public void mostrarPainelAnterior()<br />
{<br />
if(painelAtual>0)<br />
{<br />
painelAtual--;<br />
gerenciador.show(painelCentral, paineis[painelAtual]);<br />
}<br />
}<br />
/** disparador da aplicação */<br />
static public void main(String[] args)<br />
{<br />
new Cartao();<br />
}<br />
90
INTERFACE GRÁFICA CO M O USUÁRIO<br />
BorderLayout<br />
Exemplo de gerenciador de componentes baseado em moldura:<br />
java.awt.BorderLayout<br />
idéia: considerar a interface como uma moldura, dividida em<br />
cinco partes:<br />
uma borda superior, uma borda inferior, uma borda esquerda,<br />
uma borda direita e uma área central.<br />
A área central prevalece sobre as demais quando esta<br />
"moldura" for redimensionada, ou seja, o componente que está<br />
no centro da interface é redimensionado em igual proporção ao<br />
redimensionamento do container enquanto os demais<br />
componentes apenas preenchem os espaços que forem<br />
adicionados em suas respectivas bordas.<br />
/**<br />
* Observe que o componente que está no centro da interface é o "âncora", ou seja<br />
* este componente ocupará sempre a maior parte da interface. Os demais serão também<br />
* redimensionados, mas sempre respeitando o espaço do componente central.<br />
*<br />
* @author Felipe Gaúcho<br />
* @version exemplo.SOO-I<br />
*/<br />
public class Borda extends Frame<br />
{<br />
Borda()<br />
{<br />
// chamando o construtor da superclasse Frame<br />
super("exemplo de BorderLayout");<br />
// Ajusta o gerenciador de layouts para "grade"<br />
// setLayout(new GridLayout(ESPACO_ENTRE_COLUNAS, ESPACO_ENTRE_LINHAS));<br />
setLayout(new BorderLayout(2, 2));<br />
// Item central da interface<br />
TextArea entradaDeTexto = new TextArea("escreva aqui o seu texto...");<br />
add(entradaDeTexto, BorderLayout.CENTER);<br />
// Item da borda superior da interface<br />
Choice escolha = new Choice();<br />
escolha.addItem("texto jurídico");<br />
escolha.addItem("poesia");<br />
escolha.addItem("tese de mestrado");<br />
add(escolha, BorderLayout.NORTH);<br />
// Item da borda direita da interface<br />
91
INTERFACE GRÁFICA CO M O USUÁRIO<br />
}<br />
}<br />
Label leste = new Label("Leste");<br />
add(leste, BorderLayout.EAST);<br />
// Item da borda esquerda da interface<br />
Label oeste = new Label("Oeste");<br />
add(oeste, BorderLayout.WEST);<br />
// Item da borda inferior da interface<br />
CheckboxGroup grupoDeSeletores = new CheckboxGroup();<br />
Checkbox opcao1 = new Checkbox("itálico", grupoDeSeletores, false);<br />
Checkbox opcao2 = new Checkbox("negrito", grupoDeSeletores, false);<br />
Checkbox opcao3 = new Checkbox("normal", grupoDeSeletores, true);<br />
// Alterando a fonte de componentes AWT<br />
opcao1.setFont(new Font("Roman", Font.ITALIC, 14));<br />
opcao2.setFont(new Font("Roman", Font.BOLD, 14));<br />
opcao3.setFont(new Font("Roman", Font.PLAIN, 14));<br />
Panel painelInferior = new Panel();<br />
painelInferior.add(opcao1);<br />
painelInferior.add(opcao2);<br />
painelInferior.add(opcao3);<br />
add(painelInferior, BorderLayout.SOUTH);<br />
// Tornando a janela (o Frame) visível<br />
setSize(300, 275);<br />
setVisible(true);<br />
/** disparador da aplicação */<br />
static public void main(String[] args)<br />
{<br />
new Borda();<br />
}<br />
92
INTERFACE GRÁFICA CO M O USUÁRIO<br />
GridLayout<br />
Exemplo de grade de componentes:<br />
java.awt.GridLayout<br />
idéia: criar uma grade na qual os componentes são<br />
adicionados, da esquerda para a direita e de cima para<br />
baixo.<br />
?? Todos os componentes da grade são redimensionados junto com as células.<br />
?? Todas as células possuem o mesmo tamanho.<br />
/**<br />
* Observe que as células do layout em forma de grade ocupam sempre o<br />
* mesmo espaço, e os componentes são redimensionados junto com as células<br />
*<br />
* @author Felipe Gaúcho<br />
* @version exemplo.SOO-I<br />
*/public class Grade extends Frame<br />
{<br />
Grade()<br />
{<br />
// chamando o construtor da superclasse Frame<br />
super("exemplo  de  GridLayout");<br />
}<br />
// Ajusta o gerenciador de layouts para "grade"<br />
// setLayout(new GridLayout(NUMERO_DE_LINHAS, NUMERO_DE_COLUNAS,<br />
// ESPACO_ENTRE_COLUNAS, ESPACO_ENTRE_LINHAS));<br />
setLayout(new GridLayout(2, 2, 3, 2));<br />
// Criando três componentes gráficos (botões)<br />
Button botao1 = new Button("botão  1");<br />
Button botao2 = new Button("botão  2");<br />
Label label1 = new Label("nome:", Label.RIGHT);<br />
TextField campo = new TextField("campo  de  entrada");<br />
// adicionando os componentes<br />
add(botao1);<br />
add(botao2);<br />
add(label1);<br />
add(campo);<br />
// Tornando a janela (o Frame) visível<br />
setSize(300, 75);<br />
setVisible(true);<br />
}<br />
/** disparador da aplicação */<br />
static public void main(String[] args) {<br />
new Grade();<br />
}<br />
93
INTERFACE GRÁFICA CO M O USUÁRIO<br />
GridBagLayout<br />
Exemplo de gerenciador de grade assimétrica de componentes:<br />
java.awt.GridBagLayout<br />
idéia: segue a mesma idéia adicionar os componentes a<br />
uma grade, porém uma grade assimétrica, ou seja, o<br />
espaço ocupado pelos componentes não é<br />
obrigatoriamente igual.<br />
Para fazer esse agrupamento assimétrico de<br />
componentes, o gerenciador GridbagLayout adota um<br />
conjunto de restrições a cada componente. Essas<br />
restrições são definidas pelo programador em um objeto<br />
da classe: java.awt.GridBagConstraints.<br />
O gerenciamento assimétrico de componentes se baseia na idéia que existem as dimensões do espaço<br />
disponível para a visualização de um determinado componente e as dimensões desse componente.<br />
Esses dois espaços não são necessariamente iguais e, dependendo do caso, o gerenciador deve<br />
calcular de que forma o espaço disponível será preenchido pela representação gráfica do componente.<br />
O conjunto de restrições reconhecido pelo gerenciador GridBagLayout é enumerado abaixo:<br />
Variáveis de instância da classe java.awt.GridBagConstraints:<br />
?? anchor: define onde o componente será posicionado dentro da área disponível para ele:<br />
GridBagConstraints.CENTER, GridBagConstraints.NORTH, GridBagConstraints.NORTHEAST,<br />
GridBagConstraints.EAST, GridBagConstraints.SOUTHEAST, GridBagConstraints.SOUTH,<br />
GridBagConstraints.SOUTHWEST, GridBagConstraints.WEST e<br />
GridBagConstraints.NORTHWEST.<br />
?? gridx e gridy: indica a célula onde o componente deverá ser inserido, onde os valores gridx=0 e<br />
gridy=0 indicam a linha e coluna dessa célula – lembre-se que o GridbagLayout, mesmo<br />
assimétrico, continua sendo um grid e, portanto, utiliza a idéia de células. Use<br />
GridBagConstraints.RELATIVE para indicar que o componente deve ser colocado à direita da<br />
posição atual ou abaixo da posição atual.<br />
?? gridwidth e gridheight: indica o número de células em uma linha da grade e quantas linhas serão<br />
ocupadas por um componente. O valores padrão para essas variáveis é 1. Use<br />
GridBagConstraints.REMAINDER para indicar que o componente será o último de uma linha e<br />
GridBagConstraints.RELATIVE para indicar que o componente deve preencher a linha até o<br />
próximo e último componente.<br />
?? fill: usado quando a área de visualização do componente é maior que o tamanho do componente.<br />
Nesse caso, o programador pode indicar de que forma o componente deve ser visualizado:<br />
o GridBagConstraints.NONE – não muda o tamanho do componente (padrão)<br />
o GridBagConstraints.HORIZONTAL – preenche todo o espaço horizontal disponível com o<br />
componente.<br />
o GridBagConstraints.VERTICAL – preenche todo o espaço vertical disponível com o<br />
componente.<br />
o GridBagConstraints.BOTH – preenche todo o espaço disponível com o componente,<br />
horizontal e vertical.<br />
?? ipadx e ipady: define o espaçamento entre as dimensões previstas de um componente e as<br />
dimensões reais, mostradas na tela – uma borda interna e invisível do componente. A largura do<br />
componente será, no máximo, a sua largura menos ipadx*2 (uma vez que a borda interna é aplica<br />
94
INTERFACE GRÁFICA CO M O USUÁRIO<br />
aos dois lados do componente). Similarmente, a altura do componente será a sua altura menos<br />
ipady*2.<br />
?? insets: é parecido com o ipad, porém define a borda externa do componente, ou seja, o<br />
espaçamento entre o componente e o espaço disponível para a sua visualização.<br />
?? weightx e weighty: determina o "peso" dos componentes, ou seja, como eles se comportarão<br />
durante o redimensionamento do container gerenciado pelo GridBagLayout. Quando o container for<br />
redimensionado, o gerenciador calcula as novas dimensões dos componentes de acordo com o<br />
seu peso. É necessário indicar o peso de pelo menos um componente em cada linha, caso<br />
contrário, todos os componentes ficarão centralizados na linha, sem peso.<br />
/**<br />
* Exemplo de GridBagLayout<br />
* @author Felipe Gaúcho<br />
* @version exemplo SOO-I<br />
*/<br />
public class GradeAssimetrica extends Frame<br />
{<br />
/**<br />
* Método que adiciona componentes na interface<br />
* @param componente O componente a ser adicionado à interface<br />
* @param gerenciador O gerenciador de layouts utilizado<br />
* @param restricoes O objeto com as restrições associadas ao componente<br />
*/<br />
private void adicionarComponente(Component componente,<br />
GridBagLayout gerenciador,GridBagConstraints restricoes)<br />
{<br />
// Registra os restrições do componente no gerenciador de layouts<br />
gerenciador.setConstraints(componente, restricoes);<br />
add(componente); // Adiciona o componente<br />
}<br />
GradeAssimetrica()<br />
{<br />
super("FIC - exemplo de GridbagLayout");<br />
GridBagLayout gerenciador = new GridBagLayout();<br />
GridBagConstraints restricoes = new GridBagConstraints();<br />
// Define o objeto 'gerenciador' como o gerenciador de componentes da Janela<br />
this.setLayout(gerenciador);<br />
// GridBagConstraints.BOTH faz o componente ocupar toda a área disponível<br />
// horizontal e vertical.<br />
restricoes.fill = GridBagConstraints.BOTH;<br />
restricoes.weightx = 0.5; // Componente com peso horizontal 0.5x<br />
adicionarComponente(new Label("nome: "), gerenciador, restricoes);<br />
// Componente ocupando a linha até o final - REMAINDER = nova linha<br />
restricoes.gridwidth = GridBagConstraints.REMAINDER;<br />
restricoes.weightx = 3.0; // Componente com peso horizontal 2x<br />
adicionarComponente(new TextField( ), gerenciador, restricoes);<br />
// O RELATIVE faz o componente ocupar a linha até o próximo componente<br />
95
INTERFACE GRÁFICA CO M O USUÁRIO<br />
}<br />
}<br />
restricoes.gridwidth = GridBagConstraints.RELATIVE;<br />
restricoes.weightx = 0.0; // Componente com peso padrão - igual aos outros<br />
restricoes.weighty = 3.0;<br />
adicionarComponente(new Button("botão 1"), gerenciador, restricoes);<br />
// depois de um RELATIVE sempre deve vir um REMAINDER<br />
restricoes.gridwidth = GridBagConstraints.REMAINDER;<br />
adicionarComponente(new Button("botão 2"), gerenciador, restricoes);<br />
restricoes.gridwidth = 1; // recupera os padrões de alocação de espaço<br />
restricoes.gridheight = 2; // na grade<br />
restricoes.weightx = 0.0;<br />
restricoes.weighty = 1.0;<br />
Button botao3 = new Button("botão 3");<br />
botao3.setBackground(Color.cyan);<br />
adicionarComponente(botao3, gerenciador, restricoes);<br />
adicionarComponente(new Button("botão 4"), gerenciador, restricoes);<br />
adicionarComponente(new Button("botão 5"), gerenciador, restricoes);<br />
adicionarComponente(new Button("botão 6"), gerenciador, restricoes);<br />
restricoes.gridwidth = GridBagConstraints.REMAINDER;<br />
// Adicionando um container dentro do de uma célula<br />
Panel painel = new Panel();<br />
painel.setLayout(new GridLayout(2,2,2,2));<br />
painel.add(new Label("campo 1"));<br />
painel.add(new TextField());<br />
painel.add(new Label("campo 2"));<br />
painel.add(new TextField());<br />
adicionarComponente(painel, gerenciador, restricoes);<br />
restricoes.weighty = 1.7;<br />
restricoes.fill = GridBagConstraints.BOTH;<br />
Label fic = new Label("FACULDADE INTEGRADA DO CEARÁ", Label.CENTER);<br />
fic.setBackground(Color.black);<br />
fic.setForeground(Color.white);<br />
fic.setFont(new Font("Serif", Font.BOLD, 16));<br />
adicionarComponente(fic, gerenciador, restricoes);<br />
setSize(375,300);<br />
setVisible(true);<br />
static public void main(String[] args)<br />
{<br />
new GradeAssimetrica();<br />
}<br />
96
Applets<br />
Um dos maiores apelos da linguagem <strong>Java</strong> está na facilidade de imlpementação de programas executáveis<br />
através da Internet. Tais programas rodam em qualquer sistema operacional que possua um navegador<br />
web, e são conhecidos como Applets.(do inglês Application Let)<br />
O que é um Applet ? (java.awt.Applet)<br />
Um applet é um programa <strong>Java</strong> que pode ser executado via Internet, através de um browser. A principal<br />
diferença entre um applet e os demais programas implementados em <strong>Java</strong> é a forma como esses<br />
programas serão inicializados. Em uma aplicação <strong>Java</strong> usamos o método main() para inicializar a<br />
aplicação, enquanto nos applets esse processo de inicialização é um pouco mais complexo.<br />
Devido ao fato de um applet rodar dentro de um browser, ele não pode ser executado diretamente por<br />
linha de comando, tal qual uma aplicação <strong>Java</strong>. Ao invés disso, devemos criar um código HTML que<br />
contém informações sobre o código a ser carregado e executado pela máquina virtual contida no browser<br />
– os navegadores web mais populares geralmente contém uma máquina virtual embutida em seu código.<br />
Quando o navegador reconhece que o código html que ele está interpretando possui uma referência a<br />
um <strong>Java</strong> applet, ele ativa essa máquina virtual para executar esse applet.<br />
existem alguns detalhes sobre a compatibilidade de applets em navegadores que serão melhores<br />
apresentados em sala de aula. A maioria desses detalhes refere-se a questões comerciais polêmicas e<br />
de versões de produtos envolvendo a SUN, fabricante do <strong>Java</strong>, e as fabricantes dos navegadores:<br />
Netscape, Microsoft, etc. Ao aluno é importante apenas a informação de que para um applet ser<br />
executado por algum navegador web, esse navegador deve ter alguma máquina virtual compatível com<br />
<strong>Java</strong> dentro dele (ou acessível por ele).<br />
Outro detalhe importante a ser observado é que applets são programas completos de computador, e<br />
não scripts a serem executados por um servidor ou interpretados pelo browser, como <strong>Java</strong>Script, Asp e<br />
scripts interpretados por CGIs. Quando uma página Html que contém um applet é lida pelo browser, todo<br />
o código do Applet é carregado na memória da JVM do browser, para daí então ser executado. Quando<br />
implementamos um applet devemos lembrar que todo o seu código deverá ser transmitido via Internet<br />
para que ele possa rodar, logo, o programador deve ter o cuidado de não criar applets muito grandes.<br />
Restrições de segurança em applets<br />
15<br />
Pelo fato de serem distribuídos via Internet, os applets representam aplicações potencialmente perigosas<br />
para o usuário. Imagine um applet que leia o seu disco rígido e, sempre que encontrar um arquivo<br />
contendo senhas, o transmita para o endereço de algum hacker. Esse hacker poderia usar esse arquivo<br />
para descobrir suas senhas pessoais e então fazer um grande estrago com isso.<br />
Para prevenir tais problemas, a máquina virtual <strong>Java</strong> exerce um controle rígido de acesso ao sistema<br />
operacional quando executa applets, através da classe SecrityManager. Esse controle, realizado
APPLETS<br />
através do já citado modelo de segurança sandbox, permite que uma aplicação applet rode em um<br />
contexto limitado quanto à sua liberdade de operações.<br />
O quão seguro será a execução de um applet é configurável através dos browsers que, geralmente,<br />
proíbem os seguintes processos:<br />
?? Execução de outros programas via Runtime<br />
?? Acesso a arquivos<br />
?? Chamada de métodos nativos do sistema operacional<br />
?? Abrir uma conexão Socket com outro endereço senão o próprio endereço onde o applet reside.<br />
quando Frames ou caixas de diálogo (Dialog) são abertas a partir de um applet, um rodapé é<br />
adicionado a eles, informando o usuário que se trata de uma aplicação via web, insegura. Isso foi feito<br />
para evitar que uma aplicação applet "iluda" um usuário leigo a digitar, por exemplo, o número de seu<br />
cartão de crédito – que poderia ser então retornado ao servidor de origem do applet.<br />
As restrições de segurança de um applet podem ser alteradas através de arquivos de configuração da<br />
máquina virtual em Intranets ou através de um certificado de segurança vinculado ao applet na Internet –<br />
chamado de Applet assinado. O uso de applets assinados será comentado em aula, mas está fora do<br />
escopo do nosso curso. Caso seja do seu interesse o uso de certificados de segurança, procure em sites<br />
de busca pelas palavras chave: <strong>Java</strong> Signed Applet. Existem dezenas de páginas bem detalhadas<br />
sobre esse assunto.<br />
O primeiro applet<br />
A criação de um applet é feita a partir do pacote java.applet.*; conforme mostra o exemplo abaixo:<br />
import java.awt.*;<br />
import java.applet.*;<br />
/**<br />
* FIC - Faculdade Integrada do Ceará<br />
* O primeiro applet<br />
* @author Felipe Gaúcho<br />
* @version exemplo.applet<br />
*/<br />
public class AloWeb extends Applet<br />
{<br />
// Uma variável de instância, do tipo String<br />
private String texto;<br />
// Método inicial do applet<br />
public void init ()<br />
{<br />
texto = "Alô Internet";<br />
}<br />
// Imprime o conteúdo de 'texto' na posição 25, 25 do applet<br />
public void paint(Graphics g)<br />
98
APPLETS<br />
}<br />
{<br />
}<br />
g.drawString(texto, 25, 25);<br />
Para se criar um applet, é necessário a declaração de uma classe com a seguinte assinatura:<br />
import java.applet.*;<br />
public class extends Applet<br />
{<br />
// ...<br />
}<br />
Além disso, as demais considerações sobre programas <strong>Java</strong> permanecem valendo – o código do<br />
exemplo acima deve ser salvo em um arquivo AloWeb.<strong>Java</strong> e compilado antes de poder ser executado<br />
dentro de um browser.<br />
É importante que o aluno pesquise a documentação sobre a classe java.applet.Applet para compreender<br />
melhor as características desses componentes, principalmente quanto a sua posição na hierarquia de<br />
classes da API <strong>Java</strong>:<br />
java.lang.Object<br />
java.awt.Frame<br />
java.awt.Component<br />
java.awt.Window<br />
java.awt.Container<br />
Observe que um applet é uma extensão de um painel, portanto o comportamento de uma interface<br />
gráfica applet é o mesmo de um aplicativo <strong>Java</strong> que use painéis como container padrão. O gerenciador<br />
de layout padrão para o applet é herdado da classe Panel: FlowLayout.<br />
embora um applet seja uma subclasse de Panel, não faz sentido usa-lo como um componente de um<br />
programa <strong>Java</strong>. Independentemente disso, existem aplicativos híbridos, ou seja, quando um programa é<br />
codificado para ser executado tanto como aplicação de console como aplicativo web. As vantagens e<br />
desvantagens da implementação de programas híbridos serão discutidas em sala de aula.<br />
99<br />
java.awt.Panel<br />
java.applet.Applet
APPLETS<br />
Ciclo de vida de um applet<br />
O ciclo de vida de um applet prevê a execução dos métodos abaixo, na ordem em que estão<br />
enumerados:<br />
onde:<br />
1st. Construtor<br />
2nd. public void int()<br />
3rd. public void start()<br />
4th. public void paint(Graphics g)<br />
5th. public void stop()<br />
6th. public void destroy()<br />
Construtor É um construtor normal de uma aplicação <strong>Java</strong>, que leva o nome da classe a<br />
que pertence e é geralmente usado para inicializar variáveis. Blocos de<br />
inicialização também podem ser usados em applets. O programador não<br />
deve incluir a lógica do programa no construtor, mas sim no método start<br />
explicado abaixo.<br />
init() Uma vez que o construtor já tenha sido executado, o objeto applet já existe<br />
na memória e a máquina virtual do browser executa o método init(). O<br />
método init() é usado para inicializar variáveis e, principalmente, para definir o<br />
aspecto gráfico do applet: mudar o gerenciador de layout e adicionar<br />
componentes. IMPORTANTE: o método init() é chamado apenas na primeira<br />
vez que o applet é carregado pelo browser. Se o usuário atualizar a página<br />
Html que contém um applet, esse método não será executado novamente.<br />
start() Esse método é chamado após o init() e é usado para usado para executar a<br />
lógica do applet. Esse método será chamado na primeira vez que um applet<br />
for carregado, sempre que a página for atualizada ou quando o browser for<br />
restaurado após ter sido minimizado. Por exemplo, se você implementar uma<br />
animação ou execução de sons em seu applet, essa animação ou som<br />
sempre será interrompida quando o browser for minimizado e reiniciará<br />
quando a janela do browser for restaurada.<br />
Dizemos que quando o método start é chamado o applet se torna vivo.<br />
paint(Graphics g) Veja a seção "contexto gráfico AWT" abaixo.<br />
stop() O método stop é chamado sempre que um applet deixar de estar vivo, ou<br />
seja, o browser for minimizado ou o usuário trocar de página. O programador<br />
pode usar esse método para liberar recursos de multimídia, como sons e<br />
animações. Dizemos que quando o método stop é chamado o applet deixa<br />
de estar vivo.<br />
destroy() Método chamado pelo browser automaticamente após o término do método<br />
stop(). Serve para indicar à máquina virtual vinculada ao browser que o<br />
applet não está mais ativo e pode ser desalocado da memória, bem como<br />
todos os recursos associados a ele. Pode ser sobrecarregado pelo<br />
programador para incluir uma determinada funcionalidade no encerramento<br />
da execução do applet, por exemplo, abrir uma janela de despedida, iniciar<br />
100
APPLETS<br />
ou suspender threads, etc.<br />
Outros dois métodos importantes na implementação de applets são:<br />
getCodeBase() Esse método retorna o endereço onde a classe principal do applet está<br />
armazenada.<br />
.<br />
getDocumentBase() Esse método retorna o endereço da página Html que contém o applet<br />
Contexto gráfico AWT<br />
Toda a interface gráfica AWT é baseada em um intrincado mecanismo de exibição de componentes,<br />
gerenciado por um processo isolado da aplicação chamado de Thread AWT.<br />
Essa thread é ativada em duas situações:<br />
?? Quando um componente gráfico for exibido na tela do computador. Isso ocorre quando um programa<br />
é ativado e está pronto para ser exibido ao usuário<br />
?? Quando um componente gráfico precisar ser atualizado, ou seja, precisar ser redesenhado por<br />
alguma razão: mudanças de características gráficas do componente, redimensionamento,etc.<br />
Quando isso ocorre, primeiro a thread AWT precisa remover a imagem antiga para depois aplicar a<br />
nova imagem do componente.<br />
Mais adiante no curso iremos estudar o conceito de threads. Por hora basta que o estudante saiba que<br />
existe um processo externo ao programa sendo executado, controlado automaticamente pela máquina<br />
virtual e chamado de thread AWT.<br />
Apesar da imagem dos componentes ser gerada automaticamente pela thread AWT, o programador pode<br />
interferir no aspecto visual dos componentes através dos seguintes métodos:<br />
?? public void paint(Graphics g)<br />
?? public void repaint()<br />
?? public void update(Graphics g)<br />
onde:<br />
paint(Graphics g) Sempre que a imagem de um componente gráfico AWT (ou extendido de<br />
AWT, como os componentes Swing) for ser exibida na tela de um<br />
computador, o método paint() será chamado pela máquina virtual. Se o<br />
programador não sobrecarregar esse método, a JVM irá usar uma imagem<br />
padrão para o componente em questão.<br />
Uma facilidade da classe java.awt.Graphics, chamada de clip<br />
rectangle, é utilizada para que nem toda a área ocupada por um<br />
componente precise ser redesenhada a cada atualização desse<br />
componente. Por exemplo, quando você tem duas janelas sobrepostas no<br />
Windows, e parte da janela que está atrás aparece, se você minimizar a<br />
janela que está na frente apenas a parte que não aparecia da janela detrás<br />
será redesenhada.<br />
Se o programador desejar alterar a imagem do componente ou desenhar<br />
101
APPLETS<br />
diretamente sobre ele, deverá sobrepor esse método.<br />
Um applet é um componente AWT que não possui imagem gráfica<br />
padrão. Para que alguma imagem apareça quando o applet for executado,<br />
o programador deve sobrepor o método paint(..). Caso contrário, o espaço<br />
ocupado pelo applet ficará vazio, com a cor do fundo cinza.<br />
repaint() Esse método força a atualização da imagem do componenet, através de<br />
uma chamada ao método update(). É muito usado para prover uma forma<br />
interativa de modificação na imagem do componente, por exemplo, quando<br />
um usuário seleciona uma nova imagem de uma lista e essa imagem deve<br />
ser visualizada no centro de um applet.<br />
update(Graphics g) Esse método força a thread AWT a chamar um método nativo chamado<br />
update(), que tem o seguinte comportamento:<br />
?? limpa a área ocupada pelo componente gráfico, preenchendo essa<br />
área com a cor de fundo do componente.<br />
?? Ajusta a cor padrão do contexto gráfico para a cor de frente do<br />
componente gráfico.<br />
?? Chama o método paint() do componente.<br />
A ordem de execução dos métodos descritos acima é apresentada no esquema abaixo:<br />
Thread AWT esperando a próxima notificação<br />
repaint()<br />
update()<br />
Limpar a área<br />
Chamar o método paint(..)<br />
Aprendendo a usar o appletviewer<br />
O appletviewer é um aplicativo que acompanha o ambiente de desenvolvimento jdk e visa facilitar os<br />
testes durante a implementação de applets <strong>Java</strong>. Ao invés de usar um browser para testar um applet, o<br />
102<br />
paint(...)<br />
atualização<br />
componente
APPLETS<br />
desenvolvedor pode ativar o applet diretamente a partir do aplicativo chamado appletviewer, com a<br />
seguinte linha de comando:<br />
appletviewer [-debug] url<br />
onde:<br />
-debug: permite que o appletviewer mostre no console informações sobre a alocação dos objetos<br />
durante a execução do applet, e eventuais erros de execução.<br />
url: o endereço onde o código Html que contém uma referência ao applet está armazenado.<br />
Exemplo:<br />
C:\>cd jdk1.3.1<br />
C:\jdk1.3.1>cd demo<br />
C:\jdk1.3.1\demo>cd applets<br />
C:\jdk1.3.1\demo\applets>cd tic*<br />
C:\jdk1.3.1\demo\applets\TicTacToe>appletviewer<br />
example1.html<br />
uma grande fonte de aprendizado para a implementação de applets é o conjunto de exemplos que<br />
acompanham a documentação do jdk, pois tem o código fonte disponível para que o aluno aprenda.<br />
Procure identificar em que diretório esses exemplos estão copiados e execute-os para ter uma boa idéia<br />
de como um applet se comporta. Além disso, procure em sites de busca pelas pelavras chave: <strong>Java</strong> e<br />
Applets. Você verá que existem centenas de páginas com demonstrações de applets e dicas de como<br />
criar applets mais bonitos ou úteis.<br />
O código HTML de carga do applet<br />
Conforme citamos no início deste texto, para executar um applet via Internet é necessário que a<br />
referência a esse applet apareça em uma página Html. Ao interpretar uma página Html que contém um<br />
applet, o browser identifica a tag e ativa a máquina virtual vinculada a ele.<br />
Essa tag é apresentada abaixo, com as definições opcionais em itálico:<br />
<br />
archive = listaDeArquivos<br />
code = nome do applet<br />
width = largura em pixels<br />
height = altura em pixels<br />
codebase = endereço onde o applet está armazenado<br />
alt = código para ser exibido em browser sem suporte à applets<br />
name = nome da instância do applet<br />
align = alinhamento em relação ao espaço disponível para exibição<br />
vspace = espaço vertical em pixels<br />
hspace = espaço horizontal em pixels<br />
103
APPLETS<br />
onde:<br />
param name = atributo value = valor<br />
alternateHTML<br />
<br />
Archive Lista de arquivos contendo recursos a serem carregados antes da execução do<br />
applet. Normalmetne são arquivos contendo imagens, sons ou classes<br />
implementados por terceiros. O nome dos arquivos deve ser separado por ','<br />
code Atributo obrigatório que indica o nome da classe principal do applet. O nome da<br />
classe não deve incluir o caminho da classse no servidor – se a classe se<br />
encontrar em um caminho diferente do Html que a chamou, esse novo caminho<br />
deve ser definido no atributo codebase. Caso o applet pertença a um pacote, o<br />
nome do pacote deve ser inserido ao nome da classe, por exemplo:<br />
fic.jogo.jogoDaVelha.class<br />
width<br />
heigth<br />
Esse atributos obrigatórios indicam as dimensões que o applet deverá ocupar na<br />
página Html, em pixels.<br />
codebase Especifica a url onde o código se encontra. Se esse atributo não for definido, o<br />
browser assume o endereço da página Html como sendo o mesmo da classe<br />
principal do applet.<br />
alt Se o browser suportar a execução de applets mas por algum motivo o applet não<br />
puder ser executado, o texto especificado pelo atributo alt será mostrado no<br />
lugar do applet.<br />
name Atributo opcional usado para nomear a instância do applet. Isso é útil para prover<br />
a comunicação entre dios mais applets rodando na mesma página Html.<br />
align Atributo opcional de alinhamento do applet em relação ao espaço disponível<br />
para a sua exibição na página Html:<br />
left, right, top, texttop, middle, absmiddle, baseline,<br />
bottom e absbottom.<br />
vspace<br />
hspace<br />
param name<br />
value<br />
Atributos opcionais que especificam o número de pixels a serem deixados de<br />
espaço entre as bordas do applet e o espaço disponível para a sua exibição.<br />
São atributos que permitem a passagem de parâmetros ao applet. Usado para<br />
configurar o comportamento de um determinado applet, que lê os parâmetros<br />
utilizando o método getParameter();<br />
Como exemplo de Html que contém um applet, mostramos abaixo o Html responsável pela exibição do<br />
Tic-Tac-Toe, o exemplo que acompanha o jdk mostrado anteriormente:<br />
<br />
<br />
<br />
<br />
TicTacToe v1.1<br />
104
APPLETS<br />
<br />
TicTacToe v1.1<br />
<br />
<br />
alt=" o applet não está rodando, verifique suas configurações"<br />
<br />
<br />
The source.<br />
<br />
Lendo parâmetros do Html com um applet<br />
Uma das formas mais comuns de configurar o comportamento de um applet é a definição de parâmetros<br />
no código Html. Por exemplo, abaixo temos uma applet que mostra uma figura carregada a partir da url<br />
onde o applet está armazenado. Observe que o nome da figura a ser exibida pelo applet aparece como<br />
um parâmetro no Html e não no próprio código do applet, Isso é útil, pois se quisermos mudar o nome da<br />
figura, ou não soubermos de antemão o nome da figura, poderemos mudar apenas o código Html, sem a<br />
necessidade de modificar e compilar o código do applet.<br />
<br />
<br />
---------------<br />
<br />
<br />
<br />
<br />
import java.awt.*;<br />
import java.applet.*;<br />
import java.net.*;<br />
Mostrando uma imagem<br />
<br />
<br />
<br />
/**<br />
* Exemplo de leitura de parâmetro com applets<br />
* @author Felipe Gaúcho<br />
*/<br />
public class Figura extends Applet<br />
{<br />
105
APPLETS<br />
}<br />
Image figura;<br />
// Aqui a figura será carregada<br />
public void init()<br />
{<br />
URL endereço = getDocumentBase();<br />
String nomeDaFigura = getParameter("imagem");<br />
figura = getImage(endereco, nomeDaFigura);<br />
}<br />
// Aqui a figura será desenhada na tela<br />
public void paint(Graphics g)<br />
{<br />
g.drawImage(figura, 0, 0, this);<br />
}<br />
Os parâmetros encontrados em páginas Html são sempre do tipo String, mas você pode convertê-los<br />
usando os método das classes wrapper, por exemplo:<br />
int velocidade = Integer.parseInt(getParameter("velocidade"));<br />
Manipulando imagens<br />
Para que um applet use uma imagem gráfica, é necessário que essa imagem seja do tipo *.gif, ou<br />
*.jpg. Os aspectos relativos ao uso de imagens e sons serão discutidos em sala de aula, mas o aluno<br />
deve ter sempre em mente o fato de que applets são aplicativos carregados via Internet e, dependendo<br />
da velocidade de conexão com o servidor, o uso excessivo de imagens ou de imagens muito grandes<br />
deve ser evitado.<br />
Abaixo mostramos um método bastante útil, que lê imagens a partir de um determinado endereço da<br />
internet. Você pode incluir esse método no código de seus applets. Esse código será melhor explicado<br />
em sala de aula.<br />
/**<br />
* Método que carrega imagens na memória, a partir da URL onde o<br />
* applet está sendo carregado. O uso de um 'mediaTracker' é fundamental para<br />
* evitar problemas quanto à velocidade de carga de uma imagem. Se você não<br />
* usar um mediaTracker, o seu código pode tentar usar uma imagem antes dela<br />
* estar plenamente carregada na memória, o que causaria um erro de execução.<br />
* @param nomeDaImagem O nome da imagem a ser carregada<br />
* @return A imagem requisitada ou null caso não haja tal imagem na url do applet<br />
*/<br />
public Image carregarImagem(String nomeDaImagem)<br />
{<br />
try<br />
{<br />
Image imagem = getImage(getDocumentBase(), nomeDaImagem);<br />
MediaTracker carregador = new MediaTracker(this);<br />
carregador.addImage(imagem, 0);<br />
carregador.waitForID(0);<br />
106
APPLETS<br />
}<br />
Exercícios<br />
return imagem;<br />
}<br />
catch(Exception erro)<br />
{<br />
erro.printStackTrace();<br />
System.exit(0);<br />
return null;<br />
}<br />
10. Escreva um applet que mostre o seu nome no centro da tela.<br />
11. Escreva o código Html necessário para rodar o applet do exercício 1 e teste o seu applet com o<br />
appletviewer.<br />
12. Implemente um applet que desenhe círculos coloridos na tela. O tamanho e a cor dos círculos<br />
podem ser aleatórios.<br />
13. Procure na web por exemplos de applets e procure identificar como esses applets foram<br />
implementados. Dica: você pode aproveitar a seção de apoio didático como ponto de partida. Boa<br />
viagem.<br />
107
Interfaces gráficas baseadas em<br />
behaviorismo – O pacote Swing<br />
Padrões de projeto permitem ao desenvolvedor de software o aumento da produção e a garantia da<br />
qualidade de seus programas. Nesta seção, o aluno é apresentado ao pacote Swing – um conjunto de<br />
componentes gráficos baseados no padrão MVC.<br />
O que é Swing ?<br />
Swing é o nome dado ao pacote de classes desenvolvidas pelo "Projeto Swing" – parte de um contexto<br />
maior chamado de JFC – <strong>Java</strong> Foundation Classes. O Swing consiste em um conjunto de componentes<br />
gráficos (extensões dos componentes AWT e novos componentes como representação de árvores e<br />
painéis tabulados), que agregam o conceito de look and feel (L&F), ou seja, a capacidade de um mesmo<br />
componente assumir aparências diferentes sem a necessidade de mudanças no seu código. Por<br />
exemplo, você pode criar uma aplicação <strong>Java</strong> que se adapte à aparência gráfica do sistema operacional<br />
em que for executado – assumindo um aspecto semelhante as demais janelas Windows, Linux, Solaris,<br />
Macintosh, etc. Os componentes Swing são totalmente criados em <strong>Java</strong> (100% pure <strong>Java</strong>) e foram<br />
desenvolvidos no conceito de interface peso-leve do usuário. A a idéia é codificar apenas a<br />
funcionalidade do componente e a sua relação com o modelo de dados ao qual está associado, deixando<br />
a sua aparência a cargo do gerenciador de interface do usuário (UI Manager), um novo recurso<br />
incorporado às máquinas virtuais a partir da versão 1.3 do ambiente de desenvolvimento <strong>Java</strong> (jdk1.3.1).<br />
Nesta aula não iremos detalhar a manipulação dos componentes Swing. Ao invés disso, focaremos a<br />
atenção no conceito de Modelo-Visão-Controle, o padrão de projeto adotado pela maioria dos<br />
componentes Swing. Aconselha-se fortemente ao aluno que leia a excelente documentação sobre Swing<br />
e analise os exemplos que acompanham o jdk.<br />
Containers Swing<br />
Começaremos a nossa visita ao mundo das interfaces Swing apresentando a classificação de seus<br />
principais containers em relação à API <strong>Java</strong>:<br />
?? javax.swing.JFrame<br />
?? javax.swing.JPanel<br />
?? javax.swing.JApplet<br />
16
INTERFACES GRÁFICAS BASEADAS EM BEHAVIOR ISMO (SWING)<br />
java.lang.Object<br />
javax.swing.JComponent<br />
javax.swing.JPanel<br />
java.awt.Component<br />
java.awt.Container<br />
java.awt.Window<br />
java.awt.Frame<br />
javax.swing.JFrame<br />
Observe que a grande mudança de classificação em relação aos componentes AWT é que os painéis<br />
Swing passaram a ter uma superclasse distinta dos painéis AWT, embora ambos continuem a ser<br />
containers. Essa diferenciação foi promovida pela implementação da classe JComponent, que foi<br />
concebida a dar suporte ao uso de modelos separados da interface, conforme veremos mais adiante no<br />
curso. Os demais containers, JFrame e JApplet, apresentam variações apenas estéticas e, por isso<br />
mesmo, aparecem no mesmo ramo de seus pares AWT - Frame e Applet respectivamente.<br />
Além dos container tradicionais, apresentados acima, o pacote Swing introduziu uma série de novos<br />
containers que, por possuírem funções específicas na construção de interfaces inerentes a determinados<br />
tipos de problemas, não serão detalhados aqui:<br />
?? BasicSplitPaneDivider – usado para interfaces onde os painéis são redimensionáveis.<br />
?? Box – usado para<br />
?? CellRendererPane<br />
?? DefaultTreeCellEditor.EditorContainer<br />
Observe que os containers Swing tem como superclasse o container AWT, logo, todos os conceitos<br />
sobre gerenciamento de componentes em containers AWT seguem valendo.<br />
Introdução a padrões de projeto (design patterns)<br />
No começo dos anos 90, um grupo de pesquisadores conhecidos como "A gangue dos 4", lançou o<br />
célebre livro Design Patterns, Elements of Reusable Object-Oriented Software (The Gang of Four -<br />
109<br />
java.awt.Panel<br />
java.applet.Applet<br />
javax.swing.JApplet
INTERFACES GRÁFICAS BASEADAS EM BEHAVIOR ISMO (SWING)<br />
GOF, Addison-Wesley: 1995). Nesta publicação, os autores Erich Gamma, Richard Helm, Ralph Johnson<br />
e John Vlissides identificaram padrões no desenvolvimento de sistemas industriais, ou seja, observaram<br />
que vários programadores acabavam gerando soluções muito semelhantes para os problemas mais<br />
freqüentes no desenvolvimento de programas de computador. Detectaram também que o<br />
desenvolvimento de tais projetos exigia um grande esforço de reflexão acerca da ontologia dos<br />
problemas, consumindo muito tempo para ser desenvolvido e com a qualidade vinculada à experiência<br />
dos analistas responsáveis pelo projeto. Por essa razão, a gangue dos quatro resolveu documentar os<br />
padrões mais usados, permitindo aos novos programadores e analistas a redução do tempo necessário<br />
para identificar os aspectos centrais dos problemas mais comuns em computação. Ao todo, foram<br />
detectados 23 padrões de comportamento na modelagem de sistemas, subdivididos em três grandes<br />
grupos:<br />
?? Creational design patterns – relacionado à instanciação dos objetos ou grupos de objetos. Por<br />
exemplo, um sistema pode necessitar limitar o número de instâncias ativas de uma determinada<br />
classe ou então permitir que as características de uma classe sejam definidas em tempo de<br />
execução. Padrões definidos: Abstract factory, Builder, Factory Method, Prototype<br />
e Singleton.<br />
?? Structural design patterns – associado à relação entre as classes de um projeto, ou seja, a<br />
comunicação entre elas, ordem em que os objetos são instanciados, etc. Padrões definidos:<br />
Adapter, Bridge, Composite, Decorator, Facade, Flyweight e Proxy.<br />
?? Behavioral design patterns – estudo do comportamento dos objetos em relação ao seu meio<br />
ambiente, tratando isoladamente as características de um objeto e o seu comportamento. Grande<br />
parte dos componentes Swing foram implementados usando o padrão behaviorista chamado<br />
Observer, que iremos descrever com mais detalhes na próxima seção. Padrões definidos: Chainof-responsability,<br />
Command, Interpreter, Iterator, Mediator, Meme nto,<br />
Observer, State, Strategy, Template e Visitor.<br />
O termo design pattern, embora possa ser literalmente traduzido como padrão de projeto, deve<br />
ser visto como um jargão tecnológico, ou seja, usado em inglês, mesmo por falantes nativos de outras<br />
línguas. Além disso, o estudo de design patterns tornou-se referência de qualidade para todo profissional<br />
de informática. É indispensável que o aluno leia algum material completo sobre o assunto, que será<br />
retomado com mais profundidade na disciplina de Sistemas Orientados a Objetos II.<br />
Programação behaviorista - o paradigma modelo-visãocontrole<br />
(MVC)<br />
O termo behaviorismo remete à idéia de ação e reação, adotada por alguns estudiosos da mente<br />
(psicólogos, psiquiatras, etc.) como teoria de formalização do comportamento humano. A idéia é bem<br />
simples: você tem uma entidade que reage aos estímulos do meio ambiente, como por exemplo, uma<br />
pessoa gritar quando recebe um choque elétrico, uma bola se mover quando é chutada, uma lâmpada<br />
acender quando o seu interruptor é ligado, etc.<br />
No ser humano, esse conceito é um tanto quanto polêmico, pois não se pode afirmar o quanto do nosso<br />
comportamento é puramente behaviorista e o quanto é de natureza emocional, ainda pouco conhecida.<br />
Já em entidade artificiais, como componentes de programação, o reflexo condicionado é perfeitamente<br />
aplicável, pois todos os estímulos e reações de um componente serão previamente conhecidos, não<br />
havendo espaço para reações anômalas, de natureza emocional ou caótica.<br />
110
INTERFACES GRÁFICAS BASEADAS EM BEHAVIOR ISMO (SWING)<br />
Exemplo: você pode criar um botão e associar a esse botão um determinado método em seu programa.<br />
Você sabe que, sempre que esse botão for pressionado, ele irá executar o método que está associado a<br />
ele. Ação e reação: o usuário clica sobre o botão e o programa executa algum método, sempre o mesmo<br />
método e quantas vezes o usuário quiser – a reação do botão ao estímulo "clique do usuário" é prédefinida<br />
e, portanto, consistente em relação ao modelo do sistema.<br />
Tradicionalmente, a funcionalidade de um programa de computador é codificada junto ao seu modelo de<br />
dados e, às vezes, junto à sua interface gráfica. A proposta do paradigma controle-modelo-visão (CMV) é<br />
desassociar esses três aspectos do software, deixando os programas mais portáveis e simples de serem<br />
compreendidos. Um sistema behaviorista deve incluir três estruturas codificadas geralmente em<br />
separado:<br />
?? Modelo: é a enumeração das estruturas de dados associadas ao problema, ou seja, uma classe que<br />
encapsule um conjunto de dados primitivos ou agregados . O modelo também deve prover métodos<br />
que permitam a manipulação de seus dados através da interação com processos computacionais<br />
controlados pelo usuário ou por sistemas de computação. Um modelo é dito observável, ou seja,<br />
outras entidades do sistema podem monitorar o seu comportamento, reagindo a mudanças nos<br />
valores de seus dados.<br />
?? Controle: são processos computacionais que tem acesso aos dados de um modelo, podendo<br />
manipular esses dados de forma consistente. Por exemplo: se tivermos um sistema baseado em<br />
banco de dados relacional, as tabelas da base de dados serão o modelo do sistema, enquanto os<br />
processos de inserção/remoção/atualização dos dados serão o controle do sistema. Podemos dizer<br />
informalmente que o controle é forma de comunicação entre o modelo e a visão de um sistema CMV,<br />
sendo que, em muitos casos, encontramos o modelo e o controle codificados na mesma classe.<br />
?? Visão: informa o valor dos dados do modelo a outros processos do sistema ou ao usuário, ou seja, é<br />
um processo observador do modelo. Em sistemas behavioristas, a visão é normalmente a interface<br />
gráfica do usuário<br />
A figura acima representa a idéia geral de um sistema behaviorista, ou seja, o usuário manipula o modelo<br />
de dados através de mecanismos de controle e, cada vez que o valor dos dados do modelo for alterado,<br />
essa modificação é refletida na interface do usuário (visão). A figura acima mostra um exemplo simples<br />
de sistema CMV, mas é importante o aluno lembrar que podemos ter sistemas distribuídos, com múltiplos<br />
usuários e diferentes controles e visões para o mesmo modelo de dados. Sistemas multiusuários<br />
apresentam problemas complexos de sincronização de acesso ao modelo de dados que serão abordados<br />
111
INTERFACES GRÁFICAS BASEADAS EM BEHAVIOR ISMO (SWING)<br />
mais adiante, na aula sobre threads. Outro detalhe interessante a ser lembrado é o fato de que o modelo<br />
pode ser manipulado por outros sistemas de computador. Ou seja, no exemplo acima aparece um<br />
usuário como agente ativo no sistema, mas a figura do usuário poderia ser substituída por algum<br />
processo automático controlado por software e/ou hardware. Imagine um sistema bancário que aplica<br />
dinheiro automaticamente na poupança a cada vez que o saldo for superior a R$ 1.000,00. O usuário,<br />
correntista, fica mexendo em sua conta bancária sem interagir diretamente com as aplicações<br />
financeiras. O próprio sistema bancário é que detecta o saldo suficiente e dispara um processo de<br />
aplicação bancária ou resgate de aplicação.<br />
Muitas situações são imagináveis, a maioria delas prevista em design patterns, o que dá uma boa noção<br />
sobre a necessidade de um estudo detalhado sobre Design Patterns.<br />
Qual a vantagem de se codificar o modelo, o controle e a visão em classes separadas?<br />
Imagine que você desenvolva um sistema de controle de estoque de uma loja, formado por alguns<br />
formulários de entrada de dados, emissão de relatórios e que tenha também o controle fiscal. Agora<br />
imagine que o governo altere as normas fiscais vigentes no País. Caso você tenha codificado seu<br />
sistema em uma única classe, você terá que alterar os trechos de código relativos ao controle fiscal,<br />
procurando esses trechos entre outros códigos que nada tem a ver com o controle fiscal – como, por<br />
exemplo, aspectos da interface, dos relatórios, etc. Ou então imagine que o gerente de marketing de sua<br />
empresa sugeriu mudar drasticamente o visual do sistema, permitindo aos compradores da loja o acesso<br />
direto à consulta de preços através de uma nova interface que agrega informações com marketing.<br />
Mesmo que você tenha codificado seu sistema em módulos separados, se esses módulos possuem<br />
referências entre eles, ou seja, ponteiros entre as classes, você terá um grande trabalho de reformulação<br />
da interface gráfica e, principalmente, de garantir a consistência do sistema – talvez tenha que refazer<br />
tudo.<br />
Embora um software possa ser modulado através de técnicas convencionais, a presença de referências<br />
entre os módulos impede uma dinâmica eficiente na manutenção desses sistemas. Se você tiver, por<br />
exemplo, uma classe de controle fiscal com um ponteiro para a classe que gera a interface gráfica, e<br />
essa interface gráfica for renomeada ou removida do sistema, será necessário modificar também a classe<br />
de controle fiscal para que o sistema continue consistente. Ou então se você quiser reaproveitar uma boa<br />
interface gráfica em um novo modelo, você terá uma complicada tarefa de verificar todos os ponteiros<br />
entre as duas classes.<br />
O CMV vem justamente propor uma forma modular de atualização de sistemas, onde a modificação de<br />
um aspecto do sistema não interfere nos demais. Por exemplo, em um sistema CMV, podemos ter várias<br />
interfaces gráficas diferentes refletindo os dados de um único modelo, ou então diferentes formas de<br />
controle atuando sobre esse mesmo modelo – por exemplo: a interface dos usuários só permite consulta<br />
enquanto a dos gerentes permite modificação nos valores. Quem faz a comunicação entre os<br />
módulos de um sistema behaviorista não são mais os componentes do software,<br />
diretamente via referências, mas sim a própria máquina virtual que possui suporte a<br />
esse tipo de programação - baseada em eventos chamados de notificações. De fato,<br />
o comportamento de um sistema behaviorista gira em torno do modelo de dados – sempre que algum<br />
formato ou valor for modificado no modelo de dados, o objeto que representa esse modelo notifica a JVM<br />
sobre que tipo de alteração ele sofreu. A máquina virtual, por sua vez, se encarrega de repassar a<br />
notificação a todos os observadores que estão associados a esse modelo. Note que o modelo não sabe<br />
se existem observadores sobre ele e nem o quê esses eventuais observadores fazem com as<br />
notificações recebidas, pois não existe nenhuma referência direta entre o modelo (observável) e os<br />
observadores. A comunicação entre os módulos de um sistema CMV aparece na figura abaixo:<br />
112
INTERFACES GRÁFICAS BASEADAS EM BEHAVIOR ISMO (SWING)<br />
controle<br />
alteração<br />
modelo<br />
notificação<br />
Visão<br />
Swing<br />
notificação<br />
No diagrama acima, cada elipse representa uma classe diferente, ou seja, a remoção ou modificação de<br />
uma das classes do sistema não afeta o comportamento das demais. Um detalhe a ser lembrado é que<br />
não faz sentido pensar em observadores sem modelo, porém um modelo sem observadores é<br />
perfeitamente aceitável – normalmente em sistemas automáticos, com pouca ou nenhuma interferência<br />
do usuário. Outra vantagem do uso de CMV é que as partes que compõem o sistema podem ser<br />
desenvolvidas em separado, por pessoas/equipes diferentes e/ou sem a necessidade de sincronia.<br />
Os módulos de um sistema podem também ser testados em separado, por softwares de teste. O uso<br />
de ferramentas de teste vem crescendo em popularidade entre as empresas de tecnologia, bem como a<br />
precisão dessas ferramentas. É interessante que o aluno saiba da existência dessas ferramentas e<br />
procure informações de como elas são utilizadas. Dica: dê uma olhada em www.junit.org.<br />
Implementando um modelo em <strong>Java</strong> (Observable)<br />
Um modelo em <strong>Java</strong> pode ser implementado de duas maneiras:<br />
?? Contendo uma lista de observadores (Listeners)<br />
?? Estendendo a superclasse java.util.Observable<br />
Máquina Virtual <strong>Java</strong> (JVM)<br />
A discussão entre essas diferentes abordagens será dispensada aqui, deixando ao aluno o cargo de<br />
aprofundar o estudo sobre implementação de objetos observáveis em <strong>Java</strong>. A principal diferença entre a<br />
lista de observadores e a classe Observable é relacionada à inexistência de herança múltipla em <strong>Java</strong>.<br />
Quando estendemos uma classe à superclasse Observable, estamos impedindo que a classe do modelo<br />
seja derivada de outra classe, com funcionalidades eventualmente relevantes ao sistema que está sendo<br />
implementado. Mas, na maioria das vezes, um modelo deve apenas representar os dados do sistema e<br />
não necessita de outras funcionalidades exceto de ser um objeto observável. Para tal, declaramos a<br />
classe que irá representar o modelo no seguinte formato:<br />
import java.util.*; // Pacote <strong>Java</strong>.útil contém a classe Observable<br />
/** Classe observável (modelo) */<br />
public class extends Observable<br />
{<br />
113<br />
Visão AWT<br />
notificação<br />
Servidor<br />
web<br />
notificação
INTERFACES GRÁFICAS BASEADAS EM BEHAVIOR ISMO (SWING)<br />
}<br />
private int valor = 0;<br />
// Exemplo de método de acesso aos dados do modelo<br />
public void ajustarValor (int valor)<br />
{<br />
this.valor = valor;<br />
setChanged();// Registro de modificação do modelo<br />
notifyObservers(); // Notificação de modificação nos valores do modelo<br />
}<br />
// restante do corpo da classe...<br />
Implementando visões para um modelo<br />
Uma visão é uma classe <strong>Java</strong> que implementa a interface java.util.Observer, conforme o exemplo abaixo:<br />
import java.util.*; // Pacote <strong>Java</strong>.útil contém a classe Observer<br />
/** Classe observável (modelo) */<br />
public class implements Observer<br />
{<br />
public void update(Observable modelo, Object component)<br />
{<br />
}<br />
}<br />
System.out.println(o"recebeu notificação de " + modelo) ;<br />
// restante do corpo da classe...<br />
Criando o primeiro aplicativo MVC<br />
Mostraremos um exemplo simples, com os seguintes componentes:<br />
?? Modelo: contém um valor inteiro, limitado entre 0 e um determinado limite superior. Para facilitar o<br />
aprendizado de interação entre modelos e visões, iremos implementar a interface<br />
javax.swing.BoundedRangeModel no modelo. Isso faz com que os métodos de atualização dos<br />
valores do modelo sejam sobrecargas de métodos dessa interface:<br />
o setValue(int valor);<br />
que permite a um controlador modificar o valor atual do modelo.<br />
o getValue();<br />
que permite a um controlador saber o valor atual do modelo.<br />
?? Controle: contém uma referência a um modelo do contador e, ao ser ativado, incrementa<br />
sistematicamente o valor do modelo até que seja alcançado o seu limite. Para ser de simples<br />
entendimento, o controlador possui apenas um método, construtor, que executa a toda a função prédefinida<br />
ao controlador.<br />
114
INTERFACES GRÁFICAS BASEADAS EM BEHAVIOR ISMO (SWING)<br />
?? Visão: implementaremos duas interfaces gráficas, uma Swing e outra AWT, permitindo ao aluno<br />
observar o comportamento de diferentes visões associadas a um mesmo modelo:<br />
O código desse exemplo será detalhado em aula, mas para você rodar em casa faça o seguinte:<br />
?? Baixe o arquivo zipado contendo os códigos fonte do exemplo<br />
?? Descompacte os códigos em algum diretório de teste<br />
?? No diretório de testes, digite os seguintes comandos:<br />
o javac *.<strong>Java</strong><br />
o java CMV<br />
Após rodar o exemplo, procure olhar o código fonte e descobrir como ele funciona, como foi<br />
implementado. Se você não compreender o funcionamento do CMV, ou parte dele, pergunte ao<br />
professor.<br />
115
Acesso a dispositivos de entrada e<br />
saída - I/O<br />
Dispositivos de entrada e saída são utilizados para persistência ou transferência de dados. <strong>Java</strong> apresenta<br />
uma elegante abordagem Padrões de projeto permitem ao desenvolvedor de software o aumento da<br />
produção e a garantia da qualidade de seus programas. Nesta seção, o aluno é apresentado ao pacote<br />
Swing – um conjunto de<br />
O que são I/O Streams ?<br />
A linguagem <strong>Java</strong> não trata dispositivos de entrada e saída e forma específica, ou seja, com classes<br />
específicas para cada dispositivo. A invés disso, <strong>Java</strong> implementa o conceito de Streams, que podem ser<br />
vistos como canais por onde trafegam bytes entre um processo computacional e uma origem ou destino<br />
de dados: arquivos, impressora, mouse, teclado, vídeo, conexão via Socket com outros programas, etc. A<br />
ordem do fluxo de dados, entrada ou saída, é relevante na escolha do Stream a ser utilizado. Além disso,<br />
podemos ter Streams com buffer de dados e/ou com filtros de dados, que veremos a seguir.<br />
A figura abaixo mostra os dois tipos básicos de Streams utilizados pela linguagem <strong>Java</strong>:<br />
Programa<br />
<strong>Java</strong><br />
Programa<br />
<strong>Java</strong><br />
Input Stream<br />
leitura<br />
filtro<br />
buffer<br />
filtro<br />
buffer<br />
Output Stream<br />
escrita<br />
17<br />
Dispositivos de entrada:<br />
Mouse<br />
Teclado<br />
Leitura de arquivos<br />
Dispositivos de saída:<br />
Impressora<br />
Tela de saída (console)<br />
Escrita em arquivos<br />
Input Stream: canal utilizado para a leitura de bytes a partir de um dispositivo de entrada.<br />
Output Streams: canal utilizado para a escrita de bytes em um dispositivo de saída.<br />
Filtered Streams: canal com um filtro acoplado a dispositivos de entrada ou saída, e que permite a<br />
escrita ou leitura de tipos de dados ao invés de simples bytes.
Buffered Streamas: canal que permite a leitura ou escrita de dados através de um depósito de bytes,<br />
ou seja, o programador pode definir uma quantidade de bytes transferida a cada comando de escrita<br />
ou leitura.<br />
Leitura de dados (java.io.InputStream)<br />
Os métodos básicos em Streams de entrada são:<br />
int read()<br />
int read(byte[])<br />
int read(byte[], int, int)<br />
Esses três métodos são usados na leitura dos dados disponíveis em um Stream. Note que o retorno dos<br />
métodos é um número inteiro, indicando o byte lido do Stream ou então o número de bytes lidos do<br />
Stream. Caso não haja bytes disponíveis para a leitura, ou tenha ocorrido algum erro durante a leitura, o<br />
retorno desses métodos será –1. O parâmetro byte[] que aparece dentro dos dois últimos métodos<br />
representa a referência a um array de bytes onde o método deve guardar os bytes lidos do dispositivo de<br />
entrada – o número de bytes a ser lido é o tamanho desse array. No último método, os dois parâmetros<br />
inteiros representam o intervalo dentro do array onde os bytes devem ser armazenados, e o número de<br />
bytes a ser lido é a diferença entre os dois valores.<br />
para um melhor desempenho de seus programas, procure definir o tamanho do array de bytes usado<br />
para a leitura de dados com o máximo tamanho suportado pelo dispositivo de entrada.<br />
void close()<br />
Método que fecha um Stream, e que deve ser executado sempre que o seu programa não precisar mais<br />
ler dados de um dispositivo. A permanência de canais abertos a dispositivos de entrada sem necessidade<br />
prejudica o desempenho de seu programa e causa um risco à integridade dos dados desses dispositivos.<br />
int available()<br />
Método que retorna a quantidade disponível de bytes em um dispositivo de leitura, muito usado na leitura<br />
de arquivos cujo tamanho não é previamente conhecido.<br />
void skip(long)<br />
Esse método descarta um determinado número de bytes do Stream.<br />
boolean markSupported()<br />
Alguns dispositivos de entrada permitem operações push back, ou seja, a utilização de um marcador de<br />
posição do primeiro byte disponível no Stream. O método markSupported() é utilizado para detectar se o<br />
dispositivo ao qual o Stream está associado suporta push back, ou seja, retorno verdadeiro. Caso<br />
contrário, o retorno do método será falso.<br />
void mark(int)<br />
void reset()<br />
117
Se o dispositivo suportar push back, o programador pode usar o método mark para definir a posição<br />
inicial de leitura ou o método reset para restaurar a ordem original dos bytes disponíveis no dispositivo de<br />
leitura. O exemplo mais comum de push back é verificado em arquivos de acesso randômico conforme<br />
descrito mais adiante nesse texto.<br />
Escrita de dados (java.io.OutputStream)<br />
Os métodos básicos em Streams de saída são:<br />
void write(int)<br />
void write(byte[])<br />
void write(byte[],int, int)<br />
Esses três métodos são usados na escrita de dados em um dispositivo de saída, com um comportamento<br />
semelhante aos método de leitura descritos na seção anterior.<br />
void close()<br />
Método que fecha o acesso a um dispositivo de saída. Sempre que o programa encerrar as operações de<br />
escrita em um dispositivo de saída, ele deve fechar o Stream associado a esse dispositivo.<br />
void flush()<br />
Método que força o Stream a descarregar todos os bytes que já foram enviados ao dispositivo de saída.<br />
Algumas vezes, existe uma defasagem entre o momento que um programa chama uma escrita de dados<br />
em um dispositivo de saída e o Stream realmente escreve nesse dispositivo. O comando flush força a<br />
escrita imediatamente. Quando o método close é chamado sobre um Stream de saída, o método flush é<br />
automaticamente executado antes do fechamento do Stream, para garantir que todos os bytes enviados<br />
a um dispositivo de saída tenham efetivamente sido escritos nesse dispositivo.<br />
As classes de manipulação de Streams em <strong>Java</strong> (o pacote<br />
java.io)<br />
A hierarquia de classes do pacote java.io, relativos à Streams de entrada, aparece na figura abaixo. Para<br />
Streams de saída vale a mesma hierarquia, só que você deve substituir a palavra Input por Output em<br />
todas as classes.<br />
118
SequenceInputStream<br />
DataInputStream<br />
PipedInputStream<br />
InputStream<br />
FilterInpuStream StringBufferInputStream<br />
FileInputStream e FileOutputStream<br />
São classes usadas para a manipulação de arquivos, permitindo que você especifique o caminho<br />
completo do arquivo como parâmetro do construtor desses objetos classes. Para que um objeto<br />
FileInputStream seja construído com sucesso, o arquivo especificado no construtor deve existir e estar<br />
disponível para leuitura (não bloqueado). No caso de um FileOutputStream, se o arquivo destino já<br />
existir, ele será sobrescrito. A forma de construção dos Streams associados a arquivos aparece abaixo:<br />
FileInputStream entrada = new FileInputStream("c:/documentos/teste.txt");<br />
FileOutputStream saida = new FileInputStream("saida.txt");<br />
o separador de pastas utilizados em endereços de Streams <strong>Java</strong> segue o padrão UNIX, ou seja, é o<br />
''/'. Se você quiser adotar o padrão windows, você deve colocar duas barras: '\\' pois somente uma será<br />
confundida com a definição de algum caractere especial. Exemplo: "c:/teste/arq.txt" é o mesmo que<br />
"c:\\teste\\arq.txt". Por questão de portabilidade, acostume-se a sempre usar o padrão UNIX, com a barra<br />
invertida '/'.<br />
BufferedInputStream e BufferedOutputStream<br />
Um dos problemas de se usar as classes InputStream e OutputStream, é que elas só permitem a<br />
transferência byte a byte dos dados, o que é lento devido ao processo físico de transferência de bytes<br />
entre dispositivos de armazenamento e a memória. Para contornar isso, foram definidos Streams com<br />
buffer, ou seja, que permitem a leitura ou escrita de quantidades maiores de bytes, normalmente<br />
controlados por algum delimitador. Isso faz com que o número de acessos ao dispositivo de<br />
armazenamento diminua - um disco, por exemplo – aumentando o desempenho geral do sistema. Em<br />
casos onde o número de bytes a ser lido ou escrito é pequeno (
DataInputStream e DataOutputStream<br />
O raciocínio em bytes é pouco intuitivo, então a API <strong>Java</strong> provê classes que permitem a manipulação de<br />
tipos primitivos de dados, tornando a lógica de métodos de leitura e escrita mais clara em relação ao<br />
quê está sendo transferido entre um dispositivo de armazenamento e a memória da máquina virtual.<br />
Alguns métodos dessas classes aparecem descritos abaixo:<br />
public class DataInputStream extends FilterInputStream implements DataInput<br />
byte readByte()<br />
long readLong()<br />
double readDouble()<br />
public class DataOutputStream extends FilterOutputStream implements DataOutput<br />
void writeByte(byte)<br />
void writeLong(long)<br />
void writeDouble(double)<br />
Apesar da Strings serem muitas vezes tratados como tipos primitivos de dados, e existir métodos de<br />
leitura e escrita de String nas classes DataInputStream e DataOutputStream, você deve sempre optar por<br />
manipular a transferência de Strings em Streams como serialização de objetos, que veremos na<br />
seqüência desse texto.<br />
É fortemente recomendado que você leia as informações sobre conversão de caracteres que aparece<br />
na documentação da classe DataInputStream.<br />
PipedInputStream e PipedOutputStream<br />
Essas classes permitem a transferência de bytes entre Threads, ou seja, a origem e o destino da<br />
transferência não são dispositivos de armazenamento mas sim processos na memória. Além disso, por<br />
serem threads, esses processos tem comportamento diferenciado e, muitas vezes, obrigam a<br />
transferência de bytes a ser sincronizada para evitar problemas de inconsistência de dados e/ou deadlock.<br />
A thread de origem deve instanciar um objeto PipedoutputStream, enquanto a thread destino deve<br />
instanciar um objeto PipedInputStream.<br />
Leitores e Escritores de dados em Streams com buffer<br />
As classes descritas anteriormente permitem que um processo computacional leia e escreva bytes em<br />
um dispositivo de armazenamento ou em outro processo. Porém, por questões de clareza de código e<br />
desempenho costuma-se utilizar essas classes na construção de objetos de leitura e escrita com um<br />
buffer, permitindo a transferência de grandes quantidades de dados entre a origem e o destino de um<br />
Stream. Tal técnica aparece no exemplo abaixo, originalmente introduzido na aula 6:<br />
/**<br />
* FIC - Faculdade Integrada do Ceará<br />
* Sistemas Orientados a Objetos I<br />
* Lendo valores do teclado<br />
*/<br />
// Tem que importar a biblioteca de acesso aos<br />
120
dispositivos de Entrada e Saída (I/O) do <strong>Java</strong>:<br />
import java.io.*;<br />
public class Teclado<br />
{<br />
static public void main(String[] args)<br />
{<br />
// Tem que usar tratamento de exceções,<br />
// conforme explicado em aula.<br />
try<br />
{<br />
// Essas duas linhas criam um "leitor com buffer"<br />
// do dispositivo padrão de entrada do <strong>Java</strong>:<br />
// o teclado (System.in).<br />
InputStreamReader dados = new InputStreamReader(System.in);<br />
BufferedReader teclado = new BufferedReader(dados);<br />
System.out.print("digite uma frase: ");<br />
String frase = teclado.readLine();<br />
System.out.println("Frase digitada:\t" + frase);<br />
}<br />
catch(Exception erro)<br />
{<br />
}<br />
}<br />
}<br />
Conversão entre bytes e caracteres<br />
Em <strong>Java</strong>, quando cria-se um leitor ou escritor a partir de um Stream, a conversão entre os bytes<br />
transferidos entre a origem e o destino do Stream serão convertidos segundo o padrão de caracteres da<br />
plataforma onde o sistemas está rodando e a tabela UNICODE. Em lugares onde o padrão é o Latin-1<br />
(Ascii), como no Brasil, a codificação de bytes utilizada será a ISO 8859-1 (para a nossa sorte, o padrão<br />
de codificação <strong>Java</strong> é o mesmo utilizado nos computadores brasileiros, então não há necessidade de<br />
preocupações com conversões entre símbolos – a menos que você venha a produzir programas para<br />
outros países). Você pode definir qual o padrão de codificação a ser adotado pela máquina virtual,<br />
devendo para isso consultar a documentação da ferramenta native2ascii que acompanha o jdk.<br />
Esse esquema de conversão permite que os programas <strong>Java</strong> sejam portáveis a computadores de todo<br />
mundo, uma vez que os processos da memória sempre trabalharão com o UNICODE. Você pode passar<br />
o padrão de codificação no construtor dos Streams, como mostra o exemplo abaixo:<br />
InputStreamReader leitor = new inputStreamReader(System.in, "8859_1");<br />
O que é UNICODE?<br />
<strong>Java</strong> utiliza uma tabela, chamada de UNICODE, para representar os símbolos utilizados na maioria das<br />
línguas conhecidas no mundo. Essa tabela é usada para converter os símbolos utilizados pela plataforma<br />
onde a máquina virtual está rodando e os programas <strong>Java</strong> – que, na verdade, passam a reconhecer<br />
esses símbolos apenas como entradas na tabela UNICODE e não pelo significado real desses símbolos<br />
na linguagem padrão dessa plataforma.<br />
121
Manipulação de arquivos seqüenciais<br />
A manipulação de arquivos, seqüenciais ou randômicos, passa pela construção de objetos da classe<br />
java.io.File, que provê, além do acesso físico ao arquivo, uma série de métodos que facilitam o<br />
controle sobre o conteúdo e as informações básicas desses arquivos, como data da última modificação,<br />
tamanho, etc.<br />
Criando objetos do tipo File:<br />
// construtor de objeto arquivo no diretório corrente<br />
File arquivo = new File("origem.txt");<br />
// construtor com diretório especificado no construtor<br />
File arquivo = new File("/dados/", "origem.txt");<br />
// Uso de variáveis para a identificação do diretório<br />
// e do arquivo a ser aberto<br />
String diretório = "/";<br />
String arquivo = "teste.txt";<br />
File arquivo = new File(diretório, arquivo);<br />
A partir de um objeto do tipo File, você pode criar Streams de entrada ou saída, conforme mostra o<br />
exemplo abaixo:<br />
/**<br />
* Método de leitura de arquivos texto.<br />
* @param arquivo O objeto associado a um arquivo<br />
*/<br />
static public byte[] carregar(File arquivo)<br />
throws Exception<br />
{<br />
FileInputStream dispositivoDeEntrada =<br />
new FileInputStream(arquivo);<br />
byte[] conteudo = new byte[dispositivoDeEntrada.available()];<br />
dispositivoDeEntrada.read(conteudo);<br />
return conteudo;<br />
}<br />
/**<br />
* Método de gravação em arquivos texto.<br />
* @param arquivo O objeto associado a um arquivo<br />
* @param conteúdo O texto a ser escrito no arquivo<br />
*/<br />
static public void salvar(File arquivo, String conteudo)<br />
throws IOException, Exception<br />
{<br />
FileOutputStream streamDeSaida = new FileOutputStream(arquivo);<br />
streamDeSaida.write(conteudo.getBytes());<br />
streamDeSaida.close();<br />
}<br />
Apesar das classes FileInputStream e FileOutputStream permitirem a passagem do nome dos arquivos<br />
em seus construtores, é fortemente recomendado que você sempre crie um objeto da classe File para<br />
122
eferenciar um arquivo, evitando problemas de dependência à organização dos arquivos no sistema em<br />
que a máquina virtual está rodando. E lembre-se de usar sempre a barra invertida '\' como separador de<br />
diretórios. É fortemente recomendado que você leia a documentação da classe <strong>Java</strong>.io.File acerca das<br />
facilidades de manipulação das informações sobre o conteúdo e as características básicas dos arquivos.<br />
Manipulação de arquivos randômicos<br />
Um dos problemas do uso de arquivos seqüenciais é a necessidade de ler ou escrever todo o conteúdo<br />
desses arquivos, mesmo que um determinado processo necessite apenas de uma parte de seu<br />
conteúdo. Imagine que você tenha um arquivo texto com quinhentos nomes, e precise verificar o<br />
centésimo nome – nesse caso, você terá que ler 99 nomes desnecessários até obter a informação que<br />
necessita. O ideal, nesse caso, é manipular arquivos como se fossem bancos de dados, ou seja, ter a<br />
capacidade de acessar um dado em uma posição específica dentro do arquivo, sem a necessidade de<br />
percorrer o resto desse arquivo. <strong>Java</strong> provê classes para a manipulação de arquivos de acesso<br />
randômico, ou seja, arquivos que possuem um ponteiro (file pointer) que permite a localização dos<br />
dados dentro do arquivo.<br />
Para manipular um arquivo de acesso randômico, constrói-se um objeto da classe<br />
java.io.RandomAccessFile, passando como parâmetro um objeto do tipo File:<br />
File arquivo = new File("origem.txt");<br />
RandomAccessFile manipulador = new RandomAccessFile(arquivo, "rw");<br />
Note que o primeiro argumento do construtor do manipulador de arquivos randômicos é a referência ao<br />
arquivo, enquanto o segundo argumento é o tipo de acesso que será utilizado:<br />
r Read Only, significando que o acesso ao arquivo será apenas para leitura<br />
rw Read and Write, significando que o acesso permite a leitura e/ou modificação do conteúdo do<br />
arquivo.<br />
Como a classe RandomAccessFile implementa as interfaces DataInput e DataOutput, o acesso aos<br />
dados de um arquivo de acesso randômico é feito através dos métodos definidos nessas interfaces,<br />
basicamente read() e write(). Leia a documentação da classe java.io.RandomAccessFile para a<br />
completa descrição dos método de leitura e escrita, bem como os parâmetros desses métodos.<br />
O posicionamento da leitura ou escrita em arquivos randômicos é ajustado ou reconhecido através de<br />
dois métodos:<br />
void seek(long)<br />
long getFilePointer()<br />
long lenght()<br />
O método seek ajusta o ponteiro do arquivo a uma determinada posição, considerando o intervalo padrão<br />
do arquivo, que pode ser o tamanho de um tipo de dado ou definido pelo programador. O método<br />
getFilePointer retorna a posição atual desse ponteiro, e o método lenght retorna o tamanho total do<br />
arquivo. Exemplo de posicionamento em um arquivo de acesso randômico:<br />
// Criando a referência ao arquivo de acesso randômico<br />
File arquivo = new File("vendas.log");<br />
123
RandomAccessFile manipulador = new RandomAccessFile(arquivo, "rw");<br />
// Posicionando o file pointer no final do arquivo<br />
manipulador.seek(manipulador.lenght());<br />
// Todos os comandos write() a partir de agora escreverão dados<br />
// no final do arquivo (append)<br />
Arquivos de acesso randômico são uma interessante forma de acesso a dados quando lidamos com<br />
quantidades pequenas de informação (
}<br />
// .... restante da classe<br />
// IMPORTANTE: membros estáticos não são serializáveis.<br />
Escrevendo objetos serializados em um arquivo - para os exemplos sobre serialização utilizaremos<br />
a classe <strong>Java</strong>.útil.Date, que é serializável.<br />
// O construtor vazio da classe Date assume a data e hora atuais<br />
java.util.Date data = new Date();<br />
File arquivo = new File("data.txt");<br />
FileoutputStream saida = new FileoutputStream(arquivo);<br />
ObjectOutputStream escritor = new ObjectOutputStream(saida);<br />
// O processo de gravação e leitura de dados em arquivos sempre<br />
// deve ser feito prevendo-se o tratamento de exceções<br />
try<br />
{<br />
escritor.write(data);<br />
escritor.close();<br />
}<br />
Catch(IOException erro)<br />
{<br />
erro.printStackTrace();<br />
}<br />
Lendo objetos serializados de um arquivo<br />
java.util.Date data = null;<br />
File arquivo = new File("data.txt");<br />
FileInputStream entrada = new FileInputStream(arquivo);<br />
ObjectInputStream leitor = new ObjectInputStream(entrada);<br />
// O processo de gravação e leitura de dados em arquivos sempre<br />
// deve ser feito prevendo-se o tratamento de exceções<br />
try<br />
{<br />
// O casting sempre deve ser feito, porque o retorno<br />
// de uma leitura via ObjectInputStream é sempre<br />
// um objeto da superclasse Object, que deve ser convertido<br />
// conforme o tipo de dado utilizado<br />
data = (Date)leitor.readObject();<br />
leitor.close();<br />
System.out.println("objeto lido: " + data);<br />
}<br />
Catch(IOException erro)<br />
{<br />
erro.printStackTrace();<br />
}<br />
125
Exercícios<br />
14. Crie um aplicativo, com interface gráfica, que lhe permita ler, criar ou modificar arquivos texto,<br />
seqüenciais e randômicos. Dica: dê uma olhada na classe javax.swing.JfileChooser.<br />
15. Modifique o seu trabalho 3, permitindo a gravação em disco do jogo como um objeto serializável.<br />
Faça com que o usuário possa armazenar o seu jogo em disco para poder continuar depois. Não<br />
esqueça de incluir também o método de recuperação desse objeto – a leitura do disco.<br />
126