UNIVERSIDADE FEDERAL DE VIÇOSA DEPARTAMENTO DE INFORMÁTICA JAVA NA PRÁTICA. Volume I. Alcione de Paiva Oliveira Vinícius Valente Maciel
|
|
- Adelino Carmona Vilarinho
- 8 Há anos
- Visualizações:
Transcrição
1 UNIVERSIDADE FEDERAL DE VIÇOSA DEPARTAMENTO DE INFORMÁTICA JAVA NA PRÁTICA Volume I Alcione de Paiva Oliveira Vinícius Valente Maciel 2002
2 Sumário 1 Capítulo I - Introdução... 5 CONVENÇÕES... 9 Capítulo II - Programação Orientada a Objetos CLASSES E OBJETOS E LINGUAGENS DE PROGRAMAÇÃO Ocultando de Informação Especialização e Herança Sobrescrita, Sobrecarga e Polimorfismo INTRODUÇÃO À DIAGRAMA DE CLASSES Diagrama de Classes Capítulo III - Introdução à Linguagem Java PALAVRAS RESERVADAS LITERAIS SEPARADORES TIPOS DE DADOS Tipos de dados simples Tipos de dados compostos CONVERSÃO DE TIPOS OPERADORES Expressões e Precedência entre Operadores COMENTÁRIOS BLOCOS E ESCOPO ESTRUTURAS DE CONTROLE Seleção Repetição break e continue ARGUMENTOS DA LINHA DE COMANDO ASSERT (ASSERTIVAS) Sintaxe e semântica Habilitando e Desabilitando Assertivas Capítulo IV Classes, Packages e Interfaces CLASSES Construtores Valor de Retorno...68 OBJETOS MODIFICADORES DE ACESSO Outros Modificadores REFERÊNCIAS COMPARTILHADAS COPIANDO OBJETOS O objeto this PACKAGES Usando Packages Criando Packages Empacotando packages em arquivos JARs O Mecanismo de Extensão DERIVANDO CLASSES... 94
3 super A classe Object Sobrescrita e Polimorfismo CLASSES E MÉTODOS ABSTRATOS INTERFACES CLASSES INTERNAS Classes Internas Anônimas INICIALIZAÇÃO E FINALIZAÇÃO DE OBJETOS Inicialização automática Inicialização de atributos de Instância Inicialização de atributos de classe Blocos de inicialização Ordem de inicialização Finalização de Objetos CONVERSÃO DE OBJETOS EXCEÇÕES Continuação após o Tratamento da Exceção A hierarquia de Exceções Capturando mais de uma exceção Lançando exceções Comportamento do Sistema diante das Exceções Criando suas próprias exceções A cláusula finally DOCUMENTANDO O CÓDIGO Rótulos HTML embutida AGENDA ELETRÔNICA VERSÃO CONSOLE Capítulo V Entrada e Saída (java.io) ACESSO SEQUENCIAL ACESSO DIRETO Capítulo VI java.util LIDANDO COM COLEÇÕES As Interfaces Iterator e Enumeration Vector Stack Hashtable MISCELÂNEA DE CLASSES DO PACOTE JAVA.UTIL Arrays Date Observable StringTokenizer AGENDA ELETRÔNICA VERSÃO CONSOLE Capítulo VII - Serialização e Persistência AGENDA ELETRÔNICA VERSÃO CONSOLE Capítulo VIII AWT (Abstract Window Toolkit) A HIERARQUIA DE COMPONENTES OLÁ MUNDO AWT TRATAMENTO DE EVENTOS
4 Modelo de Eventos Tratamento de Eventos com classes Internas EXEMPLO BÁSICO ACRESCENTANDO CORES GERENCIANDO O LAYOUT Exemplo com BorderLayout Exemplo com FlowLayout Exemplo com CardLayout Exemplo com GridLayout Exemplo com GridBagLayout UTILIZANDO LISTAS TRABALHANDO COM MENUS E DIÁLOGOS CAPTURANDO EVENTOS DO TECLADO PRINCIPAIS CLASSES Color Component Button Label List TextField TextArea CONTAINERS Panel Frame AGENDA ELETRÔNICA VERSÃO GRÁFICA Capítulo IX - Applets DESCRIÇÃO DO CÓDIGO HTML MÉTODOS DA CLASSE APPLET EXIBINDO UMA IMAGEM ÁUDIO OBTENDO PARÂMETROS EXECUTANDO UM APPLET COMO APLICAÇÃO PREPARANDO APPLETS PARA PRODUÇÃO E ARQUIVOS JARS AGENDA ELETRÔNICA VERSÃO APPLET Capítulo X JavaBean O QUE É UM JAVABEAN? JAVABEANS E FERRAMENTAS RAD PROPRIEDADES Simples Indexada Ligada (Bound) Restringidas(Constrained) EVENTOS DESENVOLVIMENTO DO EXEMPLO TimerEventListener TimerEvent TimerBean INSTALANDO O BEANS DEVELOPMENT KIT (BDK)
5 TESTANDO EXEMPLO NO BDK Capítulo XI Perguntas Frequentes Bibliografia Links Índice
6 5 Capítulo I - Introdução Java é uma linguagem de programação desenvolvida pela Sun Microsystems e lançada em versão beta em O seu desenvolvimento foi iniciado em 1991 pela equipe liderada por James Gosling visando o mercado de bens eletrônicos de consumo. Por isso foi projetada desde o início para ser independente de hardware, uma vez que as características dos equipamentos variam amplamente neste nicho de desenvolvimento. Outro objetivo estabelecido desde sua concepção foi o de ser uma linguagem segura. Segura tanto no sentido de evitar algumas falhas comuns que os programadores costumam cometer durante o desenvolvimento, como no sentido de evitar ataques externos. Isto é importante no mercado de bens eletrônicos de consumo por que ninguém gostaria de adquirir um produto que necessitasse desligar e religar para que voltasse a funcionar corretamente. Estas características despertaram o interesse para utilização de Java em outro ambiente que também necessitava de uma linguagem com este perfil: a Internet. A Internet também é um ambiente constituído por equipamentos de diferentes arquiteturas e necessita muito de uma linguagem que permita a construção de aplicativos seguros. Muitas pessoas argumentarão que estas características podem ser encontradas em outras linguagens e portanto isto não explica o súbito sucesso da linguagem. Podemos arriscar alguns palpites apesar de este ser um terreno um pouco pantanoso para se aventurar, até por que as linguagens de programação tendem assumir um caráter quase religioso. Uma das razões que na nossa opinião favoreceram a rápida adoção da linguagem foi a sintaxe. Java é sintaticamente muito semelhante à linguagem C/C++, apesar de existirem diferenças fundamentais na filosofia de implementação entre as duas linguagens. Isto facilitou a migração de uma legião imensa de programadores C/C++ para a nova linguagem. Outra razão que não pode ser desprezada é o momento atual onde os desenvolvedores estão ansiosos para se libertarem de sistemas proprietários. Portanto, apesar de não serem novas as idéias embutidas na linguagem Java, a reunião delas em uma só linguagem, juntamente com a facilidade migração dos programadores e o momento atual, contribuíram para o rápido sucesso da linguagem. Hoje, segundo a International Data Corp. (IDC), existem mais de 2 milhões de programadores Java no mundo e a estimativa é que o número de desenvolvedores ultrapasse os 5 milhões em O número de programadores Java deve ultrapassar o de programadores C++ ainda este ano (2002), segundo a consultoria americana Evans Data. Os profissionais que dominam a linguagem
7 estão entre os mais bem pagos da área de Tecnologia da Informação (TI), com salários variando de 3 a 10 mil reais, podendo em alguns casos chegar à 16 mil reais, segundo a revista Info Exame (dezembro de 2001). A lista abaixo apresenta as principais características de Java de modo que o leitor tenha uma visão geral da linguagem: Orientação a objetos. Java não é uma linguagem totalmente orientada a objetos como Smalltalk, onde tudo é objeto ou métodos de objetos. Por questões de eficiência foram mantidos alguns tipos primitivos e suas operações. No entanto, Java possui um grau de orientação a objetos bem maior que C/C++, o que a torna bem mais harmoniosa e fácil de assimilar, uma vez que o programador tenha compreendido esta forma de desenvolvimento. Compilação do código fonte para código de uma máquina virtual (Bytecodes). Esta característica visa tornar a linguagem independente de plataforma de Hardware e Sistema Operacional. Obviamente é necessário que exista um programa capaz de interpretar o código em Bytecodes para cada Sistema Operacional, denominado de Máquina Virtual. No entanto, nada impede que o código fonte seja traduzido diretamente para o código executável na máquina de destino. Já existem ambientes de desenvolvimento que apresentam este tipo de opção. Alternativamente, é possível projetar equipamentos que processem em hardware os Bytecodes. A Sun desenvolveu um processador que executa operações em Bytecodes, denominado de JavaChip. O diagrama abaixo ilustra as etapas envolvidas na execução de um código Java. 6 Figura I-1. Fases para execução de um programa fonte em Java Ausência de manipulação explícita de ponteiros. Em linguagens como C/C++ e Pascal existe o tipo ponteiro como tipo primitivo da linguagem. A especificação original de Pascal é restritiva no uso de ponteiros, permitindo que sejam usados apenas para referenciar memória obtida na área de alocação dinâmica (heap) e não permite que o programador examine o valor
8 da variável do tipo ponteiro, nem que realize operações aritméticas com ponteiros. Já a linguagem C/C++ permite que o valor armazenado na variável do tipo ponteiro faça referência a qualquer área de memória, inclusive à área estática e automática (pilha), além de permitir aritmética de ponteiros e o exame direto do valor armazenado. A manipulação do tipo ponteiro exige uma grande dose de atenção por parte do programador e mesmo programadores experientes frequentemente cometem erros no seu uso. Além disso, o uso de ponteiros é uma fonte de insegurança na linguagem, uma vez que permite que o usuário faça acesso a memória que pode pertencer a outros processos, abrindo a possibilidade para desenvolvimento de programas hostis ao sistema. A linguagem Java não possui o tipo ponteiro. Isto não que dizer que não seja possível realizar alocação dinâmica de memória. Todo objeto criado é alocado na área de heap, mas o usuário não pode manipular a referência ao objeto explicitamente. Recuperação automática de memória não utilizada (Coleta de Lixo Garbage Collection). Nas linguagens onde existe alocação dinâmica de memória, o programador é responsável pela liberação de memória previamente obtida na área de alocação dinâmica e que não está sendo mais utilizada. Se houver falhas na execução desta responsabilidade ocorrerá o problema que é conhecido sob a denominação de vazamento de memória. Este problema faz com que a partir de certo ponto o programa não consiga obter memória para criação de novos objetos, apesar de existir área que não está sendo mais usada mas que não foi devolvida ao gerente de memória. Outro erro comum é a tentativa de acesso á áreas de memória já liberadas. Todos os programadores que trabalham com linguagens que permitem alocação dinâmica conhecem bem estes problemas e sabem o quanto é difícil implementar programas que não possuam estes tipos de erros. A maior parte dos erros que ocorrem no uso destas linguagens é devido a problemas na alocação/liberação de memória. Visando o desenvolvimento de aplicações robustas, livres deste tipo de falha, os projetistas de Java incorporaram um procedimento de coleta automática de lixo à máquina virtual. Deste modo, os objetos que não estão sendo mais usados são identificados pelo procedimento, que libera a memória para ser utilizada na criação de novos objetos. Segurança. As pessoas costumam dizer que Java é uma linguagem segura. Mas o que é ser uma linguagem de programação segura? Segurança possui significados distintos para pessoas diferentes. No caso da linguagem Java na 7
9 versão 1.0 segurança significa impedir que programas hostis que possam causar danos ao ambiente computacional, ou que busquem informações sigilosas em computadores remotos para uso não autorizado. Na versão 1.1 foi adicionada a capacidade de permitir a verificação da identidade dos programas (autenticação) e na versão 1.2 os dados que os programas enviam e recebem podem ser criptografados por meio do uso de um pacote adicional. Na versão 1.4 o pacote de criptografia JCE (Java TM Cryptography Extension) foi incorporado ao J2SDK. Suporte à Concorrência. A construção de servidores, a criação de programas com interfaces gráficas, e programas semelhantes que tem em comum a necessidade de que o atendimento de uma solicitação não incapacite o sistema de responder a outras solicitações concorrentemente, demandam o uso de uma linguagem que facilite o desenvolvimento deste tipo de programa. As linguagens projetadas antes do surgimento destas necessidades, como C/C++, não previam facilidades para este tipo de programação, o que obrigou a incorporação destes recursos posteriormente, por meio de funções adicionais. Como a programação concorrente é uma forma de programação que difere bastante da programação sequencial convencional, a simples adição de novas funções para tentar adaptar a linguagem a esta forma de codificação, não cria um ajuste perfeito com a linguagem subjacente. Por outro lado, Java foi projetada visando facilitar a programação concorrente. Isto faz com que a criação linhas de execução (threads) seja bem mais natural dos que nas linguagens tradicionais.programação em rede. Java possui em seu núcleo básico classes para comunicação em rede por meio dos protocolos pertencentes à pilha de protocolos TCP/IP. A pilha de protocolos TCP/IP é a utilizada pela Internet e tornou-se o padrão de fato para comunicação entre computadores em uma rede heterogênea. Isto torna Java particularmente atrativa para o desenvolvimento de aplicações na Internet. Além disso Java está incorpora um amplo de conjunto de soluções para computação distribuída, como CORBA (Common Object Request Broker Architecture), RMI (Remote Method Invocation) e Servlets/JSP (aplicações Java que são executadas por servidores Web). 8 Após o lançamento da versão beta da linguagem em 1995, a Sun tem liberado diversas evoluções da linguagem na forma de versões e releases de um conjunto de ferramentas denominado de Java Development Kit (JDK) até a versão 1.2, quando se passou a denominar Java 2 SDK (Standard Development Kit). Isto ocorreu porque outros kits de desenvolvimento com propósitos
10 específicos foram lançados, como o J2EE (Java 2 Enterprise Edition), voltado para aplicações distribuídas escaláveis e o J2ME (Java 2 Micro Edition), voltado para aplicações embutidas em dispositivos eletrônicos (Celulares, handheld, etc.). Durante a elaboração deste livro, a última versão estável do SDK era a de número 1.4 que pode ser obtida gratuitamente no site Convenções As seguintes convenções são usadas neste livro. 1. Fontes com larguras constantes são usadas em: exemplos de código public class Ponto private int x,y; nomes de métodos, classes e variáveis mencionadas no texto. 2. Fontes com larguras constantes em negrito são usadas dentro de exemplos de códigos para destacar palavras chave. 3. Fontes em itálico são usadas: em termos estrangeiros; na primeira vez que for usado um termo cujo significado não for conhecimento generalizado. 9
11 Capítulo II - Programação Orientada a Objetos O mundo pode ser visto como um conjunto de objetos que se relacionam. Por exemplo, uma pessoa, uma casa, uma cadeira da casa, etc. Os objetos não são necessariamente físicos. Podem possuir uma natureza abstrata, como um evento (uma partida de futebol) ou algo inexistente no mundo real (elefante corde-rosa). Na verdade, o conceito de objeto atua no nível lógico e não no real. Se iremos representar algo como objeto ou não depende apenas de uma decisão a nível lógico que pode facilitar a simulação de determinado aspecto da realidade. Os objetos se agrupam em classes, segundo propriedades ou atributos comuns. Por exemplo, a classe dos retângulos agrupa todas as formas geométricas com a propriedade de possuir quatro lados formando ângulos de 90 o. A relação entre um objeto e uma classe é de pertinência. Dizemos que um objeto pertence a uma classe ou, mais comumente, que é uma instância de uma classe. O Figura abaixo ilustra exemplos de classes. 10 Figura II-1. Classe dos (a) retângulos e dos (b) dos triângulos. As classes podem ser relacionar com outra classe no sentido que uma classe pode conter outra. Por exemplo, a classe de retângulos está inserida em uma classe mais genérica, a classe dos polígonos. A classe mais genérica é denominada de Superclasse e as classes mais específicas são denominadas de
12 Subclasses. As subclasses herdam as propriedades das superclasses. No nosso exemplo, os polígonos possuem as propriedades de ter uma área, uma posição no plano, um número n de vértices e n-1 ângulos. Todas essas propriedades são herdadas tanto pela classe dos retângulos como pela classe do triângulos. Podemos desta forma organizar os objetos em uma hierarquia onde as classes mais específicas herdam as propriedades das classes mais genéricas. 11 Figura II-2. Classe dos polígonos. Os objetos de uma classe possuem comportamentos que podem alterar o valor de suas propriedades. Por exemplo, um carro pode sofrer uma aceleração ou ser freado e com isso alterar a sua velocidade. Um objeto qualquer pode ser deslocado, alterando assim as suas coordenadas no espaço. Classes e Objetos e Linguagens de Programação As linguagens de programação são utilizadas para construir simulações de aspectos da realidade no computador. Quanto mais facilmente pudermos expressar os conceitos capturados da realidade, mais facilmente construiremos a simulação. Seguindo este raciocínio podemos concluir que as linguagens que possuem facilidades para representação de objetos permitem uma modelagem mais fácil dos conceitos do mundo real. No entanto, podemos utilizar uma linguagem de programação convencional para modelar as classes e objetos abstraídos da realidade. Por exemplo, podemos modelar a classe dos retângulos por meio de um registro (record) em Pascal.
13 12 type Classe_Retangulo = record X1,Y1,X2,Y2: integer; end; Exemplo II-1. Representação da Classe Retângulo em Pascal. No Exemplo II-1 o retângulo é definido por dois pontos, sendo X1 e Y1 o ponto superior esquerdo e X2 e Y2 o ponto inferior direito. Os objetos podem ser representados por meio de variáveis do tipo definido: type Classe_Retangulo = record X1,Y1,X2,Y2: integer; end; var Retangulo1 : Classe_Retangulo; Exemplo II-2. Criação de objetos em Pascal. procedure intretangulo(xa,ya,xb,yb: integer; var R: Classe_Retangulo); begin R.X1 := XA; R.Y1 := YA; R.X2 := XB; R.Y2 := YB; end; procedure MudaPos(X,Y: integer; var R: Classe_Retangulo); begin R.X2 := X+(R.X2-R.X1); R.Y2 = Y+(R.Y2-R.Y1); R.X1 = X; R.Y1 = Y; end; Exemplo II-3. Definição das operações em Pascal.
14 As propriedades dos objetos podem ser manipuladas através de funções e procedimentos. Por exemplo, podemos ter uma operação para inicializar os valores da estrutura e outra para alterar a posição do retângulo. O Exemplo II-3 mostra essas duas operações. Contudo, existem algumas limitações das linguagens convencionais que as tornam inadequadas para a modelagem de objetos: Não existem recursos para ocultar a estrutura de dados de procedimentos que não foram projetados para a sua manipulação. É muito importante que a linguagem forneça recursos para se implementar este tipo de isolamento. Se acessarmos uma estrutura apenas por meio dos procedimentos projetados para este fim, quando a estrutura for alterada apenas os procedimentos que manipulam a estrutura sofreriam modificações. No entanto, se não agirmos desta forma será necessário procurar em todo o programa os acessos diretos à estrutura. Claro que este comportamento pode ser adotado em qualquer linguagem, mas é mais seguro se a linguagem fornece meios para o programador forçar este tipo de comportamento. A capacidade de esconder a estrutura de dados de acessos diretos é chamada de ocultação de informação. Não existem recursos para herança de propriedades entre classes e subclasses. Se precisarmos implementar uma estrutura que é uma especialização de outra já implementada, será preciso codificar novamente todas as propriedades, mesmo as comuns, e todos os procedimentos de acesso. Isto dificulta o reaproveitamento de código, o que, consequentemente aumenta o tempo de desenvolvimento e a possibilidade de erros. Não existe uma forma de relacionar explicitamente as estruturas de dados com os procedimentos que as manipulam. O relacionamento entre os procedimentos que manipulam uma estrutura de dados e a estrutura é estabelecido implicitamente, por meio de alguma convenção definida pelo programador. É importante que a linguagem obrigue o programador relacionar explicitamente os procedimentos com a estrutura de dados, de modo que fique claro qual é a interface de acesso ao objeto. A adoção de uma linguagem programação orientada a objetos resolve todos esses problemas. Existem várias linguagens comerciais com esta característica: Smalltalk, Eiffel, C++, etc. Algumas com elementos não orientados a objetos como C++, outras puramente orientadas a objetos como Smalltalk, onde tudo é 13
15 objeto. Java é uma linguagem orientada a objetos mais pura do que C++, fugindo desta orientação apenas em alguns pontos bem definidos, em nome da eficiência de execução. Em Java podemos representar diretamente as classes e objetos. Por exemplo, a classe retângulo seria declarada da seguinte forma: 14 class Retangulo int X1,Y1,X2,Y2; public Retangulo(int XA, int YA, int XB, int YB) X1 = XA; Y1 = YA; X2 = XB; Y2 = YB; void MudaPos(int X, int Y) X2 = X+(X2-X1); Y2 = Y+(Y2-Y1); X1 = X; Y1 = Y; ; Exemplo II-4.Representação da Classe Retângulo em Java. No Exemplo II-4 mudamos o nome do procedimento iniretangulo para Retangulo, que é o mesmo nome da classe. No momento não é importante entendermos a razão desta mudança, que será esclarecida no próximo capítulo. Note que os procedimentos são declarados dentro do corpo da classe, tornando explícito relacionamento entre a classe e os procedimento. As funções declaradas nas classes são chamadas de métodos e a partir de agora nos referenciaremos a eles como tal. Note também que diferentemente do exemplo em Pascal, não é preciso passar o objeto como parâmetro, uma vez que as variáveis que estão sendo modificadas pertencem ao objeto corrente, ao qual está associado o método. É como se para cada objeto de uma classe fossem criadas versões de todos os métodos da classe, de modo que cada método só opera sobre as variáveis do objeto a quem pertencem. Para declarar uma variável do tipo da classe basta preceder a variável com o nome da classe. Retangulo ret;
16 15 Até esse momento nenhum objeto foi criado. Para criar um objeto (instância) é usado o operador new. ret = new Retangulo(10,10,20,30); Note que o operador new é seguido de uma chamada ao método com o mesmo nome da classe. O métodos com esta característica são chamados de construtores e só podem ser invocados durante a criação de um objeto. Como veremos mais tarde, uma classe pode ter mais de um construtor. Após a criação do objeto é possível acessar os outros métodos do objeto através do operador.. Por exemplo, podemos mudar a posição do objeto por meio do método MudaPos. ret.mudapos(40,40); Como já dissemos, não é preciso passar o objeto como argumento, já que é criada uma cópia do método para cada objeto. A grosso modo podemos dizer que cada instância da classe recebe uma cópia da variáveis e dos métodos da classe. Ocultando de Informação O projetista cuidadoso deve ocultar a representação interna da classe, permitindo o acesso aos atributos da classe via métodos predefinidos. Desta forma a representação interna fica isolada do restante do programa e fica mais fácil alterá-la sem que seja preciso alterar outras partes do código. A ocultação de informação é obtida por meio de qualificadores, como o private, que impede o acesso à variáveis via métodos definidos em outras classes. O nível de ocultação depende do qualificador utilizado. Todos os qualificadores serão abordados com detalhes no Capítulo IV. O exemplo II-5 mostra como impedir que as variáveis declaradas na classe Retangulo sejam acessadas diretamente. class Retangulo private int X1,Y1,X2,Y2; public Retangulo(int XA, int YA, int XB, int YB) X1 = XA; Y1 = YA;
17 X2 = XB; Y2 = YB; Java na Prática 16 void MudaPos(int X, int Y) X2 = X+(X2-X1); Y2 = Y+(Y2-Y1); X1 = X; Y1 = Y; ; Exemplo II-5. Ocultando informação em Java. Especialização e Herança Para criar uma subclasse de uma classe pré-existente utilizamos o operador extends. Por exemplo, podemos definir uma subclasse da classe Retangulo, chamada de RetanguloColorido, que possui, além das variáveis e métodos herdados da superclasse, uma variável para armazenar a cor do retângulo, juntamente com um método para alterar o valor. class RetanguloColorido extends Retangulo private Color Cor; void AtribuiCor(Color C) Cor = C; ; Exemplo II-6. Declarando subclasses em Java. A princípio, subclasse pode acessar todos os métodos e variáveis da superclasse. No entanto, isto também pode ser alterado via qualificadores.
18 Sobrescrita, Sobrecarga e Polimorfismo 17 Podemos definir mais de um método com o mesmo nome na mesma classe ou subclasses. Caso o método possua a mesma assinatura (número e tipos de argumentos e tipo de retorno) que outro método, então o método não pode pertencer à mesma classe do anterior. Se ambos os métodos estiverem na mesma linha hierárquica (classe/subclasse), dizemos que o método da subclasse sobrescreve o método da superclasse. O método que será executado dependerá da classe do objeto. class Empregado protected float salario; public float getsalario() return salario; class Vendedor extends Empregado protected float comissao; public float getsalario() return salario+comissao; Exemplo II-7. Sobrescrita do método getsalario(). No exemplo II-7 o método getsalario() da classe Vendedor sobrescreve o método do mesmo nome da classe Empregado. Se a assinatura do método for diferente de outro método com o mesmo nome definido anteriormente na mesma classe ou em outra classe da mesma linha hierárquica, então estamos realizando uma sobrecarga sobre o identificador do método. Quando for usado o identificador dentro do código de um programa o método invocado será determinado pela classe a que pertence o objeto do método e pelo número e tipos dos argumentos passados para o método. O termo sobrecarga advém do fato de um mesmo identificador denotar mais de método. class Empregado protected float salario; public void aumento() salario= salario*10.0; public void aumento(float porcent)
19 Java na Prática salario= salario*porcent; 18 Exemplo II-8. Sobrecarga do método aumento(). No exemplo II-8 o identificador aumento pode referenciar dois métodos distintos. Um aumenta o salário em 10% e no outro o aumento depende do valor da porcentagem passado como parâmetro. Note que as assinaturas do métodos diferem entre si. Alguns autores chamam sobrecarga de polimorfismo, que é a habilidade de um determinado objeto se comportar ou ser visto de diferentes formas, quando na verdade a sobrecarga é um tipo particular de polimorfismo, chamado de polimorfismo ad hoc. Na sobrecarga um identificador representa vários métodos com computações distintas. Existe também o polimorfismo paramétrico, onde um método pode realizar a mesma computação sobre objetos de tipos distintos. Isso pode ser implementado em Java definindo um método que recebe e retorna objetos da classe Object. Como a classe Object é a mãe de todas as classes o método pode operar da mesma forma independente da classe a qual o objeto realmente pertence, desde que a computação seja independente da classe. class Poli public Object identidade(object objeto) return Object; Exemplo II-9. Polimorfismo paramétrico. No Exemplo II-9 o método identidade() retorna o objeto passado como parâmetro. Este método realiza a mesma computação, independentemente da classe do objeto. Obviamente é um exemplo muito simples e sem utilidade prática mas serve para ilustrar o conceito de polimorfismo paramétrico.
20 Introdução à Diagrama de Classes 19 É possível registrar diretamente em uma linguagem de programação os objetos percebidos em uma determinada realidade. No entanto, é melhor utilizarmos uma notação gráfica intermediária para melhor visualizarmos os objetos e as relações entre objetos, e ir alterando esta representação até estarmos seguros que possuímos um entendimento razoável do problema, e ai sim, partirmos para a codificação da solução. Muitas vezes é preciso recorrer a mais de uma notação gráfica, de modo a expressar várias facetas da realidade que está sendo modelada. Neste livro, recorreremos ao uso de notação gráfica em alguns exemplos para ilustrarmos a arquitetura dos programas antes de apresentarmos o código. Acreditamos que desta forma o leitor compreenderá melhor os exemplos. A notação gráfica que adotamos mostra as relações estáticas entre classes de objetos. Ela faz parte do conjunto de notações da UML (Unified Modeling Language ou Linguagem de Modelagem Unificada) proposta por Grady Booch, James Rumbaugh e Ivar Jacobson em A UML é um conjunto de notações que tem por objetivo modelar diversos aspectos de um sistema em diferentes níveis de abstração. Ou seja, pode ser utilizado para a captura de requisitos de um sistema assim como em projeto de programas. É voltada para análise e projeto de sistemas orientados a objetos. A área de análise e projeto orientados a objetos ainda não possui uma notação vencedora como existe para a análise e projeto estruturado. Contudo, a UML vem se popularizando rapidamente, e é encontrada com facilidade em textos de programação, sobretudo em se tratando de Java. Portanto, um conhecimento sobre as principais notações que constituem a UML é importante para qualquer um que deseja ingressar na área de programação orientada a objetos. Este livro utiliza uma das linguagens, ou diagramas, que compõem a UML: o diagrama de classes. Diagrama de Classes O Diagrama de Classes representa graficamente as classes do sistema e o relacionamento estático entre as classes, isto é, o relacionamento que não muda com o tempo. Por exemplo, em um sistema acadêmico, um aluno cursa várias disciplinas. O número de disciplinas e a disciplina que efetivamente está sendo cursada pode alterar, mas o vínculo aluno-cursa-displinas permanece. Para ilustrar o nosso estudo dos diagramas da UML utilizaremos exemplos sobre modelagem de aspectos realidade acadêmica.
21 Uma classe é representada no diagrama de classes por meio de um retângulo, que pode ser dividido em até três seções horizontais, como mostrado na figura abaixo: 20 Figura II-3. Forma geral para representação de uma classe. A seção superior é usada para registrar o nome da classe. A seção intermediária é reservada para registro das propriedades da classe, caso existam, e na seção inferior é registrado a assinatura dos métodos que pertencem à classe, caso existam. Por assinatura do método queremos dizer o nome do método, juntamente com seus argumentos e valor de retorno. A figura abaixo mostra uma representação da classe das disciplinas. Figura II-4. Representação da classe das disciplinas De modo geral, por razões de simplicidade, não se representam os métodos que tratam da alteração dos atributos e construtores. Se Assumi que toda classe possui estes métodos. Portanto, podemos simplificar a representação acima, omitindo a seção do métodos.
22 21 Figura II-5. Representação simplificada da classe das disciplinas. Podemos indicar tanto nos atributos quanto nas classes a visibilidade deles em relação a outras classes. As visibilidades possíveis, juntamente com os símbolos adotados estão listados na tabela abaixo: Visibilidade Símbolo Descrição Pública + Sem restrição de acesso. Protegida # Pode ser acessado apenas na própria classe e por subclasses. Privada - Pode ser acessado apenas na própria classe. Tabela II-1. Visibilidades possíveis para atributos e métodos. A visibilidade é atribuída a um atributo ou método precedendo a declaração do método com o símbolo adequado, como na figura abaixo: Figura II-6. Representação com visibilidade.
23 As classes podem se relacionar de diversas formas: por associação comum, por agregação e generalização. Abaixo é apresentada cada forma de relacionamento, juntamente com suas as notações. Associação comum A notação utilizada para associar duas classes é simplesmente uma linha unindo as classes. A figura II.7 mostra a associação entre a classe dos alunos e a classe das disciplinas. 22 Figura II-7. Associação entre Aluno e Disciplina. A figura acima expressa que alunos se associam com disciplinas mas não indica se um aluno se relaciona com várias ou apenas uma disciplina. Esta informação é chamada de cardinalidade da relação e é expressa anotando-se o valor da cardinalidade na associação junto à classe que está sendo relacionada. Assim, a figura II.8 expressa que um aluno se relaciona com várias disciplinas. Figura II-8. Associação de um Aluno com várias Disciplinas. Como uma disciplina se relaciona com vários alunos, o diagrama completo é o representado na figura II-9. Figura II-9. Associação de vários Aluno com várias Disciplinas. A tabela II-2 mostra algumas representações de cardinalidade:
24 23 Notação Descrição 1 Exatamente um * ou 0..* Zero ou mais 0..1 Opcional (zero ou um) n..m Máximo e mínimo Tabela II-2. Representações de cardinalidade. Até agora apresentamos apenas associações entre duas classes, mas nada impede que mais de duas classes participem de uma associação. Por exemplo, a figura II-10 ilustra uma associação ternária que representa o fato de um aluno cursar uma disciplina em um período. Figura II-10. Associação entre aluno, disciplina e período. Uma associação pode ter atributos próprios. Ou seja, atributos que não pertençam a nenhuma das classes envolvidas na associação mas sim à própria associação. Na associação entre alunos e disciplina o atributo nota, não pertence a aluno, tampouco à disciplina, uma vez que para saber uma nota é preciso saber quem é o aluno e qual é a disciplina. A representação de atributos da associação é representada por meio de um retângulo ligado à associação por meio de uma linha tracejada.
25 24 Figura II-11. Representação de atributos de associação. Agregação Alguns objetos são compostos por outros objetos. Por exemplo, um carro é composto por chassi, lataria, pneus e motor, que por sua vez é composto pelo carburador, pistões, bloco, etc. Este tipo de associação é representada por uma linha com um losango na ponta. Figura II-12. Agregação entre Curso e Disciplina. Generalização O último tipo de associação entre classes é o que o ocorre entre superclasses e subclasses. Uma superclasse é uma generalização das suas subclasses, que herdam os atributos e métodos da primeira. A notação utilizada para representar a generalização é uma linha com um triângulo na extremidade da associação no lado da classe mais genérica.
26 25 Figura II-13. Representação da generalização. A figura II-14 procura representar todas associações discutidas em um único diagrama. Figura II-14. Associação entre as classes do domínio acadêmico.
27 26 Capítulo III - Introdução à Linguagem Java Existe uma tradição entre os programadores que estabelece que ao se começar a aprender uma nova linguagem de programação, o primeiro programa a ser escrito deve ser um que imprima a frase Olá mundo em um dispositivo de saída. Dizem que isto atrai a sorte e espanta os bugs. Independente da crença geral, existem algumas razões bastante justificáveis para se começar o aprendizado de uma linguagem executando logo um programa, mesmo sem ter muita idéia do que se está fazendo. Primeiramente, existe o fator psicológico. Iniciar o aprendizado executando um programa sem erros, aumenta confiança do aluno e elimina temores de se estar aprendendo algo muito complexo. Existe também o fato de que apesar de um programa muito simples dar uma visão um pouco limitada da linguagem, já é possível observar alguns elementos importantes. Afinal trata-se de um programa completo. Portanto, para não fugir a tradição, eis o programa OlaMundo em Java: public class OlaMundo public void exibeola() System.out.println( Ola, Mundo! ); public static void main(string args[]) OlaMundo obj = new OlaMundo(); Obj.exibeOla(); Exemplo III-1. Programa OlaMundo. O programa acima é composto por uma única classe que possui apenas dois métodos. Isto é importante, porque não é possível fazer um programa Java sem recorrer às classes, uma vez que os procedimentos são definidos como métodos de classes. Isto não é verdade em linguagens como C++, o que diminui o seu grau de orientação a objetos.
28 Os métodos com o nome main são métodos especiais e servem como ponto inicial para execução do programa. Ou seja, a execução do programa será iniciada a partir da execução de um método main(). A assinatura do método main é sempre a mesma e a sua descrição em detalhes será vista na seção Argumentos da linha de comando. Podemos adiantar apenas que o qualificador public estabelece que este método pode ser chamado por métodos ou procedimentos externos à classe. O qualificador static significa que o método pertence à classe e não às instâncias da classe, e deste modo pode ser invocado mesmo antes de ser criado algum objeto para a classe. void indica que o método não retornará valor algum. Já argumento String args[] é um array de Strings contendo os parâmetros passados na linha de comando. O corpo do método main() possui duas linhas. A primeira instrução cria um objeto da classe OlaMundo e o atribui à variável obj. A segunda linha invoca o método exibeola() do objeto recém criado. O método exibeola() invoca o método println() do objeto out da classe System, que faz parte do pacote de classes fornecido com a linguagem. Este método exibe no dispositivo de saída padrão a String que é passada como argumento. Se você olhar os programas OlaMundo mostrados em outros livros sobre Java irá notar que o programa apresentado aqui é um pouco mais complicado que o exibido nesses livros. Geralmente este programa inicial é apresentado apenas com um método: o método main(). A razão de termos usado uma abordagem diferente é que desejamos desenvolver um hábito saudável na programação em Java: procure usar o método main() apenas para criar os objetos e deixe que os objetos executem a lógica do problema. Este programa deve ser salvo em um arquivo contendo O MESMO NOME DA CLASSE e com as mesmas letras maiúsculas e minúsculas (é importante frisar, uma vez que esquecer este detalhe é um erro muito comum), e com a extensão.java. Portanto o arquivo contendo o programa acima deverá se chamar OlaMundo.java. Se você instalou o Java 2 development kit (SDK), que pode ser obtido gratuitamente no site então para compilar o programa basta digitar o comando: javac OlaMundo.java O código Java é traduzido (o termo mais comum é compilado) para instruções de uma máquina virtual (bytecodes) para que possa ser executado de forma independente da plataforma (sistema operacional e hardware). O código em bytecodes é armazenado em um arquivo com o mesmo nome do original e com a extensão.class. Assim após a execução do comando acima será 27
29 gerado o arquivo OlaMundo.class. A invocação da máquina virtual para executar o código em bytecodes é efetuada por meio do seguinte comando: java OlaMundo Neste ponto nota-se uma desagradável idiossincrasia do SDK. Para compilar o programa para bytecode foi necessário explicitar a extensão do arquivo, porém, para executar o código é preciso omitir a extensão.class. Apesar de ser um detalhe que aparentemente não prejudica o uso do ambiente, confunde o usuário, principalmente o iniciante. Quando recebe um arquivo de bytecodes para a executar, a máquina virtual procura o método main() em uma classe com o mesmo nome do arquivo. Uma vez encontrado o método a execução é iniciada com a execução do método. Isto significa que em nosso exemplo será procurado o método main() da classe OlaMundo para servir de ponto de entrada. Podem existir outros métodos main() em outras classes codificadas no mesmo arquivo de bytecodes, mas apenas o método main() da classe com o mesmo nome do arquivo servirá de ponto inicial de execução. Portanto, em nosso caso, o programa será executado produzindo a saída: Ola, Mundo! Agora que já cumprimos o ritual de iniciação na linguagem, podemos passar a descrever os detalhes da linguagem. Grande parte das descrições que seguem abaixo foram baseadas na especificação da linguagem registrada no livro The Java Language Specification, segunda edição, por James Gosling e outros. 28 Identificadores Todos os identificadores da linguagem devem iniciar com uma letra, ou o caractere _`, ou o caractere $`. Deve-se evitar o uso do caractere $`, de modo que fique reservado para geração de código automático. Tratamos por letra todo caractere reconhecido pelo método Character.isJavaLetter. Isto inclui uma ampla gama de caracteres do conjunto Unicode 1, de modo que os programadores podem usar identificadores adaptados a uma ampla gama de idiomas. Após o 1 O conjunto de caracteres Unicode foi criado para substituir o conjunto ASCII. Ele usa 16 bits para representar os caracteres, o que resulta em caracteres possíveis, em oposição aos 7 bits do código ASCII (8 para o ASCII estendido), o que resulta em 128 caracteres possíveis (256 para 8 bits).
30 primeiro caractere, também podem ser usados os caracteres que vão de 0` até 9`. A linguagem Java distingue as letras maiúsculas das minúsculas, portanto o identificador Aluno é distinto do identificador aluno. A tabela III-1 mostra alguns identificadores válidos e inválidos. 29 Válido Inválido Aluno10 Aluno#10 Num_Alunos Num Alunos _disciplina$!disciplina Professor_10 10Professor αβγ &uuu Não Não? Tabela III-1. Identificadores válidos e inválidos em Java. Palavras Reservadas As seguintes sequências de caracteres são reservadas para o uso como palavras chave e, portanto, não podem ser usadas como identificadores: abstract continue goto package synchronized assert 2 default if private this boolean do implements protected throw break double import public throws byte else instanceof return transient case extends int short try catch final interface static void char finally long strictfp 3 volatile class float native super while const for new switch Tabela III-2. Palavras reservadas da linguagem Java. As palavras chave const e goto, apesar de serem reservadas, não estão sendo usadas correntemente na linguagem. 2 Introduzida a partir da versão 1.4 do SDK. 3 Introduzida a partir da versão 1.2 do SDK
31 30 Literais Literais são elementos do código que representam um valor de tipo primitivo, tipo String ou null. Os literais podem ser numéricos, booleanos, caracteres ou cadeias de caracteres (Strings). Literais numéricos incluem inteiros, ponto flutuante. Literais Inteiros Um literal inteiro é do tipo primitivo long (longo) se possui o sufixo L` ou l`, caso contrário é do tipo primitivo int (inteiro). Um numeral hexadecimal é prefixado pelos caracteres 0x` ou 0X` seguidos de um ou mais dígitos hexadecimais. Os dígitos hexadecimais com valores entre 10 e 15 são representados pela letras a` até f` ou A` até F`, nessa ordem. Um numeral octal consiste de um dígito 0 seguido de um ou mais dígitos de 0 até 7. O maior literal decimal do tipo int é Os literais decimais de 0 até podem aparecer em qualquer lugar que um literal inteiro pode aparecer, mas o literal pode aparecer somente como operando de uma negação unária. Os maiores literais hexadecimal e octal positivos inteiros são 0x7fffffff e , respectivamente, que correspondem ao valor decimal Os maiores literais hexadecimal e octal negativos inteiros são 0x e , respectivamente, que representam o valor decimal (). Os literais hexadecimal e octal 0xffffffff e , representam o valor decimal -1. Abaixo estão listados alguns exemplos de literais inteiros: xCafe x00FF00FF O maior literal decimal do tipo longo é o L. Os literais decimais de 0L até L podem aparecer em qualquer lugar que um literal inteiro longo pode aparecer, porém o literal L pode aparecer somente como operando de uma negação unária. Os maiores literais hexadecimal e octal positivos inteiros longos são 0x7fffffffffffffffL e L, respectivamente, que correspondem ao valor decimal L.Os maiores literais
32 hexadecimal e octal negativos inteiros longos são 0x L e L, respectivamente, que representam o valor decimal L. Os literais hexadecimal e octal 0xffffffffffffffffL e L, representam o valor decimal - 1. Abaixo estão listados alguns exemplos de literais inteiros longos: 0L 0777L 0xC0B0L 0xCafe x L 31 Literais de Ponto Flutuante Um literal de ponto flutuante é composto por uma parte inteira seguida de um ponto decimal, uma parte fracionária, um expoente e um sufixo determinando o tipo. O expoente, se presente, é indicado pela letra E ou e, seguido por um inteiro com sinal um opcional. Pelo menos um dígito na parte inteira ou fracionária e o ponto decimal ou o expoente ou o sufixo indicando o tipo são exigidos. Todos os outros componentes são opcionais. Um tipo ponto flutuante é do tipo float se possuir o sufixo F ou f, caso contrário é do tipo double. O tipo double possuir opcionalmente o sufixo D ou d. O tipo float e double de Java obedecem a especificação IEEE 754 para binários de ponto flutuante de precisão simples (32-bit) e dupla (64-bit). O maior literal positivo do tipo float é e+38f. O menor literal positivo do tipo float diferente de zero é e-45f. O maior literal positivo do tipo double é e+308. O menor literal positivo do tipo double diferente de zero é e-324. Um programa em Java pode representar quantidades infinitas sem produzir erros de compilação por meio da utilização de expressões constantes tais como 1f/0f e -1d/0d ou pela utilização das constantes predefinidas POSITIVE_INFINITY e NEGATIVE_INFINITY das classes Float e Double. Exemplos de literais do tipo float: 1e1f 2.f.3f 0f 3.14f e+23f Exemplos de literais do tipo double: 1e e-9d 1e137
33 Literais Booleanos Java na Prática 32 O tipo boolean possui dois valores, representados pelos literais true e false. Literais de Caracteres Um literal do tipo char é expresso como um caractere ou uma sequência de escape, envolvida por aspas simples. Os caracteres CR e LF nunca são caracteres de entrada, uma vez que são reconhecidos como terminadores de linha. Exemplos de literais do tipo char: Caractere Descrição 'a' o caractere a. '%' o caractere % '\n' line feed '\t' Tab '\\' o caractere \ '\'' o caractere '. '\u03a9' '\uffff' '\177' Tabela III-3. Exemplo de literais do tipo char. Literais de Cadeia de Caracteres (Strings) Um literal do tipo String consiste de zero ou mais caracteres envolvidos por aspas duplas. Cada caractere pode ser representado por uma sequência de escapes. O tipo String não é um tipo primitivo e sim uma classe denominada String. Portanto, um literal é na verdade uma instância da classe String. Uma String longa pode ser particionada em cadeias menores unidas pelo operador de concatenação +. Exemplos de literais do tipo String: Literal Descrição "" Cadeia vazia.
34 "\"" Uma cadeia com apenas o caractere ". "esta é uma cadeia" Uma cadeia contendo 17 caracteres. "esta é uma " +"cadeia " Uma cadeia formada por dois literais do tipo String. 33 Tabela III-4. Exemplo de literais do tipo String. Sequências de escape Sequências de escape permitem a representação de alguns caracteres não gráficos, assim como as aspas simples e duplas e a barra invertida. Exemplos de sequências de escape: Sequência Descrição \b \u0008: backspace BS \t \u0009: tab horizontal HT \n \u000a: linefeed LF \f \u000c: form feed FF \r \u000d: carriage return CR \" \u0022: aspas duplas " \' \u0027: aspas simples ' \\ \u005c: barra invertida \ Tabela III-5. Exemplo sequências de escape. O Literal null O tipo null possui apenas um valor, representado pelo literal null. Separadores Java possui nove separadores (caracteres de pontuação), listados abaixo: ( ) [ ] ;,.
35 Tipos de dados Java na Prática 34 Os tipos de dados definem como devem ser interpretados os dados armazenados. São essenciais a toda linguagem de programação e devem ser cuidadosamente selecionados para compor a linguagem, de modo a não limitar a sua área de atuação. A maioria da linguagens modernas definem um conjunto de tipos simples, um conjunto de tipos compostos, formados por tipos mais primitivos e alguma forma de definição de novos tipos, de modo que possa modelar mais apropriadamente a realidade. A linguagem Java obedece esta regra, fornecendo um conjunto de tipos simples e um conjunto de tipos compostos. O mecanismo para definição de novos tipos utilizado por Java é simplesmente a definição de classes. Java é uma linguagem fortemente tipada. Isto significa que toda variável e expressão possui um tipo determinado em tempo de compilação. Os tipos primitivos disponíveis em Java não são classes. Esta é uma das razões porque Java não é uma linguagem 100% orientada a objetos. No entanto, para cada tipo primitivo existe uma classe correspondente onde são declarados um conjunto de métodos para a manipulação dos valores primitivos, focalizando principalmente na conversão de tipos. Estas classes estão agrupadas no pacote java.lang. Por exemplo, o tipo primitivo int é usado para expressar valores inteiros. A classe correspondente ao tipo int é a Integer. Nela estão declaradas variáveis públicas contendo o valor máximo e mínimo que uma variável do tipo inteiro pode armazenar e métodos para conversão para outros tipos. Tipos de dados simples Tipos de dados simples são aqueles que não podem ser divididos em tipos mais primitivos. Os tipos de dados simples de Java podem ser divididos em inteiros, ponto flutuante, booleano e caractere. Para se definir uma variável de um determinado tipo basta preceder o nome da variável com o nome do tipo desejado, como na forma abaixo: <nome do tipo> <nome da variável>; Assim, para se declarar uma variável var1 do tipo inteiro basta a linha de código abaixo: int var1;
36 Podemos também declarar mais de uma variável em com o mesmo tipo, usando o caractere, como separador. Portanto, a declaração é equivalente às declarações int var1, var2; int var1; int var2; 35 Java também permite que as variáveis sejam inicializadas durante a declaração. Assim, para inicializarmos a variável var1 com o valor 1 na declaração basta a linha de código abaixo: int var1=1; Segue abaixo as categorias de tipos de dados simples. Inteiros Nome byte short int long Tamanho 8 bits 16 bits 32 bits 64 bits Tabela III-6. Tipos inteiros. Ponto Flutuante Nome float double Tamanho 32 bits 64 bits Tabela III-7. Tipos de ponto flutuante. booleanos boolean true,false
37 36 Caractere char 16 bits Tipos de dados compostos Os tipos de dados compostos são constituídos por tipos mais simples. Todos os tipos compostos em Java são Classes. Nesta seção trataremos duas classes de objetos muito usadas em Java para criação de tipos compostos: os arrays (arranjos) e strings. Posteriormente abordaremos a declaração de objetos no caso geral e no Capítulo VI será apresentado o pacote de classes java.util que contém várias classes para agrupamento de objetos. Arrays Um array é um objeto, e como tal herda todos os métodos da classe Object. Um array contém um certo número de variáveis chamadas de componentes. Em Java todo objeto é acessado indiretamente, via uma referência. Ou seja, não se cria uma variável do tipo de um objeto e sim uma variável que pode referenciar um objeto. Estamos falando de ponteiros, o que pode parecer contraditório, uma vez que tínhamos mencionado que Java não possui ponteiros. Na verdade Java acessa as instâncias de objeto por meio de ponteiros mas eles não estão disponíveis como tipo da linguagem e nem é possível que o programador os manipule diretamente. Pode parecer pouco importante para o programador saber que a variável não armazena o objeto diretamente e sim uma referência a um objeto, uma vez que ele não pode manipular ponteiros. No entanto, acreditamos que esta informação é importante para que o leitor possa entender as etapas para a criação de objetos. A primeira etapa é a declaração da variável para referenciar o objeto e a segunda etapa é a criação do objeto propriamente dito. Para se declarar uma variável para referenciar objetos do tipo array é usada a seguinte sintaxe: tipo identificador[]; tipo[] identificador; Exemplos int numeros[]; char[] letras;
38 37 long grade[][]; Note que não é definido o número de elementos do array. Isto faz parte do objeto. Note também que existem duas formas de declarações de referências a arrays. O número de [] indica o número de dimensões do array. A criação do objeto é realizada por meio do operador new, seguido pelo tipo do array e pelo número de componentes de cada dimensão, como nos exemplos abaixo: numeros = new int[10]; char alfabeto[] = new char[26]; grade = new long[10][10]; Alternativamente podemos realizar as duas etapas acima e ainda definir os elementos do array em uma única declaração como no exemplo abaixo, onde é criado um array de três inteiros, referenciados pela variável primos e onde o primeiro elemento é 7, o segundo é 11 e o terceiro é 13. int primos = 7, 11, 13; De agora em diante não faremos distinção entre referência a objeto do tipo array e o objeto array, a não ser que seja necessário explicitar esta distinção. O acesso aos elementos do array é realizado por meio do nome da variável seguida por um expressão inteira não negativa envolvida pelos caracteres [ e ]. A expressão inteira é chamada de índice e os valores admissíveis para a expressão vai de 0 a n-1, onde n é número de elementos do array. O índice do primeiro elemento é 0. Abaixo seguem alguns exemplos: alfabeto[0] = a ; grade[0][5] = 10L; for(int i=0; i<10; i++) numeros[i] = i*2; É possível descobrir o tamanho de um array em tempo de execução acessando a variável pública length do objeto, onde está armazenada a capacidade do array. Por exemplo: for(int i=0; i< numeros.length; i++) numeros[i] = i*2; Strings
39 O manipulação de cadeias de caracteres (strings) em Java é realizada por uma classe do pacote java.lang denominada String. Ou seja, não existe um tipo primitivo para tratar cadeias de caracteres. Para se declarar uma variável que faz referencia a um objeto String usa-se a seguinte linha de comando: String nome; Para que a variável faça referência a um objeto basta criar um objeto por meio do operador new ou atribuir a referência de um objeto preexistente. nome = new String( Pedro ); String x, y; x= nome; y = Pedro ; No primeiro caso a variável nome faz referencia a um objeto recém criado contendo o valor Pedro. Já a variável x faz referencia ao mesmo objeto referenciado pela variável nome. Ou seja, nenhum novo objeto String é criado, ocorrendo um compartilhamento de objetos. Já a variável y faz referência a um objeto String contendo o valor Pedro, distinto do objeto referenciado por x e nome. Podemos também inicializar a variável durante a declaração. String nome = Pedro ; Os objetos do tipo String possuem um conjunto extenso de métodos e construtores para a manipulação e criação de Strings. A tabela abaixo mostra alguns dos mais utilizados. 38 Construtor Descrição String(byte[] bytes, int offset, Constrói uma nova String convertendo o subarray de int num) bytes especificado String(StringBuffer buffer) Constrói uma nova String usando a sequência de caracteres contida no StringBuffer. String(byte[] bytes) Constrói uma nova String convertendo o array de bytes especificado String(String valor) Constrói uma nova String com o mesmo conteúdo da String passada como argumento. String() Constrói uma nova String contendo zero caracteres. String(char[] valor) Constrói uma nova String convertendo o array de String(char[] valor, int offset, int num) caracteres especificado. Constrói uma nova String convertendo o subarray de caracteres especificado.
40 39 Tabela III-8. Principais construtores da classe String. Método charat(int indice) compareto(string outrastring) equals(object anobject) getchars(int srcbegin, int srcend, char[] dst, int dstbegin) indexof(string str) indexof(string str, int fromindex) indexof(int ch, int fromindex) indexof(int ch) length() replace(char oldchar, char newchar) substring(int beginindex) substring(int beginindex, int endindex) tolowercase() touppercase() trim() valueof(object obj) valueof(char c) valueof(boolean b) valueof(long l) valueof(int i) valueof(char[] data) valueof(float f) valueof(double d) Descrição Retorna o caractere localizado no índice especificado. Compara duas Strings lexicograficamente. Verifica se dois objetos são iguais. Copia os caracteres da String em um array de caracteres. Retorna o índice da primeira ocorrência do substring no string. Retorna o índice da primeira ocorrência do substring no string a partir do índice especificado. Retorna o índice da primeira ocorrência do caractere no string a partir do índice especificado. Retorna o índice da primeira ocorrência do caractere no string. Retorna o comprimento do string. Retorna uma nova String onde todos os caracteres oldchar foram substituídos pelos caracteres newchar. Retorna uma nova string que é substring da atual. Retorna uma nova string que é substring da atual. Converte para minúsculas. Converte para Maiúsculas. Remove os espaços em branco do inicio e do fim do String. Retorna a representação em String do argumento Object. Retorna a representação em String do argumento char. Retorna a representação em String do argumento booleano. Retorna a representação em String do argumento long. Retorna a representação em String do argumento int.. Retorna a representação em String do argumento array de caracteres Retorna a representação em String do argumento float. Retorna a representação em String do argumento double.
41 40 Tabela III-9. Principais métodos da classe String. Os literais de Strings são tratados como instâncias da classe String e como tal possuem os métodos de um objeto da classe String. Por exemplo, após a execução da expressão abaixo a variável x conterá o valor 5. int x = Pedro.length(); Conversão de Tipos De forma geral as conversões entre tipos em Java devem ser especificadas explicitamente. A forma mais comum de se especificar uma conversão é por meio da notação abaixo: Por exemplo: (<tipo destino>) <expressão> int i = 10; char c = (char) i; Este tipo de conversão é chamada de casting. O programador deve ficar bastante atento no que diz respeito a conversões, uma vez que pode acontecer perda de informação quando convertemos um tipo para outro que ocupa um espaço menor na memória. A tabela abaixo mostra as conversões entre tipos primitivos que podem causar perda de informação: do tipo byte short char int long float double para o tipo char byte, char byte, short byte, short, char byte, short, char, int byte, short, char, int, long byte, short, char, int, long, float Tabela III-10. Conversão de tipos que podem causar perda de informação.
42 Outra forma de converter tipos é usando os métodos fornecidos pelas classes associadas aos tipos. A tabela abaixo mostra a classe associada a cada tipo primitivo. 41 Tipo int float double boolean byte short long Classe Integer Float Double Boolean Byte Short Long Tabela III-11. Classes associadas a cada tipo primitivo. As classes fornecem métodos para conversão mais sofisticados como do tipo primitivo para String e vice-versa. Por exemplo, a classe Integer fornece um método para converter String para int: int i = Integer.parseInt( 12 ); Para se converter um inteiro para String podemos utilizar o método tostring(): String s = Integer.toString(12); Existem métodos semelhantes nas outras classes. Existe também um tipo de conversão que somente se aplica aos operandos do operador binário + quando um dos operandos é um objeto da classe String. Caso o outro operando não seja um objeto da classe String, então ele será convertido para String e o resultado da operação será a concatenação das duas cadeias. As Conversões entre objetos de classes distintas serão tratadas no próximo Capítulo. Operadores Os operadores atuam sobre valores e variáveis de modo a gerar novos valores ou modificar os valores das variáveis. Os símbolos abaixo representam os 37 operadores da linguagem Java:
43 42 = > <! ~? : == <= >=!= && * / & ^ % << >> >>> += -= *= /= &= = ^= %= <<= >>= >>>= Os operadores podem ser divididos nas seguintes classes. Unários Descrição Símbolo Incremento ++ Decremento -- Negativo - Complemento de bit ~ Tabela III-12. Operadores unários. Os operadores de incremento e decremento (++ e --) aumentam e decrementam variáveis de tipo inteiro de uma unidade. Estes operadores podem ser usados na forma prefixa ou pósfixa. Na forma prefixa o operador modifica o valor da variável antes que o valor seja usado na expressão onde está a variável. Na forma pósfixa o operador modifica o valor da variável depois que o valor é usado na expressão onde está a variável. Por exemplo, o valor das variáveis x, y e w após a execução do trecho de código abaixo int x = 1, y, w; y = x++; w = --x; é 1. Isto ocorre porque y recebe o valor de x antes de ser incrementado e w recebe o valor de x depois de ser decrementado. O operador de negação unário (-) é usado para mudar o sinal de um valor inteiro. O operador de Complemento de bit (~) inverte cada bit da representação binária de um inteiro. O exemplo abaixo mostra efeito da aplicação deste operador. byte x = 10; // valor em binário
44 x = ~x; // valor em binário ; valor em decimal O programa abaixo mostra o uso de cada operador unário. public class Unarios public static void main (String args[]) int x = 10, y = 0; System.out.println("x = " + x); System.out.println("y = " + y); System.out.println("++x = " + ++x); System.out.println("y++ = " + y++); System.out.println("x = " + x); System.out.println("y = " + y); System.out.println("-x = " + -x); System.out.println("~y = " + ~y); Saída: x = 10 y = 0 ++x = 11 y++ = 0 x = 11 y = 1 -x = -11 ~y = -2 Exemplo III-2. Uso dos operadores unários. Binários Descrição Símbolo Adição e concatenação de strings + Subtração - Multiplicação * Divisão / Módulo % E de bit & OU de bit OU exclusivo de bit ^ deslocamento de bits a esquerda <<
45 deslocamento de bits a direita >> des. a direita com preenchimento zero >>> 44 Tabela III-13. Operadores binários. Acreditamos que os operadores binários para adição, subtração, multiplicação, divisão e concatenação de Strings não necessitam maiores explicações. O operador de módulo (%) retorna o resto da divisão entre dois operandos inteiros. Por exemplo, o valor da variável x após a execução do trecho de código int x = 7 % 4; é 3. Os operadores &, e ^ implementam operações orientada para bits, ou seja, operações que atuam sobre os bits individuais dos operandos. São úteis quando se usa valores como campos de bits. O operador & realiza a operação lógica E entre cada bit individual dos operandos. Por exemplo, o valor da variável x após a execução do trecho de código byte y = 3; byte x = Y & 5; é 1. O operador realiza a operação lógica OU entre cada bit individual dos operandos. Por exemplo, o valor da variável x após a execução do trecho de código byte y = 3; byte x = Y 5; y & y é 7. O operador ^ realiza a operação lógica XOU (ou exclusivo) entre cada bit individual dos operandos. Por exemplo, o valor da variável x após a execução do trecho de código é 6. byte y = 3; byte x = Y & 5; y ^
46 Os operadores <<, >> e >>> implementam operações de deslocamento de bits. Os bits do operando à esquerda serão deslocados o número de posições especificadas pelo operando à direita. Os bits que saem dos limites do campo são perdidos e a variável é preenchida com zeros no lado oposto ao deslocamento. O operador << realiza a operação de deslocamento de bits a esquerda. Por exemplo, o valor da variável x após a execução do trecho de código byte x = 7 << 1; é 14. Note que o deslocamento de uma unidade para a esquerda tem o mesmo efeito da multiplicação por 2, desde que nenhum bit seja. Os operadores >> e >>> realizam a operação de deslocamento de bits a direita. A diferença entre o operador >> e o >>> é que o primeiro não desloca o bit de mais alta ordem usado para indicar números negativos. O programa abaixo ilustra o uso desses operadores: 45 public class Deslocamento public static void main (String args[]) int x = 7, y = 7; System.out.println("x = " + x); System.out.println("y = " + y); System.out.println("x << 1= " + (x<<1)); System.out.println("x >> 2= " + (x>>2)); System.out.println("y >> 2= " + (y>>2)); System.out.println("y >>> 31= " + (y>>>31)); Saída: x = 7 y = -7 x <<= 14 x >> 2= 1 y >> 2= -2 y >>> 30= 3 Exemplo III-3. Uso dos operadores de deslocamento de bits. Note que no caso do número positivo o deslocamento à direita teve o efeito de uma divisão inteira por dois. Já quando o número é negativo este efeito
47 não ocorre, devido ao fato da representação de números inteiros ser feita em complemento a dois. Relacionais Descrição Símbolo Menor que < Maior que > Menor igual <= Maior igual >= Igual == Diferente!= Tabela III-14. Operadores relacionais. 46 Os operadores relacionais são usados para comparar valores. O resultado da aplicação desses operadores é um valore boolano, ou seja :true ou false. O programa abaixo ilustra o uso desses operadores: public class Relacional public static void main (String args[]) int x = 7, y = 8; System.out.println("x = " + x); System.out.println("y = " + y); System.out.println("x < y = " + (x < y)); System.out.println("x > 10 = " + (x > 10)); System.out.println("y <= 8 = " + (y <= 8)); System.out.println("y == 5 = " + (y == 5)); System.out.println("x!= y = " + (x!= y)); Saída: x = 7 y = 8 x < y = true x > 10 = false y <= 8 = true y == 5 = false x!= y = true Exemplo III-4. Uso dos operadores relacionais.
48 47 Booleanos Descrição Símbolo E & OU OU Exclusivo ^ E Curto circuito && OU Curto circuito Negação! Igualdade == Condicional?: Tabela III-15. Operadores booleanos. Os operadores booleanos atuam sobre valores booleanos e retornam um valor booleano. A tabela abaixo resume o resultado da aplicação dos operadores booleanos, exceto o operador condicional (?:) e o de negação (!): Operando 1 Operando 2 & ^ && == true true true true false true true true true false false true true false true false false true false true true false true false false false false false false false false true Tabela III-16. Resumo dos operadores booleanos. O operador de negação muda o valor booleano como mostrado na tabela abaixo: Operando! true false false true Tabela III-17. Operador de negação.
49 O operador condicional atua sobre três operandos: uma expressão condicional e duas outras expressões quaisquer. A forma geral do operador é <condição>? <expressão 1> : <expressão 2>. Se <condição> for avaliada como true, então o resultado da aplicação do operador condicional será o retorno da avaliação da <expressão 1>. Caso contrário o resultado da avaliação da <expressão 2> será retornado. O programa abaixo ilustra o uso deste operador: public class Condicional public static void main (String args[]) int x = 5; 48 boolean par = (x % 2 == 0)? true : false; System.out.println("x = " + x); System.out.println("É par = " + par); Saída: x = 5 É par = false Exemplo III-5. Uso do operador condicional. Atribuição Descrição Símbolo Simples = Adição += Subtração -= Multiplicação *= Divisão /= Modulo %= AND &= OR = XOR ^=
50 Tabela III-18. Operadores de atribuição. 49 Os operadores de atribuição simplesmente atribuem um valor a uma variável. Exceto o operador de atribuição simples (=), todos os outros operadores na forma <variável> <op>= <expressão> funcionam como uma maneira abreviada de se escrever <variável> = <variável> <op>(<expressão>) Por exemplo, a atribuição tem o mesmo significado da atribuição x += 2; x = x +2; Esta forma de escrever facilita a trabalho de digitação, porém torna o código fonte menos legível. Expressões e Precedência entre Operadores Expressões são trechos de códigos que geram valores. Portanto, o trecho de código 1+2 é uma expressão pois gera o valor 3. Literais também são expressões pois geram o próprio valor que representam. Portanto, o literal 3.14 é também uma expressão. As variáveis que ocorrem em uma expressão são também expressões, pois representam o valor que armazenam. As expressões são combinadas por meio dos operadores para formar expressões maiores. Assim, podemos combinar as expressões 3 e 5*6 por meio de um operador tipo + para formar a expressão 3+5*6 Uma pergunta que pode surgir é: como a expressão é avaliada? Ou seja, que operador é aplicado primeiro: o + ou o *? Dependendo da ordem de aplicação dos operadores o resultado da avaliação da expressão pode ser diferente, como no caso acima. A ordem de aplicação obedece uma prioridade
51 definida pela precedência relativa entre os operadores. A tabela abaixo define a precedência entre os operadores. A precedência decresce de cima para baixo. Em se tratando de operadores com a mesma precedência a avaliação em uma expressão é feita da esquerda para a direita. 50 Comentários. [] () ++ --! ~ * / % + - << >> >>> < > <= >= ==!= & ^ &&?: = Tabela III-19. Precedência de operadores. Comentar o código fonte faz parte das regras que definem um bom estilo de programação. Java possui três diferentes formas de se comentar o código, ilustradas abaixo: // comentário de linha /* comentário de bloco */ /** comentário de bloco c/ propósito de documentação */ As duas primeiras formas de comentário são familiares para os programadores de C++. Na primeira forma, todos os caracteres posicionados após // e antes do final da linha são ignorados pelo compilador. Na segunda forma todos os caracteres que ocorrem após /* são ignorados pelo compilador, até que seja encontrado a sequência */. A terceira forma de comentário é semelhante a segunda, com a diferença de que é usada pela ferramenta de documentação javadoc para gerar uma documentação no formato HTML (HyperText Markup Language). O uso dessa ferramenta será tratado no
52 final do próximo capítulo. O exemplo III-6 repete o código do exemplo III-5 com a adição de alguns comentários: 51 public class Condicional // classe para teste do operador?: public static void main (String args[]) int x = 5; // variável usada para o teste /* Será impresso true se x for par */ boolean par = (x % 2 == 0)? true : false; System.out.println("x = " + x); System.out.println("É par = " + par); Exemplo III-6. Uso de comentários. Blocos e Escopo Na linguagem Java, assim como em C/C++ um bloco de comandos é delimitado pelos caracteres e. Os blocos são utilizados para agrupar comandos que são relacionados. Um bloco também pode conter blocos, chamados de blocos internos. Todas as variáveis declaradas em um bloco podem ser referenciadas apenas dentro do bloco e nos blocos internos ao bloco onde foi definida, desde que não exista nenhuma variável no bloco mais interno com o mesmo nome. As seções onde uma determinada variável pode ser acessada definem a escopo da variável. A figura III-1 ilustra a relação entre blocos e escopo. escopo de y... int x =2; public class Visivel public static void main (String args[]) int x = 5; int y = 2; escopo do x externo escopo do x interno
53 52 Figura III-1. Visibilidade de variáveis. Estruturas de Controle As estruturas de controle definem a sequência de execução das instruções. Não é possível implementar um algoritmo que não seja trivial em uma linguagem de programação que não tenha um conjunto mínimo de estruturas de controle. As estruturas de controle podem ser divididas em seleção, repetição e sequência. A sequência é simplesmente definida pela execução sequencial dos comandos, de cima para baixo. Falta abordarmos as estruturas pertencentes às outras duas classes. Seleção Na seleção o fluxo sequencial de execução é desviado segundo uma condição ou valor. Java apresenta duas formas seleção: o if e o switch. if O comando de seleção de if possui duas formas básicas: if (condição) comando1 (a) if (condição) comando1 else comando2 (b) Na forma (a) o comando1 é executado se condição for avaliada como true. Na forma (b) o comando1 é executado se condição for avaliada como true senão o comando2 é executado. if (x==0) y = 5; else y+=6; Exemplo III-7. Comando if.
54 53 Se o comando a ser executado for na verdade um conjunto de comandos devemos então usar os delimitadores de bloco e para agrupar os comandos em um único bloco. if (x==0) y = 5; else y+=6; x++; Exemplo III-8. Comando if executando um bloco. Os comandos if podem combinar em forma aninhada como mostrado no exemplo III-9. if (x==0) if (y == 1) x= y; else y+=6; Exemplo III-9. ifs aninhados. O aninhamento como no exemplo III-9 deixa uma dúvida com que if o else está relacionado? Com o mais interno ou o mais externo? A regra para esses casos é: o else se relaciona com o if mais interno. Essa regra pode ser alterada, utilizando os delimitadores e para definir os blocos de comandos, como mostrado no exemplo III-10. if (x==0) if (y == 1) x= y; else y+=6;
55 Exemplo III-10. Uso de blocos para definir o par if-else. 54 switch O comando switch é útil quando existem várias ações distintas que precisam ser realizadas em função do resultado da avaliação de uma expressão. Não é nada que não poderia ser feito com um conjunto de if-else, no entanto o uso do switch facilita codificação e a legibilidade do programa. A forma geral do comando está definida abaixo: switch(expr) case const1: com1;... case constn: comn; default: comdef O comando switch avalia a expressão Expr e compara o valor resultante com todas as constantes colocadas após a palavra a chave case. A comparação é feita na ordem de cima para baixo e a primeira constante que igualar com o valor resultante da expressão faz com que os comandos após o : sejam executados até o fim ou até que seja encontrado o comando break. A palavra chave default é opcional e ela serve como ponto de entrada para os comandos que serão executados caso o valor da expressão não seja igual a nenhuma das constantes. O exemplo III-11 ilustra o uso do comando switch. switch(letra) case i : case a : case e : System.out.println( inserir ); break; System.out.println( alterar ); break; System.out.println( excluir ); break; default: System.out.println( Ação ignorada: +letra);
56 Exemplo III-11. Uso do comando switch. 55 O comando break é muito importante na composição do switch. Ele faz com que a execução sequencial dos comandos seja interrompida para ser retomada no primeiro comando após o comando switch. O exemplo III-12 mostra as saídas de um programa com um comando switch que não faz uso do comando break. Note que todos os comandos são executados, uma vez que não existe um comando break para interromper a execução. public class Switch public static void main (String args[]) char letra = i ; switch(letra) case i : System.out.println( inserir ); case a : System.out.println( alterar ); case e : System.out.println( excluir ); default: System.out.println( Ação ignorada: +letra); Saída: inserir alterar excluir Ação ignorada:i Exemplo III-12. Uso do comando switch sem break. O leitor pode se estar perguntando porque a sequência de comandos não é interrompida automaticamente em vez de deixar para o programador a tarefa de sinalizar isso por meio do comando break. A verdade é que a omissão do comando break pode ser útil para poupar digitação naqueles casos onde mais de uma opção precisa executar a mesma sequência de comandos. O exemplo III-13 mostra um desses casos. switch (mês) case 1:
57 Java na Prática case 3: case 5: case 7: case 8: case 10: case 12: dias = 31; break; case 4: case 6: case 9: case 11: dias = 30; break; case 2: if (((ano % 4==0) &&!(ano % 100 == 0)) (ano % 400 == 0) ) dias = 29; else dias = 28; break; Exemplo III-13. Uso útil da omissão do comando break. 56 Repetição Os comandos de repetição, ou de iteração, executam um comando ou um bloco de comandos várias vezes. É uma das formas para executar repetidamente um comando. A outra é a recursão que é a chamada direta ou indireta de um método durante a execução do próprio método. Em Java existem três formas de comandos de repetição: o while; o do-while; e o for. while A forma geral do comando while é: while(condição) Comando; O comando while executa Comando enquanto condição for avaliada como true. Portanto, para encerrar a iteração é necessário que exista a possibilidade da execução de Comando alterar a avaliação de condição. A condição é testada antes da execução do comando. O exemplo III-14 mostra a soma dos 100 primeiros números inteiros. int i = 1;
58 int x = 0; while(i<=100) x +=i++; 57 Exemplo III-14. Soma dos 100 primeiros números inteiros. Se for necessário executar mais de um comando deve-se agrupá-los por meio de delimitadores de bloco, como mostrado no exemplo III-15. while(i<=100) x *=y; i++; Exemplo III Uso de while com bloco. do-while A forma geral do comando do-while é: do Comando; while(condição) O comando do-while executa Comando enquanto condição for avaliada como true. A diferença em relação ao comando while é que a condição é testada após a execução do comando. O exemplo III-16 mostra a soma dos 100 primeiros números inteiros. Os comandos também podem ser agrupados em um bloco. int i = 1; int x = 0; do x +=i++; while(i<100); Exemplo III-16. Soma dos 100 primeiros números inteiros com do-while.
59 58 for A forma geral do comando for é: for(cod.inicialização;condição; Cod.passo) Comando; No comando for o Cod.Inicialização (código de inicialização) é executado uma vez, a condição é avaliada antes de cada iteração e o Comando e o Cod.passo é executado a cada iteração. O Cod.passo é executado após o Comando. Todas as seções do comando for podem ser omitidas. Enquanto condição for avaliada como true é executada mais uma iteração. Em geral, o Cod.Inicialização é utilizado para inicializar a variável que será testada na condição e o Cod.passo é utilizado para alterar o valor da mesma. O exemplo III-17 mostra a soma dos 100 primeiros números inteiros. x = 0; for(int i=1; i<=100; i++) x +=i; Exemplo III-17. Soma dos 100 primeiros números inteiros com for. Tanto o Cod.Inicialização como o Cod.passo podem ser compostos de vários comandos. Cada comando é separado por,. O exemplo III-18 ilustra esta forma de uso do comando for. for(int i=1, x = 0; i<=100; i++) x +=i; Exemplo III-18. Soma dos 100 primeiros números inteiros com for. Como já foi dito, todas as seções do comando for podem ser omitidas. O exemplo III-19 mostra uma iteração infinita, definida pela omissão das seções do comando for.
60 59 for(;;) System.out.println( ola mundo ); Exemplo III-19. Iteração infinita. break e continue Já vimos o uso do comando break quando discutimos o comando switch. Ele servia para interromper a sequência de execução dos comando e sair do comando switch. Isto não acontece apenas no comando switch. Toda vez que um break é encontrado, o fluxo de execução salta para fora do bloco onde está contido o break, sendo retomado no comando após o bloco. A figura III-2 ilustra este comportamento. for (;;)... break;... fluxo retomado aqui... Figura III-2. Uso do comando break. O uso mais comum do comando break ocorre em uma condição de teste dentro de uma iteração. O exemplo III-20 é equivalente ao exemplo III-18. for(int i=1, x = 0;; i++) if (i==101) break; else x +=i; Exemplo III-20. Soma dos 100 primeiros números inteiros com for e break. Já o comando continue é usado dentro de uma iteração e faz com que ocorra um desvio para a condição de teste da iteração. Ou seja, o comando continue força um salto para próxima iteração. A figura III-3 ilustra este comportamento.
61 60 while (Condição)... fluxo retomado aqui continue; Figura III-3. Uso do comando continue. Como no comando break, o uso mais comum do comando continue é em uma condição de teste dentro de uma iteração. break e continue com rótulos Diferentemente C/C++ Java permite o uso de rótulos após os comandos break e continue para permitir que a saída para mais de um nível de iteração quando em laços aninhados. Rótulos correspondentes aos usados nos comandos break/continue devem ser posicionados imediatamente antes dos comandos de laços, seguidos do caractere :. O exemplo III-21 mostra o uso de rótulos para desviar para um laço mais externo ao corrente. No caso do exemplo uma nova iteração do laço mais externo é iniciada quando o continue é executado. public static boolean contemstr(string sub, String str) prox: for (int i=0; i<=str.length()-sub.length(); i++) for (int j=0; j<sub.length(); j++) if (str.charat(i+j)!= sub.charat(j)) continue prox; return true; return false; Exemplo III-21. Uso de comandos com rótulos.
62 Argumentos da linha de comando 61 Os argumentos digitados na linha de comando são passados para o método main() da classe invocada por meio de um vetor de Strings. Por exemplo, se executarmos a linha de comando abaixo java teste um dois três o método main() da classe teste receberá o seguinte vetor de Strings: [0] um [1] dois [2] três Figura III-4. Vetor com os argumentos da linha de comando. Para quem está acostumado a programar na linguagem C note o primeiro elemento do vetor não é o nome do programa e sim o primeiro argumento da linha de comando. O espaço serve como separador de argumentos. Se desejarmos tratar uma cadeia de caracteres com espaço como um único argumento é necessário delimitá-la com aspas duplas. Por exemplo, o comando abaixo resulta no seguinte vetor: java teste um dois três [0] um [1] dois três Figura III-5. Vetor com os argumentos da linha de comando com aspas. O classe do exemplo III-22 mostra um programa que imprime os argumentos recebidos, um em cada linha do dispositivo de saída. public class ImpLinha
63 Java na Prática public static void main(string args[]) int i; for (i=0;i<args.length;i++) System.out.println(args[i]); Exemplo III-22. Imprimindo a linha de comando. 62 Todos os argumentos são tratados como Strings. Se houver necessidade de tratar os argumentos como pertencendo a um tipo diferente será preciso realizar as conversões necessárias. No exemplo III-23 os argumentos são convertidos para números inteiros para serem somados. public class Soma public static void main(string a[]) int i,soma=0; for (i=0;i<a.length;i++) soma += Integer.parseInt(a[i]); System.out.println( A soma é: +soma); Assert (Assertivas) Exemplo III-23. Imprimindo a soma dos números. Com o surgimento da versão 1.4 do SDK uma nova alteração na linguagem Java foi introduzida, envolvendo a inclusão de uma nova palavra reservada: a palavra assert. O objetivo desta alteração é incluir na linguagem recursos que permitam que o programador verifique se uma dada condição, chamada de assertiva, é verdadeira em um local específico do código. Isto possibilita o desenvolvimento de códigos mais confiáveis, sendo um recurso utilizado durante a etapa de desenvolvimento e desabilitado em tempo de execução. Devido a inclusão de uma nova palavra chave, códigos fontes preexistentes que incluam a palavra assert como identificador gerarão erro de compilação ao serem compilados com a versão 1.4 do SDK. Este problema de compatibilidade é solucionado por meio de flags de compilação. A linguagem C++ possui recurso semelhante, com a diferença de ser implementado por meio de biblioteca e não a nível de linguagem. No caso de
64 Java, a decisão de não utilizar uma biblioteca para implementação deste recurso deveu-se ao objetivo de se buscar uma implementação mais transparente, apesar do inconveniente da perda de compatibilidade. A inclusão de recursos de para verificação de condições estava presente na especificação original da linguagem, quando ainda era denominada oak, mais foi retirada da primeira versão por se acreditar que não haveria tempo para se desenvolver uma implementação adequada. Sintaxe e semântica A formato geral de uma assertiva é: assert<expressão booleana1>; ou assert<expressão booleana1>:<expressão booleana2>; A avaliação de uma assertiva funciona da seguinte forma: a primeira expressão é avaliada. Caso o resultado seja false e não exista uma segunda expressão então a exceção AssertionError é lançada. Caso o resultado seja false e exista uma segunda expressão então ela é avaliada e o resultado é passado para o construtor da exceção AssertionError antes de ser lançada. Caso o resultado seja da primeira expressão seja true então a segunda expressão, caso exista, não será avaliada. O exemplo III-24 mostra um exemplo do uso de assertivas para garantir que uma determinada condição seja verdadeira assert b > 0; int x = a/b;... Exemplo III-24. Uso de assertivas. Habilitando e Desabilitando Assertivas
65 As assertivas podem ser habilitadas/desabilitadas por flags na linha de comando e chamadas a métodos durante a execução. A habilitação/desabilitação pode ter uma atuação que varia desde o envolvimento de uma única classe até um pacote ou todo o programa. Por default as assertivas estão desabilitadas. Habilitação por flags A forma geral para a habilitação de assertivas é: java [-enableassertions -ea] [:<nome package>... :<nome classe>] O flag sem argumento habilita as assertivas para todas as classes. Se for seguindo por o nome de um pacote seguido por... as assertivas serão habilitadas para o pacote especificado e todos os subpacotes. Se for seguido apenas de... as assertivas serão habilitadas para o pacote do diretório corrente e todos os subpacotes. Se o argumento não terminar com..., então as assertivas serão habilitadas apenas para a classe especificada. Abaixo segue um exemplo de habilitação de assertivas: java ea:br.ufv.dpi.apo... Teste A desabilitação de assertivas segue a mesma lógica, mudando apenas o flag que passa ser disableassertions ou da. 64 Habilitação por métodos Os seguintes métodos da classe ClassLoader podem ser invocados para habilitar/desabilitar as assertivas em tempo de execução: void setdefaultassertionstatus(boolean status) habilita/desabilita por default as assertivas. void setpackageassertionstatus(string nomepack, boolean status) habilita/desabilita as assertivas no pacote e subpacotes. void setclassassertionstatus(string nomeclasse, boolean status) habilita/desabilita as assertivas na classe. void clearassertionstatus() retorna ao default.
66 65
67 66 Capítulo IV Classes, Pac kages e Interfaces A unidade fundamental de programação em Java é a Classe. Não é possível fazer um programa em Java que não tenha pelo menos uma classe. Isso não é verdade para todas as linguagens Orientadas a Objeto. Em C++, por exemplo, é possível fazer um programa que não use classe. Em Java todas as variáveis e métodos de um programa Java devem ser definidos dentro de uma classe. Um programa que não contenha classes não é um programa orientado a objetos. No entanto, o fato de um programa conter classes também não o torna merecedor do título de orientado a objetos. A programação orientada a objetos é mais um estilo de programação do que um conjunto de palavras-chave colocadas em um programa. Porém, é obviamente melhor implementar este estilo usando uma linguagem que oferece suporte a ele. Neste capítulo estudaremos o suporte oferecido por Java a este estilo de programação. Não só a implementação de classes será vista, como também conceitos relacionados, como pacotes e interfaces. Classes No Capítulo II foram discutidas Classes e Objetos do ponto de vista de modelagem e de implementação. Nesta seção será detalhada a implementação de classes em Java. A definição de uma classe em Java obedece a seguinte forma geral: [public] class <Identificador> <corpo da classe> A palavra reservada public é opcional e indica que a classe pode ser referenciada por qualquer outra classe além do próprio pacote (conceito que será visto mais adiante). Apenas uma classe por arquivo pode ser precedida pela palavra reservada public e o nome do arquivo tem que ser idêntico ao nome desta classe. Se a declaração da classe não for precedida de public, então ela só poderá ser referenciada por outras classes do mesmo pacote. Os modificadores
68 de acesso private e protected não podem ser aplicados às classes. O <Identificador> é usado para referenciar a classe. No <corpo da classe> são definidos os atributos e métodos da classe. Na comunidade de programadores Java existe a convenção de associar às classes identificadores que iniciem com letra maiúscula, enquanto que os identificadores de variáveis e métodos devem iniciar com letra minúscula. No exemplo IV-1 é mostrada a definição da classe de uma classe com o identificador Pessoa. Nele foi definido um construtor para atribuir valores aos atributos e definido os métodos para acesso aos atributos. Note que os métodos que atribuem valores às variáveis da classe iniciam com a palavra set e os métodos que retornam os valores armazenados nas variáveis da classe iniciam com a palavra get. Este padrão de nomeação de método foi adotado a partir da versão 1.1 da linguagem e é muito importante seguí-lo, principalmente se pretendemos implementar JavaBeans, como será visto no Capítulo X. public class Pessoa String nome; String telefone; String endereço; public Pessoa(String n, String t, String e) nome = n; telefone = t; endereço = e; public void setnome(string n) nome=n; public void settelefone(string t) telefone = t; public void setendereço(string e) endereço = e; 67 public String getnome() return nome; public String gettelefone() return telefone; public String getendereço() return endereço; Exemplo IV-1. Definição da classe Pessoa. Unidade de Compilação Cada arquivo fonte Java é uma unidade de compilação. Ao compilar o arquivo fonte o compilador irá gerar um arquivo.class para cada classe constante no arquivo. O nome de cada arquivo será o nome da cada classe.
69 68 Os atributos ou variáveis declarados na classe são criados somente quando os objetos são criados. Ou seja, cada objeto da classe Pessoa terá a sua variável nome, telefone e endereço. Portanto, a variável pertence à instância da classe e não à classe. Variáveis que pertencem ao objeto são chamadas variáveis de instância. Com os métodos o raciocínio é o mesmo, ou seja, para podermos usar o método getnome() é preciso criar um objeto da classe Pessoa e usar o método getnome() deste objeto. Métodos que pertencem ao objeto são chamados de métodos de instância. Como veremos mais adiante, podemos usar a palavra reservada static para declarar variáveis e métodos que pertencem à classe (métodos e variáveis de classe). Os métodos definem operações sobre os atributos. Eles possuem a forma geral: <modificador> <tipo> <identificador>(<parâmetros>) <corpo do método> onde <modificador> é um modificador de acesso, <tipo> é o tipo do valor de retorno do método, <identificador> é o identificador do método e <parâmetros> é uma lista de parâmetros. O corpo do método é composto de comandos, expressões e declarações de variáveis locais ao método. Os métodos do exemplo IV.1 são muito simples. Eles apenas retornam um valor ou atribuem um valor aos atributos do objeto. Construtores Podemos observar no exemplo IV-1 que existe um método público com o mesmo nome da classe e que não define um valor de retorno. Métodos como esse são denominados de construtores e são chamados pelo operador new. Uma classe pode não ter construtor ou ter vários, desde que tenham diferentes tipos de argumentos. O interpretador decidirá qual chamar a partir dos argumentos passados para o construtor. Se o programador não declarar nenhum construtor então o compilador irá criar automaticamente um construtor default para a classe. Valor de Retorno
70 Com exceção dos construtores todos os outros métodos precisam retornar algum valor. Se programador não precisa que o método retorne algum valor, então ele deve especificar que o método irá retornar o valor void que é o valor vazio. Se o método não foi definido com valor de retorno void e não é um construtor então é preciso que no corpo do método seja indicado o valor de retorno. Esta indicação deve ser feita em toda ramificação de código que leve ao fim da execução do método e é feita com a palavra chave return seguida de uma expressão: return <expressão> A expressão pode ser uma variável, um literal ou qualquer outra expressão que gere um valor do tipo especificado para o retorno da função. O exemplo IV-2 mostra um método que retorna um inteiro. Note que toda ramificação de código que leve ao fim da execução do método necessita de um comando return. public class ExemploIV2... public int calc(int a) if (a==0) return 1; else return a*a; 69 Exemplo IV-2. Método que retorna um inteiro. Objetos Para se criar um objeto de uma classe (ou uma instância da classe) é preciso primeiro declarar uma variável que irá referenciar o objeto, para depois criar o objeto. A declaração de um referência a um objeto é feita obedecendo a seguinte forma geral: <nome da classe> <identificador>; Por exemplo, para declarar uma referência à um objeto da classe Pessoa devemos adicionar a seguinte linha ao código.
71 70 Pessoa p1; Pode-se declarar mais de uma referência a objetos de uma classe em uma única declaração, separando os identificadores por vírgula. Pessoa p1, p2; Para criar um objeto usa-se o operador new conforme a forma geral abaixo: <identificador> = <nome da classe>(<argumentos>); onde <argumentos> é uma lista de argumentos que serão passados para um método especial da classe, denominado de construtor. Por exemplo, para criar um objeto do tipo Pessoa podemos usar a declaração abaixo: P1 = new Pessoa( Ana, , Rua ); Podemos também combinar a declaração da referência com a criação do objeto em uma única linha de código. Pessoa P1 = new Pessoa( Ana, , Rua ); Uma vez criado o objeto podemos acessar os elementos do objeto por meio da referência ao objeto e do operador.. Por exemplo para acessar o método getnome() do objeto referenciado pela variável P1 devemos usar o seguinte código: P1.getNome(); Para acessar um atributo diretamente a sintaxe é mesma. Por exemplo, para acessar o atributo nome diretamente basta usar a seguinte linha de código. P1.nome; O exemplo IV-3 mostra a criação de um objeto do tipo Pessoa e o acesso a seus membros. Para que a classe seja executável diretamente pela máquina virtual é preciso que ela possua um método main() que servirá como ponto de entrada para a execução. Podemos então, inserir código no método main() para criar uma instância da classe para que, a partir de então, possamos a acessar os
72 membros do objeto. O método main() existe antes da criação da instância, e portanto pode ser acessado pela máquina virtual, porque foi declarado com o modificador static. Isto significa que o método pertence à classe (método de classe) e não ao objeto (método de instância). 71 public class Pessoa... // igual ao exemplo IV.1... public static void main(string args[]) Pessoa p; p = new Pessoa( Ana, , Rua ); // Acessa os dados via métodos System.out.println( Nome: +getnome()); System.out.println( Telefone: + gettelefone()); System.out.println( Endereço: + getendereço()); // Altera o endereço setendereço( Rua ) // Acessa os atributos diretamente System.out.println( Endereço: + endereço); Exemplo IV-3. Definição da classe Pessoa. As implementações da classe Pessoa mostradas nos exemplos IV-1 e IV-3 possuem alguns problemas que iremos abordar gradativamente. O primeiro problema está relacionado com o acesso direto aos atributos. Em um programa bem projetado o acesso aos atributos da classe a partir de métodos definidos em outras classe é restringido de modo a diminuir a dependência em relação à representação interna de uma classe. Quanto maior for esta independência maior é a facilidade para manutenção do programa. Para se restringir o acesso aos membros da classe deve-se preceder a declaração dos membros com palavras reservadas denominadas de modificadores de acesso que serão descritos a seguir.
73 Modificadores de acesso Java na Prática 72 Os modificadores de acesso tem por objetivo definir a visibilidade de um membro de uma classe (atributo ou método) em relação à outras classes. A tabela IV-1 mostra os modificadores de acesso disponíveis em Java. Modificador default public protected private Descrição Somente classes do mesmo package possuem acesso Todos possuem acesso Apenas os membros da classe e subclasse Apenas os membros da classe Tabela IV-1. Modificadores de acesso. Quando não é especificado nenhum modificador de acesso é assumido o modificador default, também chamado de friend ou escopo de pacote. Ou seja, não existe uma palavra reservada default. O acesso default determina que todas as classes dentro do mesmo package (pacote) podem acessar os membros da classe corrente. O conceito de package será detalhado mais adiante, no entanto podemos adiantar que um package é um agrupamento de classes e interface (também será vista mais adiante). Toda classe e interface pertence a um package, mesmo que o programador não indique o package explicitamente. Se o programador não indicar o package então será assumido que a classe ou interface pertence ao package default. O modificador public determina que todas as classes podem acessar o membro. Já o modificador protected limita o acesso ao membro apenas aos membros da própria classe e aos membros das subclasses da classe. Finalmente, o modificador private limita o acesso ao membro apenas aos membros da própria classe. O exemplo IV-4 é uma redefinição da classe Pessoa, explicitando os modificadores de acesso dos atributos com objetivo de limitar a visibilidade dos mesmos. public class Pessoa protected String nome; protected String telefone; protected String endereço;...
74 // igual ao exemplo IV Exemplo IV-4. Definição da classe Pessoa com os atributos protegidos. O modificador de acesso usado para os atributos no exemplo IV-4 foi o protected. Portanto, se for criada uma subclasse da classe Pessoa, esta poderá acessar diretamente os atributos da classe Pessoa. O exemplo IV-5 mostra a definição da classe Funcionario como subclasse de Pessoa. Note que o atributo de instância salario da classe Funcionario é definido com o modificador de acesso private, impedindo, dessa forma, acessos diretos ao atributo a não ser por membros da própria classe. Note também que o construtor da classe Funcionario possui uma chamada a um método super. Esta chamada representa, na verdade, uma chamada ao construtor da superclasse. Isto será visto com maiores detalhes mais adiante. public class Funcionario extends Pessoa private double salario; public Funcionario(String n, String t, String e, double s) super(n,t,e); salario = s; public void setsalario(double s) salario = s; public double getsalario() return salario; Exemplo IV-5. Definição da classe Funcionario. Para utilizar a classe Funcionario pode-se criar uma terceira classe, como mostra o exemplo IV-6, com o um método main() que serve de ponto de entrada para o início da execução. Foi colocado propositadamente uma tentativa de acesso ao atributo privado por um método externo à classe do atributo, de modo a ilustrar um acesso não permitido. public class TesteFun public static void main(string a[])
75 Funcionario f; f = new Funcionario( Carlos, , Av Nova 32,3000.0); System.out.println( Nome: +f.getnome()); 74 // Acesso Ilegal. Use o método. System.out.println( Salario: +f.salario); Exemplo IV-6. Utilizando a classe Funcionario. Outros Modificadores Além dos modificadores de acesso existem outros modificadores que estabelecem significados distintos da visibilidade. A tabela IV-2 exibe os modificadores adicionais. Modificador static final synchronized native Descrição A variável ou método é comum a todas as instâncias da classe. O valor da variável não pode ser modificado. atribui o monitor do objeto ao thread corrente. indica que se trata de um método nativos. O modificador static Tabela IV.2 Modificadores de adicionais. Um atributo declarado com o modificador static significa que é um atributo da classe e não de instância. Em outras palavras, existirá apenas um atributo, ocupando uma única posição de memória, em vez de um atributo para cada instância, cada um com sua própria posição de memória. Como resultado, o atributo existirá antes mesmo que qualquer objeto seja criado e, se for público, poderá ser acessado prefixando-o com o nome da classe e o operador.. O exemplo V-7 mostra uma classe com um atributo static. Note que o não é preciso declarar uma instância da classe X para que o atributo exista e para acessá-lo basta prefixá-lo como nome da classe. class X
76 Java na Prática static public int tot =0; 75 public class Main public static void main(string a[]) X.tot = 2; System.out.println( Valor de tot: +X.tot); Exemplo IV-7. Classe com atributo static. Outro efeito é que o atributo será compartilhado por todas as instâncias da classe. Desta forma, se um objeto alterar o conteúdo do atributo, a alteração afetará todos os outros objetos da classe. O exemplo V-8 mostra a declaração de dois objetos da classe X. Um dos objetos altera o valor do atributo static e o outro imprime o valor do atributo. Isto mostra que as alterações feitas por um objeto no atributo é percebida por todos os outros objetos da classe. class X static public int tot =0; public void inc() tot++; public class Main public static void main(string a[]) X x1, x2; x1 = new X(); x2 = new X(); // x1 modifica o valor x1.inc(); System.out.println( Valor de tot: +x2.tot); Saída:
77 Valor de tot:1 Java na Prática 76 Exemplo IV-8. Objetos com atributo static. O modificador static também pode ser usado na declaração de métodos. Neste caso o método fica ligado à classe e não à instância, sendo denominado de método de classe. Como consequência o método pode ser acessado antes de existir um objeto da classe. Vários exemplos mostrados neste livro tem pelo menos um método static: o método main(). Ele serve como ponto de entrada para a execução da aplicação Java, uma vez que inicialmente não existem objetos. Os métodos static não podem acessar atributos e outros métodos não estáticos da classe, a não ser que exista uma declaração de um objeto da classe no corpo do método. Os métodos estáticos são muito usados em classes utilitárias. Classes utilitárias são classes que servem para agrupar métodos que prestam algum tipo de serviço a outras classes. Por exemplo, o método exit() é um método estático da classe System. Ele serve para encerrar a execução da máquina virtual corrente. Sendo um método estático não é preciso criar um objeto da classe System para acessá-lo. A classe System é uma classe utilitária, contendo vários métodos e atributos estáticos úteis para outras classes. O Modificador final O modificador final pode ser usado em atributos, métodos e classes. O modificador final impede se modifique o que está sendo prefixado. Um atributo prefixado com final indica que uma vez definido seu valor ele não será alterado. Ou seja, é uma constante. A definição do valor do atributo pode ser feito tanto na declaração como durante a execução do programa, sendo que esta última possibilidade não era permitida na versão 1.0 da linguagem Java. O exemplo IV-9 mostra o uso do modificador final em atributos. class X public int k =0; public class Main1 public final int i = 1; public final int j; public final X x = new X();
78 77 public Main1()j = 2; public static void main(string a[]) Main1 m = new Main1(); m.x.k= 3; // Erro : o atriuto não pode ser modificado // m.j = 4; System.out.println("Valor de k:"+ m.x.k); System.out.println("Valor de j:"+ m.j); System.out.println("Valor de i:"+ m.i); Exemplo IV-9. Uso do modificador final em atributos. Note que no exemplo IV-9 o atributo j é inicializado no construtor e não na declaração. No exemplo o atributo k do objeto referenciado pelo atributo x é modificado, mesmo sendo x um atributo final. Isto ocorre porque x é uma referencia a um objeto e não o próprio objeto, portanto, no caso de referencias a objeto, o modificador final implica que o atributo não pode referenciar outro objeto, mas não significa que o objeto referenciado não possa ser modificado. Um método declarado com o modificador com final não pode ser sobrescrito em uma subclasse. Alguns compiladores podem aproveitar isso e verificar se é possível definir as chamadas a esses métodos como inline. Nas chamadas inline o compilador substitui a chamada ao método pelo o código do método, tornando mais eficiente a execução do programa. O exemplo IV-10 mostra o uso do modificador final em um método. class X public int k =0; public final void mostra() System.out.println("Valor de k:"+ k); class SX extends X // Erro: Sobrescrita ilegal public void mostra()
79 Java na Prática System.out.println("k:"+ k); 78 Exemplo IV-10. Uso do modificador final em métodos. Uma classe declarada com o modificador com final impede que sejam criadas subclasses desta classe. O programador pode decidir fazer isso por razões de projeto ou de segurança, já que neste caso ele terá certeza que a funcionalidade da classe não será alterada. Outra razão pode ser eficiência, uma vez que os métodos de uma classe final também são final, e portanto podem ser otimizados para serem tratados como inline. Os Modificadores synchronized e native O modificador synchronized é para controlar o acesso a áreas críticas no processamento concorrente em Java. É usado em métodos e blocos de comandos, e seu uso será discutido em detalhes no Erro! A origem da referência não foi encontrada.. O modificador native é usado em métodos para indicar que o método foi implementado em outra linguagem de programação. O programador deve apenas indicar a assinatura do método, como mostrado abaixo: native int meumetodo(int a); O uso de chamadas nativas restringe a portabilidade do programa. No momento a linguagem Java só suporta chamadas nativas implementadas na linguagem C/C++. Referências Compartilhadas Um outro problema das implementações da classe Pessoa mostradas nos exemplos IV-1 e IV-3 está relacionado com o compartilhamento de referências. No construtor, assim como nos métodos setxxx, o argumento é atribuído diretamente às variáveis de instância. Como toda variável de objeto em Java é na verdade uma referência para o objeto então as variáveis de instância da classe Pessoa e os argumentos do construtor e dos métodos setxxx são referências a objetos do tipo String. Portanto, ao atribuir diretamente o argumento às variáveis de instância geramos um compartilhamento de referências. Essa
80 situação é melhor ilustrada graficamente, como pode ser visto na figura IV-1, onde é mostrado o compartilhamento entre o argumento n e a variável de instância nome após a atribuição. O problema do compartilhamento de referências, é que ele permite acesso aos objetos referenciados internamente pelos objetos. Com isso é possível modificar, com métodos externos ao objeto, o objeto referenciado. No entanto, no caso particular dos objetos da classe String, isso não chega a ser um problema. Strings são constantes, ou seja, seus valores não podem ser alterados após a sua criação e, portanto, objetos Strings podem ser compartilhados sem riscos. Toda alteração em um objeto do tipo String retorna um novo objeto String, deixando a original inalterada. 79 Antes da atribuição n nome Objeto null Após a atribuição n Objeto nome Figura IV-1.Compartilhamento de referencias. Se o programador desejar alterar o valor de uma cadeia de caracteres sem criar um novo objeto, então ele deve optar por utilizar a classe StringBuffer que implementa uma sequência mutável de caracteres. No entanto, é preciso estar alerta para o problema de compartilhamento de referências. O trecho de código do exemplo IV-11 mostra uma ocorrência de compartilhamento de referência. No exemplo a classe Pessoa é definida usando a classe StringBuffer para armazenar as sequências de caracteres. Os objetos da classe X possuem uma referência a um objeto do tipo Pessoa. Na construção do objeto Pessoa são passados como parâmetros as referências dos objetos StringBuffer do objeto da classe X, gerando um compartilhamento de referencias. Quando é invocado o método m1() do objeto da classe X, este concatena a sequência fim ao StringBuffer referenciado por n. Como n compartilha o mesmo objeto com
81 a variável de instância nome do objeto Pessoa, então a alteração também afeta o objeto Pessoa, ocasionando um efeito colateral, provavelmente indesejável. Para se evitar esta situação é necessário que o objeto crie uma cópia do objeto que está recebendo e retorne uma cópia do objeto que referencia. No caso de objetos do tipo StringBuffer basta construir um novo objeto, passando como parâmetro a String correspondente ao StringBuffer anterior, como mostrado abaixo: nome = new StringBuffer(n.toString()); No entanto, no caso de objetos mais complexos é mais complicado criar uma cópia por meio do construtor. Neste caso é necessário criar uma cópia usando o método clone(). Este método será visto na próxima seção. class Pessoa StringBuffer nome; StringBuffer telefone; StringBuffer endereço; 80 public Pessoa(StringBuffer n, StringBuffer t, StringBuffer e) nome = n; telefone = t; endereço = e; // O restante da da classe é semelhante ao exemplo VI.1 //... public class X StringBuffer n = new StringBuffer("Pedro"); StringBuffer t = new StringBuffer(" "); StringBuffer e = new StringBuffer("Rua 17/148 Natal"); Pessoa p; public X() p = new Pessoa(n,t,e); public void m1() n.append("fim"); public static void main(string a[]) X x = new X(); x.m1(); System.out.println(x.p.nome);
82 Java na Prática Exemplo IV-11. Exemplo de compartilhamento de referência. 81 Copiando Objetos Como visto na seção anterior, a atribuição envolvendo duas referencias a objetos acarreta um compartilhamento do objeto e não uma cópia do objeto original. No entanto, em algumas situações não queremos compartilhar um objeto e sim criar uma cópia de um objeto que já existe, ou seja um clone. Existem dois tipos de modos de se clonar um objeto: 1- Cópia rasa (shallow copy) - neste caso os atributos do objeto resultante recebem os valores dos atributos correspondentes no objeto original. Os objetos referenciados pelo objeto original serão compartilhados pelo objeto resultante. A figura IV-2 ilustra este tipo de cópia. Note o compartilhamento das referencias. Figura IV-2.Cópia rasa entre os objetos A e D. 2- Cópia profunda (deep copy) - neste caso não só o objeto original é copiado como também todos os objetos por ele referenciados. Este tipo de cópia pode ser bastante ineficiente. A figura IV-3 ilustra este tipo de cópia.
83 82 Figura IV-3.Cópia profunda entre os objetos A e D. Java oferece suporte para a cópia rasa. Para que a classe esteja apta a ser clonada é preciso que ela implemente a interface Cloneable (interfaces serão discutidas mais adiante). A interface Cloneable não declara nenhum método, portanto, para implementar a interface basta apenas escrever Cloneable após a palavra reservada implements na declaração da classe, como mostrado no exemplo IV-12. class Clonavel implements Cloneable int a[]; public Clonavel(int n) a = new int[n]; public void setelemento(int i, int el) if (i < a.length) a[i] = el; public int getelemento(int i) if (i < a.length) return a[i]; else return 0; public Object clone() try return super.clone(); catch (CloneNotSupportedException e) return null; public class CloneTeste
84 public static void main(string a[]) Clonavel x1 = new Clonavel(2); x1.setelemento(0,1); x1.setelemento(1,2); Clonavel x2 = (Clonavel) x1.clone(); x1.setelemento(0,3); System.out.println("x1:"+x1.getElemento(0)+", "+ x1.getelemento(1)); System.out.println("x2:"+x2.getElemento(0)+", "+ x2.getelemento(1)); Saída 83 x1:3, 2 x2:3, 2 Exemplo IV-12. Cópia rasa. Além de implementar a interface é preciso sobrescrever o método clone() da classe Object, uma vez que este método é declarado com o modificador protected e portanto não é visível fora da linha hierárquica. Se o objetivo for uma cópia rasa então basta invocar o método clone() da classe Object de dentro método que o está subscrevendo, como feito no exemplo IV-12, por meio da palavra chave super. Note que é preciso capturar uma exceção que é lançada (a CloneNotSupportedException) caso a classe não suporte a clonagem (se não implementar a interface Cloneable). Note também que o método clone() da classe Object retorna um Object e, portanto, precisa ser feita uma conversão explicita para o tipo apropriado. Para demonstrar que o exemplo IV-12 implementa uma cópia rasa é feito um teste onde são criados dois objetos, sendo um deles o clone do outro. A saída do programa mostra que ao alterarmos um objeto referenciado por um dos objeto que foram duplicados a alteração afeta o outro, uma fez que as referências são compartilhadas. Se o usuário desejar realizar uma cópia profunda, então ele deve fornecer o código necessário ao sobrescrever o método clone(). Neste caso ele deve invocar os métodos clone() dos objetos referenciados. Esta pode ser uma tarefa bastante complexa uma vez que um objeto pode referenciar vários objetos e estes podem referenciar outros objetos e assim sucessivamente, em uma longa cadeia de referências. Além disso é preciso contar com o fato dos objetos
85 referenciados terem sobrescrito o método clone(). O exemplo IV-13 mostra como implementar uma cópia profunda para o exemplo anterior. 84 class Clonavel implements Cloneable int a[]; public Clonavel(int n) a = new int[n]; public void setelemento(int i, int el) if (i < a.length) a[i] = el; public int getelemento(int i) if (i < a.length) return a[i]; else return 0; public Object clone() Clonavel o = null; try o = (Clonavel) super.clone(); catch (CloneNotSupportedException e) return null; o.a = (int []) a.clone(); return o; public class CloneTeste public static void main(string a[]) Clonavel x1 = new Clonavel(2); x1.setelemento(0,1); x1.setelemento(1,2); Clonavel x2 = (Clonavel) x1.clone(); x1.setelemento(0,3); System.out.println("x1:"+x1.getElemento(0)+", "+ x1.getelemento(1)); System.out.println("x2:"+x2.getElemento(0)+", "+ x2.getelemento(1));
86 Saída x1:3, 2 x2:1, 2 Java na Prática Exemplo IV-13. Cópia profunda. 85 Note que no exemplo IV-13 a saída do programa mostra que ao alterarmos um objeto referenciado por um dos objeto que foram duplicados a alteração não afeta o outro, uma fez que as referências foram clonadas. Isso foi possível porque os arrays sobrescrevem o método clone(). E quanto às classes da biblioteca padrão do SDK? Elas sobrescrevem o método clone()? A resposta é: poucas sobrescrevem. Portanto, o programador deve estar atento e verificar se é possível invocar o método clone() de uma classe da biblioteca padrão ou se terá que proceder de outra forma. O objeto this Em algumas situações é necessário referenciar o próprio objeto corrente. Por essa razão todo objeto possui um atributo especial identificado por this que é uma referência para o próprio objeto. O exemplo IV-14 mostra um uso típico do atributo this. Ele mostra um método cujo parâmetro formal possui o mesmo nome de um atributo de instância. Para distinguí-los é necessário qualificar o atributo da instância com o atributo this. public class objetogeo protected Color cor; protected int x, y; public objetogeo(color cor, int x, int y) this.cor = cor; this.x=x; this.y = y; public Color retcor() return cor; Exemplo IV-14. Exemplo de uso do atributo this.
87 Outro uso típico e quando queremos passar a instância corrente como argumento de um método. Como mostra o exemplo IV-15. class X public void mx(y y)...; class Y X x; public void my() x.mx(this);... Exemplo IV-15. Exemplo de uso do atributo this como argumento. No exemplo IV-15 o objeto da classe Y passa ele próprio como argumento do método mx() do objeto da classe X. Packages Pacotes (Packages) é a solução proposta por Java para agrupar Classes e Interfaces relacionadas para compor bibliotecas. As Interfaces serão vistas mais adiante. Organizando as classes em pacotes evita-se a colisão entre os nomes das classes. No caso dos métodos isso não é necessário, pois métodos com o mesmo nome em classes diferentes são facilmente distinguidos uma vez que são qualificados pelos objetos das classes. No entanto como distinguir classes com o mesmo nome? Esse problema é muito comum, principalmente quando se trabalha em equipe. Como impedir que um programador trabalhando no mesmo projeto crie uma classe com o mesmo nome que outra criada por outro programador? Na hora de unir as classes o sistema não irá compilar devido ao problema de redeclaração de classes. Sendo Java uma linguagem para atuar na Internet o problema é ainda mais grave: como impedir que uma classe que foi
88 baixada de outra máquina não possua o mesmo nome de uma classe local? Para contornar todos estes problemas foram criados os Pacotes. 87 Usando Packages Toda classe pertence a um pacote. No caso do programador não indicar a que pacote pertence a classe, o compilador irá assumir que a classe pertence ao pacote default que é o diretório corrente. Para se usar uma classe definida em outro package é preciso usar a palavra chave import seguida do nome da classe qualificada pelo nome do pacote como mostra a forma geral abaixo: import nome_package.nomeclasse; Por exemplo, para importar a classe Color do pacote java.awt o programador deverá usar a diretiva abaixo: import java.awt.color; Se o programador quiser importar todas as classes do pacote java.awt basta usar o caractere *` no lugar do nome da classe: import java.awt.*; É possível usar uma classe declarada em outro pacote sem usar a palavra chave import. Nesse caso é necessário qualificar o nome da classe com o nome do pacote toda vez que a classe for referenciada. Por exemplo, no caso da classe Color seria necessário qualificá-la da seguinte forma: java.awt.color Existem dois pacotes que não precisam ser importados para que suas classes possam ser usadas sem a qualificação: o pacote default e o pacote java.lang. O pacote default agrupa todas a classes que estão no diretório corrente e o pacote java.lang agrupa as classes do núcleo básico da linguagem. Criando Packages
89 Para incluir uma unidade compilação em um pacote basta que o programador inclua a seguinte declaração no arquivo. package nomepacote; Importante: a declaração acima precisa ser a primeira declaração no arquivo. O nome do pacote pode ser composto por vários nomes separados pelo caractere., como no caso do pacote java.awt. As classes de um determinado pacote devem ser colocadas em um diretório obedecendo a mesma estrutura do nome do pacote, a partir de algum diretório constando na variável de ambiente classpath. A variável de ambiente classpath indica os diretórios que servem como pontos de entrada para procura de classes pela máquina virtual. Por exemplo, suponha que eu resolva colocar as classes sobre objetos geométricos em um determinado pacote, digamos br.com.alcione.geo. Para indicar que unidade de compilação pertence a esse pacote devemos colocar a diretiva adequada no início do arquivo fonte, como mostrado no exemplo IV package br.com.alcione.geo; public class objetogeo protected Color cor; protected int x, y; public objetogeo(color cor, int x, int y) this.cor = cor; this.x=x; this.y = y; ; public Color retcor() return cor; Exemplo IV-16. Indicando que uma classe pertence ao pacote br.com.alcione.geo. Para que a máquina virtual consiga achar o pacote devemos colocá-lo em diretório com a mesma estrutura do nome do pacote e que tenha como raiz algum diretório constante na variável de ambiente classpath. Por exemplo, suponha que a variável classpath contenha os seguintes diretórios: CLASSPATH=.;C:\JAVA\LIB;C:\meujavalib
90 89 então o arquivo objetogeo.class resultante da compilação do arquivo objetogeo.java pode ser colocado no diretório: C:\meujavalib\br\com\alcione\geo Note que a estrutura do diretório combina com a estrutura do nome do pacote. Para usar a classe em outra unidade de compilação basta incluir a seguinte diretiva no arquivo fonte: import br.com.alcione.geo.*; O formato do nome do pacote não foi escolhido por acaso. Se existe a pretensão de usá-lo na Internet então deve-se adotar um nome que seja único na rede. Para garantir esta unicidade utiliza-se o nome do domínio do criador do pacote com a ordem dos campos invertida, evitando assim a colisão de nome na Internet. Por exemplo, nome do pacote br.com.alcione.geo origina-se da inversão do nome do domínio (alcione.com.br) concatenado com o nome geo. Por simplicidade, na maioria dos exemplos adotados neste livro, adotaremos o pacote default. Empacotando packages em arquivos JARs Um sistema pode possuir um grande número de classes organizadas em pacotes. Essas classes espalhadas pela estrutura de diretório de um computador pode se tornar um problema no momento da distribuição de uma aplicação. O melhor seria organizar as classes em um único arquivo para poder ser distribuída. Para solucionar esses e outros problemas foi introduzido a partir da versão 1.1 da linguagem Java o formato de arquivo JAR (Java Archive). O formato de arquivo JAR permite o agrupamento de vários arquivos em um único arquivo. Além disso, o formato JAR permite a compressão de dados de modo otimizar o armazenamento e a transmissão de dados. O principais benefícios do uso do formato JAR são os seguintes: Menor tempo de carga de Applets: Se todos os arquivos relacionados com um Applet estão agrupados em um único arquivo JAR, apenas uma transação HTTP será necessária para carregar o Applet. Este tempo será ainda menor se os arquivos estiverem compactados. Segurança: o conteúdo do arquivos JAR podem ser assinados digitalmente. Deste modo, os usuários que reconhecerem a sua assinatura podem conceder
91 privilégios para acessar recursos que não estariam disponíveis, caso contrário. Portabilidade: a API para manipulação dos arquivos JAR é parte integrante do núcleo da biblioteca de classes de Java, o que a torna independente de plataforma. Outros benefícios foram adicionados com o lançamento da versão 1.2 do SDK, como por exemplo, informações sobre versão. Os arquivos contidos em um arquivo JAR podem ser de tipos variados como, como bytecodes, imagens e sons, podendo pertencer a um Applet, aplicação ou simplesmente a uma biblioteca de classes. Além disso, um arquivo JAR pode conter uma descrição dos arquivos armazenados, chamada de manifest. O SDK vem com uma ferramenta para criação e manutenção de arquivos JAR. O formato mais comum de invocação do programa é: jar [opções] destino arquivo(s)-de-entrada onde destino é o nome do arquivo, arquivo(s)-de-entrada representa o nome dos arquivos que serão incluídos no arquivo JAR e opções é uma coleção de letras com o seguinte significado: 90 Opções c t x x file f m v O M Descrição Cria um arquivo novo. Lista o conteúdo. Extrai todos os arquivos. Extrai o arquivo especificado. Indica que o nome do arquivo de saída, caso seja a criação de um arquivo, ou de entrada caso contrário, será especificado. Sem essa opção o programa jar assume que saída ou a entrada será a padrão. Indica que o primeiro argumento será o nome de um arquivo manifest criado pelo usuário. Gera saída com os detalhes da execução. Não comprime os arquivos. Usada para criar arquivos JAR que podem ser colocados no classpath. Não cria o arquivo manifest. Tabela IV-3. Opções para uso do comando jar. Se um subdiretório for incluído nos arquivos de entrada então o subdiretório será automaticamente inserido no arquivo JAR, incluindo os seus subdiretórios. As informações sobre o caminho até o arquivo é também armazenada.
92 Para invocar uma aplicação armazenada em um arquivo JAR é necessário passar o nome do arquivo JAR como parâmetro. Na versão JDK1.1 é preciso usar o comando jre no lugar do comando java. Por exemplo, suponha que armazenamos uma aplicação, cuja classe principal é Mainclass, em um arquivo arq.jar. Para executá-la basta digitar o comando: jre -cp arq.jar Mainclass onde a opção cp indica que o arquivo arq.jar deve ser anexado ao classpath. Já na versão SDK1.2 usa-se o comando java, sendo que o arquivo JAR deve conter um arquivo manifest indicando a classe principal: java -jar arq.jar A tabela VII-4 mostra alguns exemplos do uso do utilitário que gera arquivos JAR. 91 jar cf meuarq.jar *.class jar cmf meuarq.jar meumanifest.mf *.class jar tf meuarq.jar jar xf meuarq.jar Cria um arquivo JAR com o nome meuarq.jar contendo todos os arquivos de classes do diretório corrente. Um arquivo manifest é gerado automaticamente. Como o exemplo anterior, porém o arquivo manifest meumanifest.mf é adicionado. Lista o conteúdo do arquivo meuarq.jar. Extrai todos os arquivos contidos em meuarq.jar. Tabela IV-4. Exemplos do uso do comando jar. O arquivo manifest é um arquivo de texto onde cada linha possui par diretiva-valor no formato: <diretiva>:<valor> As diretivas dependem da versão do SDK. Por exemplo, na versão 1.2 a classe principal é indicada por meio da diretiva Main-Class, como mostrado abaixo: Main-Class: nome da classe Criando os próprios arquivos manifest
93 Os arquivos manifest são criados automaticamente. O conteúdo original de um arquivo manifest criado pelo SDK 1.3 é: Manifest-Version: 1.0 Created-By: (Sun Microsystems Inc.) A opção m da ferramenta para criação de arquivos JAR permite adicionar informações ao arquivo manifest default. É necessário que o usuário crie um arquivo contendo as adições que devem ser feitas. O formato básico do comando é: jar cmf arq_adições arq_jar arquivo(s) O arquivo de adições é simplesmente um arquivo texto onde cada linha é par diretiva-valor. Por exemplo se o usuário quiser especificar que a classe que deve ser usada como ponto de entrada é a classe Mainclass, então basta inserir a seguinte linha no arquivo de adições: Main-Class: Mainclass 92 O Mecanismo de Extensão A partir da versão 1.2 a linguagem Java introduziu um mecanismo para adicionar classes ao núcleo básico da linguagem, sendo uma alternativa ao uso da variável de ambiente classpath. Este mecanismo é chamado de Mecanismo de Extensão. O mecanismo de extensão também oferece suporte para envio de classes pela rede para uso em Applets. As extensões são agrupadas em arquivos JAR. Uma vez feito isso, pode-se transformar as classes em extensões utilizando-se uma das duas formas: 1. Extensão Instalada colocando o arquivo JAR em um local predeterminado na estrutura de diretórios do ambiente de tempo de execução de Java (Java Runtime Environment ou JRE). 2. Extensão de download Extensão referenciando o arquivo JAR de uma forma específica, a partir do manifest de outro arquivo JAR. Extensão Instalada
94 Para incluir uma nova classe ou pacote no núcleo básico da linguagem basta colocar o arquivo JAR que contém a classe no diretório /lib/ext do ambiente de tempo de execução. O diagrama mostrado na figura IV-4 mostra a localização deste diretório dentro da estrutura de diretórios do ambiente Java. 93 Figura IV-4. localização do diretório ext dentro da estrutura de diretórios Java. Por exemplo, suponha que desejamos acrescentar o pacote do exemplo IV-16 como uma extensão da linguagem Java no ambiente local. Para isso devemos criar primeiro o arquivo JAR. No ambiente DOS o comando seria o abaixo, executado no diretório acima do diretório /br: jar cvf geo.jar br\com\alcione\geo\objetogeo.class Após isso colocaríamos o arquivo geo.jar dentro do diretório /ext. Para se usar a classe o procedimento é idêntico ao uso no caso do classpath, ou seja basta colocar a seguinte diretiva no arquivo fonte: import br.com.alcione.geo.*; Extensão de Download As extensões de download ocorrem quando arquivos JAR ou classes são indicados como extensões de outros arquivos JAR. Por exemplo, suponha que existem dois arquivos JAR, x.jar e y.jar, localizados no mesmo diretório.
95 Para tornar o arquivo y.jar uma extensão do arquivo x.jar basta colocar a seguinte informação no manifesto do arquivo x.jar: Class-Path: y.jar Deste modo toda classe no arquivo x.jar pode referenciar as classes do pacote y.jar. Se os arquivos não estiverem no mesmo diretório será necessário indicar o caminho relativo até o arquivo da extensão. É possível indicar várias extensões, separando-as por espaço ou usando várias diretivas Class-Path. Para indicar um diretório como extensão basta adicionar o sufixo / ao nome do diretório. Por exemplo: Class-Path: meudir/ 94 Derivando classes O reuso de componentes de programas é uma das principais metas dos projetistas de linguagens de programação. A razão deste objetivo é que a reutilização de componentes economiza esforços e acelera o processo de desenvolvimento de sistemas de computadores. Essa meta pode ser atingida em parte por meio da criação de classes que são extensões de classes predefinidas. As extensões herdam todos os atributos e métodos da classe base ou superclasse. Por exemplo, suponha que alguém tenha definido uma classe para representar os dados de uma conta bancária: Figura IV-5. Classe Conta. Suponha também que além da conta corrente básica, mais dois tipos de conta devam ser criadas: poupança e conta especial. A poupança possui todos os
96 atributos de uma corrente e mais um atributo contendo a data de abertura da conta que será usada para cálculo do valor dos juros e correção. A conta especial possui todos os atributos de uma corrente e mais um atributo contendo o valor de limite de crédito. No exemplo descrito acima seria um desperdício de tempo e código se precisássemos codificar todos os atributos e métodos relacionados à classe Conta nessas duas novas classes. Para evitar esta duplicação de esforço basta declaramos essas duas novas classes como subclasses da classe Conta. Desta forma apenas os atributos que não estão declarados na superclasse e métodos associados precisam ser codificados, uma vez que os membros da superclasse são herdados pelas subclasses. A figura IV-3 mostra o diagrama de classe contendo o relacionamento entre as classes e o exemplo IV-17 mostra o código Java correspondente. 95 Figura IV-3. Relação entre as classes. public class Conta protected long numero; protected long cpf; protected double saldo; public Conta(long n, long c, double s) numero = n; cpf = c; saldo = s;
97 Java na Prática public long getnumero()return numero; public long getcpf()return cpf; public double getsaldo()return saldo; public void setsaldo(double s)saldo = s; public void somasaldo(double v)saldo += v; 96 import java.util.*; public class CPoupanca extends Conta private Date dataaber; public CPoupanca(long n, long c, double s, Date d) super(n,c,s); dataaber = d; public Date getdata()return dataaber; public class CEspecial extends Conta private double limite; public CEspecial(long n, long c, double s, double l) super(n,c,s); limite = l; public double getlimite()return limite; public double getsaldo()return saldo+limite; public void setlimite(double l) limite = l; Exemplo IV-17. Implementação das Classes com herança. Podemos notar que para declarar uma classe como subclasse de outra basta colocarmos a palavra-chave extends após o nome da classe e o nome da superclasse após a palavra-chave. Apenas o nome de uma superclasse é permitido, portanto Java não permite que uma classe seja subclasse de mais de uma classe (herança múltipla). Herança Múltipla
98 A capacidade de possuir mais de uma superclasse é chamada de herança múltipla. A linguagem C++ suporta a herança múltipla. No entanto, a implementação desta facilidade é complexa e sua utilização tende a gerar erro. Além disso poucos são os casos que demandam o uso desta solução, e mesmo nestes casos é possível utilizar outras soluções. Por essas razões a linguagem Java não implementou a herança múltipla. Nos casos onde é necessário que um objeto adote o comportamento de mais de uma classe devemos utilizar as interfaces, como será visto mais adiante. 97 Todos os membros declarados com os modificadores public e protected podem ser acessados diretamente na subclasse. O membros declarados com o modificador private não são visíveis dentro da subclasse. super Podemos notar também que nos construtores das subclasses do exemplo IV-17 existem chamadas a um método super(). Este método representa a chamada ao construtor da superclasse. Se não for chamado explicitamente o compilador irá gerar código para chamar o construtor default da superclasse. No entanto, se desejamos chamar explicitamente um construtor com argumentos devemos usar o método super(). A classe Object É importante salientar que toda classe em Java, com exceção da classe Object é subclasse de alguma classe. Se o programador não declarar a classe base então a classe base será a classe Object. Desta forma a classe Object é a raiz da hierarquia de classes da linguagem Java. Sendo assim, todo objeto na linguagem Java herda os seguintes métodos públicos e protegidos da classe Object. Métodos Públicos Descrição public boolean equals(object obj) public final native Class getclass() public native int hashcode() public final native void notify() public final void notifyall() public String tostring() Verifica se dois objetos são iguais. A verificação é feita sobre o endereço do objeto. Retorna a classe do objeto em tempo de execução. Retorna o valor hash do objeto. Notifica um thread que está esperando sobre o monitor do objeto. Notifica todos os threads que está esperando sobre o monitor do objeto. Retorna uma representação em String do
99 98 public final void wait() public final void wait(long timeout) public final void wait(long timeout, int nanos) Métodos Protegidos protected native Object clone() protected void finalize() objeto. Espera no monitor do objeto para ser notificado por outro thread. Espera no monitor do objeto para ser notificado por outro thread. Espera no monitor do objeto para ser notificado por outro thread. Descrição Cria uma cópia rasa do objeto corrente. Chamado pelo coletor de lixo antes de liberar a área de memória. Tabela IV-5. Métodos públicos e protegidos da classe Object. Sobrescrita e Polimorfismo Outro ponto interessante que podemos observar no exemplo IV-17 é que a classe CEspecial possui um método com a mesma assinatura que um método da superclasse. É o método getsaldo() que no caso da classe CEspecial deve incluir o limite de crédito como parte do saldo. Neste caso, o método da subclasse está sobrescrevendo ou ocultando o método da superclasse. Toda vez que o método getsaldo() for invocado dentro de algum método da classe CEspecial ou for chamado por meio de um objeto da classe CEspecial o método chamado será o declarado na classe e não o declarado na superclasse. No entanto, é possível invocar o método declarado na superclasse de dentro de algum método da subclasse, bastando qualificar o método com a palavra chave super como mostrado abaixo: super.getsaldo(); Por exemplo, o método getsaldo() da classe CEspecial poderia ser reescrito da seguinte forma: public double getsaldo() return super.getsaldo()+limite; Suponha agora uma classe como mostrada no exemplo IV-18. O método imprimesaldo() espera um objeto da classe Conta mas recebe um objeto da classe CEspecial. Isto não causa nenhum problema porque todo objeto da classe
100 CEspecial é também um objeto da classe Conta. No entanto fica a pergunta: que método getsaldo() é invocado dentro o método imprimesaldo()? O da classe Conta ou o da classe CEspecial? 99 public class TestaConta public void imprimesaldo(conta c) System.out.println( O Saldo e : +c.getsaldo()); public static void main(string a[]) TestaConta testa = new TestaConta(); CEspecial c= new CEspecial(1, , 500.0, 100.0); testa.imprimesaldo(c); Exemplo IV-18. Teste de polimorfismo. A resposta é: o método chamado pertence a instância da classe Cespecial. Portanto a regra é a seguinte: sempre é chamado o método da declarado na classe a que o objeto pertence e não da classe superclasse. Para que essa regra seja implementada é necessário que a associação da chamada do método com o código do método em casos como no exemplo IV-18 seja feita em tempo de execução. Isso ocorre porque somente em tempo de execução será possível saber a classe do objeto que está sendo recebido como parâmetro. O momento em que ocorre a associação de um identificador com a objeto que identifica é chamado tempo de amarração (ou vinculação). Se este momento ocorre em tempo de compilação a amarração é dita estática (static binding). Se associação ocorre em tempo de execução a amarração é dita dinâmica (dynamic binding). Assim, na linguagem Java, em se tratando da associação da chamada do método ao código do método, a amarração é dinâmica. A única exceção é se o método for declarado como final. Neste caso a amarração é estática porque não é possível sobrescrever o método. A implementação da amarração dinâmica é claramente mais ineficiente que a estática, uma vez que o compilador precisa gerar código para determinar a classe do objeto em tempo de execução. No entanto, existem vantagens consideráveis na adoção da amarração dinâmica. Ela facilita o reuso e a extensão de programas. Por exemplo, se criarmos uma nova subclasse da classe Conta que também sobrescrevesse o método imprimesaldo(), nenhuma
101 modificação precisaria ser feita em métodos como o imprimesaldo() da classe TestaConta, uma vez que o método correto sempre é chamado. Portanto, podemos ampliar a hierarquia de um conjunto de classes sem precisar alterar, na maioria das vezes, os métodos que fazem uso dessa hierarquia. A amarração dinâmica de métodos também acarreta um comportamento polimórfico. Por exemplo, o objeto que é passado para o método imprimesaldo() pode exibir comportamento diversos, dependendo da classe a qual o objeto realmente pertence. 100 Amarração Dinâmica em C++ Apesar da amarração dinâmica entre os identificadores dos métodos e o corpo dos métodos ser considerada uma característica importante para linguagens orientadas a objetos, nem todas a linguagens deste estilo de programação adotam este comportamento como padrão. Por exemplo, a linguagem C++ adota a amarração estática como padrão. Caso o programador deseje que os métodos possuam amarração dinâmica então deve instruir o compilador explicitamente prefixando os métodos com a palavra-chave virtual. A razão para esta decisão dos projetistas da linguagem está na eficiência. A amarração dinâmica é bem mais ineficiente que a amarração estática e a linguagem C++ se propõe a ser uma linguagem que é capaz de gerar código que executa eficientemente. O exemplo IV-19 procura esclarecer melhor a amarração dinâmica dos métodos na linguagem Java. public class Animal public void fala() public class Cachorro extends Animal public void fala()system.out.println( Au au! ); public class Gato extends Animal public void fala()system.out.println( Miau! ); public class UsaAnimal public void falaanimal(animal a)a.fala();
102 Exemplo IV-19. Classes de vozes dos animais. 101 No exemplo IV-19 o método falaanimal() da classe UsaAnimal recebe qualquer subclasse da classe Animal é invoca o método fala() da subclasse. Para incluir mais um animal na hierarquia basta declarar mais uma subclasse: public class Pato extends Animal public void fala()system.out.println( Quá Quá! ); Exemplo IV-20. Classe com a voz do pato. Nada precisa ser alterado nas outras classes. A nova classe pode ser inclusive subclasse de outra subclasse que não haverá problema: public class GatoRonronando extends Gato public void fala()system.out.println( Purr! ); Exemplo IV-21. Classe com a voz do pato. Podemos observar que a classe Animal propriamente dita não faz nada a não ser servir de cabide para as outras classes. É pouco provável que ela seja instanciada, já que o seu único método não faz nada. Classes como essa funcionam como um esquema para as subclasses e deveriam ser tratadas adequadamente pelo compilador. Este tópico será tratado na próxima seção. Classes e Métodos Abstratos Classes abstratas são esquemas ou esqueletos de classes. Uma classe abstrata não pode ser instanciada e possui um ou mais métodos abstratos. Um método abstrato é um método sem corpo cuja assinatura é precedida da palavrachave abstract. As subclasses de uma classe abstrata precisam sobrescrever os métodos abstratos, caso contrário também serão abstratas e não poderão ser instanciadas. A classe Animal do exemplo IV-19 é uma ótima candidata à classe abstrata. Para torná-la uma classe abstrata basta preceder a declaração da classe
103 com a palavra chave abstract e declarar como abstratos os métodos que se deseje tornar abstratos, como mostra o exemplo IV-22. abstract public class Animal abstract public void fala(); Exemplo IV-22. Classe abstrata Animal. Além de não poder ser instanciada a classe abstrata possui outras limitações. Uma classe abstrata não pode possuir métodos estáticos e construtores. Outra limitação é que os métodos abstratos não podem ser privados, uma vez que as subclasses precisam sobrescrever os métodos abstratos. 102 Interfaces As interfaces podem ser encaradas como classes abstratas completamente não implementadas. Ou seja, todos os métodos são abstratos e os atributos só podem ser do tipo static final (constantes). A forma geral de uma interface obedece o seguinte esquema: interface identificador corpo da interface A interface não é usada como superclasse de outras classes. Uma interface é implementada por outras classes. A classe que implementa uma interface precisa fornecer uma implementação para todas os métodos definidos na Interface. Para que uma determinada classe seja vista como uma implementação de uma interface é preciso indicar isto colocando a palavra chave implements na declaração da classe e após a palavra chave a lista com o nome de todas as interfaces que se deseja implementar, separados por vírgula. O esquema abaixo mostra a forma geral do uso de interfaces. class Identificador implements Interface1, Interface2,... corpo da classe
104 103 O leitor deve estar se perguntando: se a interface é apenas uma classe abstrata completamente não implementada então porque não criar simplesmente uma classe abstrata? A resposta está relacionada com o tipo de herança que ocorre entre as classes de Java. Como já mencionamos, a linguagem Java não permite a herança múltipla, ou seja, uma classe só pode ser subclasse de apenas uma classe. No entanto, existem situações em que é necessário definir uma classe que possa ser vista de modos distintos. É ai que entram as interfaces. A interface define uma forma de ver uma classe, restando para a classe a implementação desta visão e a linguagem Java permite que uma classe implemente tantas interfaces quantas se desejar. Desta forma Java evita os principais problemas da herança múltipla (herdando múltiplas declarações e não múltiplas implementações), sem abrir mão de criar classes que podem ser vistas de formas diferentes. Por exemplo, suponha que um método receba um objeto que representa a interface de saída de um determinado programa. O objetivo deste método é usar um método do objeto de interface para escrever uma mensagem na tela. O exemplo IV-23 mostra o código da classe que contém o método. public class EscreveMem public static void escreve(interusuario inter) inter.exibemensagem( ola mundo! ); Exemplo IV-23. Classe que recebe um objeto de interface. Suponha também que deseja-se que o programa tenha tanto uma interface com o usuário em modo gráfico como em modo texto. Usaremos de agora em diante as iniciais IU para significar interface com o usuário para evitar a confusão com o conceito de interfaces que estamos introduzindo. Se deseja-se que o método escreve() possa receber instâncias dos dois tipos de IU é necessário que ambas sejam do tipo InterUsuario. No entanto, como veremos mais adiante, as classes que implementam IU gráficas devem ser subclasses de classes predefinidas como por exemplo da classe java.awt.frame. Portanto, temos agora um problema de herança múltipla. A classe que implementa a IU gráfica deve herdar de uma classe como a Frame e da classe InterUsuario. A solução é simples. Basta definir InterUsuario como uma interface como mostra o exemplo IV-24.
105 104 interface InterUsuario abstract public void exibemensagem(string men); Exemplo IV-24. Definição de como InterUsuario interface. Agora basta que cada classe que representa um tipo de IU implemente a interface InterUsuario. O exemplo IV-25 mostra a implementação de uma IU gráfica simples e o exemplo IV-26 mostra a implementação de uma IU em modo texto simples. import java.awt.*; import java.awt.event.*; public class Janela extends Frame implements InterUsuario private Label label1; public Janela() addwindowlistener (new WindowAdapter() public void windowclosing(windowevent evt) System.exit(0); ); setlayout (new BorderLayout()); label1 = new Label(); label1.settext ("Mensagem"); add (label1, "South"); setsize(200,100); public void exibemensagem(string men) label1.settext(men); Exemplo IV-25. IU gráfica. public class Console implements InterUsuario
106 Java na Prática public void exibemensagem(string men) System.out.println(men); Exemplo IV-26. IU em modo texto. 105 O exemplo IV-27 mostra uma forma de uso das classes definidas nos exemplos de IV-24 a IV-26. public class UsaIUs public static void main(string args[]) Console c = new Console(); Janela j = new Janela(); j.setvisible(true); EscreveMem. escreve(c); EscreveMem. escreve(j); Exemplo IV-27. Uso das IUs. As interfaces também são úteis sob o aspecto de projeto. Em C++ os programadores estão acostumados separar a interface de uma classe de sua implementação. Geralmente dentro das classes são declaradas apenas as assinaturas dos métodos sendo que a implementação dos mesmos pode ser feita posteriormente. Isto permite separar a especificação do uso de uma classe (o que ela faz) de sua implementação (como ela faz), reduzindo a complexidade da tarefa de implementação e permitindo uma melhor divisão de tarefas. No entanto, na linguagem Java os métodos de uma classe são geralmente implementados dentro da classe. As Interfaces e Classes podem ser utilizadas para permitir a separação entre a interface e a implementação a exemplo de como e feito em C++. Classes Internas
107 A partir da versão 1.1 da linguagem Java foi incluída a permissão para definir uma classe dentro da definição de outra. As classes declaradas dentro de outras são denominadas de Classes Internas. O exemplo IV-28 mostra um esquema contendo o posicionamento relativo de uma classe interna. public class Externa... class Interna... Exemplo IV-28. Posicionamento relativo das classes Interna e Externa. Os atributos e métodos declarados na classe externa são visíveis pela classe interna, mesmo os declarados como protected ou private. No entanto, o contrário não é verdadeiro, ou seja, os atributos e métodos da instância da classe interna só são visíveis pela classe externa se forem declarados como públicos. Como já foi dito as classes mais externas (nível topo) só podem ser declaradas como públicas ou com escopo de pacote (default). No entanto, as classes internas também podem receber os qualificadores private e protected. O efeito destes qualificadores sobre a visibilidade da instância da classe é o mesmo obtido sobre um atributo qualquer. O exemplo IV-29 mostra o caso de uma classe contendo uma classe interna privada. 106 public class Calc Incrementa inc1, inc5; private int num; public Calc () inc1=new Incrementa(1); inc5=new Incrementa(5); private class Incrementa int i; public Incrementa(int ai)i=ai; public int inc()return i+num; public int calcula()return inc1.inc()+inc5.inc();
108 Java na Prática Exemplo IV-29. Uso de classes internas. 107 A inclusão de classes internas foi uma modificação a nível de linguagem motivada pelo modelo de eventos proposto a partir da versão 1.1. Neste modelo, como poderá ser constatado no capítulo que trata sobre a AWT, as classes internas se ajustam perfeitamente. O exemplo IV-30 mostra o uso de uma classe interna para capturar eventos. Este tipo de uso será tratado com maiores detalhes no capítulo sobre AWT. import java.awt.*; import java.awt.event.*; public class Janela extends Frame public Janela (String Titulo) super(titulo); addwindowlistener(new JanelaListener()); setsize(100,50); private class JanelaListener implements WindowListener public void windowopened(windowevent we) public void windowclosed(windowevent we) public void windowiconified(windowevent we) public void windowdeiconified (WindowEvent we) public void windowactivated(windowevent we) public void windowdeactivated(windowevent we) public void windowclosing(windowevent we) setvisible(false); dispose(); System.exit(0); Exemplo IV-30. Uso de classes internas para receber eventos. Classes Internas Anônimas Java permite que se crie um objeto de uma classe sem nome. As classes são declaradas no momento do retorno de um método ou durante a passagem de parâmetros e o objeto é criado quando o argumento ou a expressão de retorno é
109 avaliada. O exemplo IV-31 mostra o retorno de um objeto de uma classe anônima. 108 public class TesteAnonima public Object retanon() return new Object() // Definição da classe anônima private int i=10; public int value()return i; ; Exemplo IV-31. Retorno de um objeto de uma classe anônima. Pode parecer pelo exemplo IV-31 que o objeto retornado é uma instância da classe Object, no entanto, na verdade é uma instância de uma classe anônima que é subclasse da classe Object. A classe anônima possui atributos e métodos próprios que a diferenciam da classe Object e estão definidos na declaração da classe. Usamos a classe Object apenas para exemplificar com uma classe conhecida. Qualquer classe pode fazer o papel de superclasse. O exemplo IV-32 mostra o uso de uma classe anônima na criação de um objeto para lidar com eventos sobre um botão. import java.awt.*; import java.awt.event.*; public class BT extends Frame Button bt; public BT() bt = new Button( OK ); bt.addactionlistener( new ActionListener() public void actionperformed(actionevent e) dispose(); );
110 109 Exemplo IV-32. Uso de uma classe anônima para tratamento de eventos. As classes anônimas são adequadas para situações onde necessitamos em um local específico de um objeto de uma classe ligeiramente diferente de outra classe já declarada. Com a classe anônima evitamos ter que declarar uma nova classe para atender apenas uma necessidade local. Inicialização e Finalização de Objetos Inicialização automática A inicialização automática de variáveis em Java se dá de duas formas: 1. Variáveis locais a métodos nenhuma inicialização automática é realizada. O compilador checa se as variáveis foram inicializadas antes do seu uso. 2. Variáveis de instância ou de classe tipos numéricos e caractere são inicializados com zeros (caso o caractere seja impresso a saída será um espaço), tipo booleano é inicializado com false e referências são inicializadas com null. Inicialização de atributos de Instância Existem dois lugares onde os atributos de instância podem ser inicializados: dentro do construtor ou na declaração do atributo. O exemplo IV-33 mostra estes dois tipos de inicialização. O atributo a é inicializado na declaração e o atributo b é inicializado no construtor. public class ExInicia int a = 10; // inicializa o atributo a int b; public ExInicia() b = 20; // inicializa o atributo b
111 Exemplo IV-33. Tipos de inicialização de atributos de instância. 110 Qual é a diferença entre os dois tipos de inicialização? A primeira diferença é que os construtores são parametrizados e, portanto, a inicialização com construtor pode variar conforme os parâmetros passados. A segunda diferença é que a inicialização no construtor é feita após a inicialização na declaração. A inicialização pode ser realizada com o uso de invocação de métodos e com o uso de outros atributos, desde que já tenham sido inicializados. Inicialização de atributos de classe A inicialização das variáveis de classe não pode ocorrer dentro dos construtores, uma vez que o construtor pertence ao objeto e não à classe. Outra diferença é que a inicialização só ocorre uma vez, já que existe apenas uma versão atributo. A inicialização ocorre quando o primeiro objeto é criado. Blocos de inicialização Java permite que as inicializações de atributos, tanto de classe como de instâncias de classe sejam agrupadas em blocos. O exemplo IV-34 mostra o uso de blocos de inicialização. public class ExBlocoInicia static int a; static int b; int c; int d; static a = 10; b = 20; c = 30; d = 40;
112 111 Exemplo IV-34. Uso de blocos de inicialização. Note que o bloco que inicia os atributos de instância é precedido pela palavra reservada static. Ordem de inicialização Como já foi dito a inicialização na declaração é feita antes da inicialização no construtor. E no caso da herança? O que é inicializado primeiro? A classe ou a superclasse? Nesse caso a superclasse é inicializada primeiro. Mesmo que não ocorra uma invocação explícita ao construtor da superclasse é chamado o construtor default. O exemplo IV-35 mostra a seqüência de inicialização em uma herança. class X public X() System.out.println("X foi inicializada!"); public class Y extends X public Y() System.out.println("Y foi inicializada!"); public static void main(string a[]) new Y(); Saída: X foi inicializada! Y foi inicializada! Exemplo IV-35. Sequência de inicialização em herança.
113 Finalização de Objetos Java na Prática 112 Com a linguagem Java não existe a preocupação de liberar a memória, como é o caso de C/C++, uma vez que os objetos que não estão sendo referenciados são coletados automaticamente pelo coletor de lixo. No entanto, como Java pode invocar uma função de C/C++ por meio de uma chamada nativa, e como esta função pode alocar memória que não será devolvida pelo coletor de lixo, então pode ser necessária alguma forma de devolver explicitamente esta memória quando o objeto for coletado. Para esses casos a programador deve prover o método finalize(), que será invocado quando o objeto for coletado. O método finalize() não é para ser chamado explicitamente. Ele é invocado pela máquina virtual antes de devolver a memória usada pelo objeto. Nele o programador deve devolver a memória alocada por métodos nativos e associada ao objeto. Conversão de Objetos Uma variável declarada como referencia para objetos de uma determinada classe pode receber referencias de objetos de subclasses sem a necessidade de se usar qualquer operador de conversão. A conversão é feita automaticamente, uma vez que toda instância de uma subclasse é também uma instância da superclasse. Por exemplo: Object o; String s = teste ; o = s; A atribuição acima é possível porque a classe Object é uma superclasse de String (Na verdade é superclasse de todas as classes). Já a conversão inversa só é possível se o objeto for realmente do tipo da subclasse e, mesmo assim, é preciso utilizar um operador de conversão forçada (casting): Object o; String s = teste ; String t; o = s; t = (String) o;
114 Exceções Java na Prática 113 Quanto maior o número de situações de exceção que um determinado programa consegue lidar, mais robusto é este programa. Para construir um programa robusto o programador deve prever, além das situações onde os dados de entrada e os recursos se apresentam de forma adequada, as situações onde algo pode impedir a computação normal. Existem três abordagens típicas para esses casos: 1) Ignorar neste caso a ação será a padrão do sistema, provavelmente a interrupção do programa, ou no mínimo do comando onde ocorreu o erro, e a exibição de uma mensagem ininteligível na saída padrão, gerada pelo sistema. Esta é abordagem mais simples, mas certamente não é a mais interessante, principalmente do ponto de vista do usuário final. 2) Retornar o código de erro. No caso da linguagem Java este retorno só pode ser feito via valor de retorno da função. No entanto algumas vezes não existem valores disponíveis para indicar o erro. Por exemplo, quando um método retorna um valor inteiro costuma-se usar os valores 0 ou 1 para sinalizar uma condição de erro, mas como agir no caso desses e todos os outros valores de inteiros serem valores válidos em uma execução normal? Existe ainda o problema de que esta abordagem não resolve o problema de erros que ocorrem nos construtores. Como os construtores não retornam valores não é possível sinalizar erros dessa forma. Esta é uma abordagem muito comum entre os programadores de C e Pascal, e é ainda usada pelos programadores de C++. No entanto, as linguagens mais modernas como C++ e Java disponibilizam recursos que permitem um melhor tratamento de exceções, como veremos na terceira abordagem. 3) Declarar um ou mais tratadores de exceções. Um tratador de exceção é uma parte do código que tem por objetivo recuperar a execução do programa após a ocorrência da exceção, permitindo que o sistema se comporte suavemente, mesmo sob condições adversas. A sintaxe e semântica dos tratadores de exceção variam muito de linguagem para linguagem, mas tem tendido a uma certa uniformização nas linguagens mais modernas. A linguagem Java, por ter como objetivo ser uma linguagem segura, obriga que os
115 114 programadores capturem todas as exceções, menos as derivadas da classe RuntimeException. Na linguagem Java a amarração do tratador à exceção é definida em tempo de compilação. Nela, o programador envolve os comandos passíveis de gerar exceções em blocos try/catch com o formato geral mostrado abaixo: try código que pode gerar exceções catch(classe de exceção1 objeto) tratamento da exceção1 catch(classe de exceção2 objeto)... catch(classe de exceçãon objeto) tratamento da exceçãon As exceções são representadas como objetos A porção catch do bloco possui um bloco com comandos para tratamento da exceção. O bloco que trata as exceções não tem acesso às variáveis declaradas dentro do bloco try, uma vez que o escopo destas variáveis está delimitado pelo bloco. O exemplo IV-36 mostra um bloco para capturar a exceção NumberFormatException que pode ser lançada pelo método estático parseint() da classe Integer se o método receber como argumento uma cadeia de caracteres que não pode traduzido para uma representação inteira. Note que a variável i não pode ser acessada dentro do bloco catch, uma vez que foi declarada dentro do bloco try. public class Excecao1 public static void main(string a[]) try int i = Integer.parseInt(a[0]); System.out.println("A cadeia passada pode ser + tranformada para número : +i); catch(numberformatexception e) System.out.println("A cadeia passada não pode ser + tranformada para número : +a[0]);
116 Exemplo IV-36. Capturando a exceção NumberFormatException. 115 Continuação após o Tratamento da Exceção Se o bloco catch não possuir nenhum comando return, throw ou alguma chamada à função exit(), então o programa é reassumido após o último bloco catch. O caso de relançamento de exceções (throw) será tratado mais adiante. A hierarquia de Exceções As exceções são objetos que pertencem a classes que estão organizadas em uma hierarquia. A classe que se encontra no topo da hierarquia é a classe Throwable. A classe Throwable possui duas subclasses: Exception e Error. Exception e suas subclasses são usadas para indicar condições que podem ser recuperadas. Error e suas subclasses indicam condições que em geral não podem ser recuperadas, causando a terminação do programa. Todos os objetos da classes Exception devem ser capturados ou explicitamente relançados para um nível acima na pilha de chamadas. Isto só não é válido para os objetos das classes RuntimeException. A RuntimeException é suas subclasses definem as exceções que podem ser lançadas durante a execução normal da máquina virtual, como divisão por zero ou indexação fora dos limites do array. A exceção NumberFormatException mostrada no exemplo é uma subclasse da RuntimeException. Como essas exceções podem ser lançadas por um número muito grande de métodos, seria trabalhoso para o programador se ele fosse obrigado a incluir código para capturar todas essas exceções. Portanto, instâncias da classe RuntimeException e suas subclasses não precisam ser capturadas. A Figura IV-4 mostra uma parte da hierarquia Throwable até o nível 3. O programador pode também adicionar classes a esta hierarquia como veremos mais adiante.
117 116 Figura IV-4. Hierarquia parcial das exceções. Capturando mais de uma exceção Um método pode lançar mais de uma exceção, assim como pode existir mais de um método dentro de um bloco try/catch com potencial de lançar exceções. Portanto, muitas vezes é preciso definir um bloco try/catch com capacidade de tratar mais de uma exceção. Isto é feito por meio da definição de mais de uma cláusula catch. Cada cláusula catch trata uma exceção. Como a busca pela cláusula catch que deve tratar uma determinada exceção é feita de cima para baixo é preciso tomar o cuidado de posicionar as o tratamento das exceções mais genéricas mais abaixo do que o tratamento das exceções mais específicas. Caso contrário o tratamento das exceções mais especificas nunca será executado. O exemplo IV.xx mostra um trecho de código cujo bloco try/catch é capaz de capturar as exceções IOException, NumberFormatException e Exception. Note que por ser uma exceção mais genérica que as outras duas o tratamento da exceção Exception é posicionado após os outros. Como as exceções NumberFormatException e IOException não se encontram na mesma linha de herança, a posição de uma relativa a outra não tem significado. import java.io.*; class Excecao2 public static void main(string a[])
118 Java na Prática try BufferedReader in = new BufferedReader( new InputStreamReader(System.in)); System.out.print("Entre um número:"); int i = Integer.parseInt(in.readLine()); catch(ioexception e) System.out.println( Erro de leitura! ); catch(numberformatexception e) System.out.println( Não é um número! ); catch(exception e) System.out.println( ocorreu algum erro ); Exemplo IV.XX- Capturando a várias exceções. 117 Lançando exceções Até agora temos visto apenas como capturar as exceções lançadas. Está na hora de olharmos o outro lado da moeda, ou seja, como lançar as exceções. As exceções são lançadas utilizando-se o operador throw. A forma geral do comando é a seguinte: throw <objeto> Pode-se também criar o objeto no instante do lançamento, invocando o construtor. Neste caso a forma geral do comando seria a seguinte: throw new <classe>(<parâmetros>) Note que pode-se passar parâmetros para o construtor que podem ajudar na recuperação do problema pelo tratador de exceções. O método onde a exceção é lançada deve tratar a exceção ou passá-la para método que o invocou. Para passar a exceção para o método que o invocou a assinatura do método corrente deve conter uma cláusula throws após a lista de argumentos, como mostrado abaixo: <modificador><tipo retorno> <identificador>(<argumentos>) throws <exceção 1 >,<exceção 2 >,...,<exceção n >
119 118 Note que é possível indicar na assinatura do método o repasse de várias exceções. O exemplo IV.xx mostra tanto o lançamento de um objeto da classe Exception caso os parâmetros para o construtor da classe não sejam corretos. import java.io.*; class NumPos private int num; public NumPos(int anum) throws Exception if (anum < 1) throw new Exception( Número não positivo ); num = anum; public class Excecao3 public static void main(string a[]) try NumPos np = new NumPos(Integer.parseInt(a[0])); catch(exception e) System.out.println(e.getMessage()); Exemplo IV.XX- Lançando exceções. Na verdade o pode-se usar a cláusula throws na assinatura do método para repassar qualquer exceção que se desejar. O exemplo IV.xx, mostra o método m1 que repassa qualquer objeto da classe Exception que for lançado em seu corpo, mesmo os que não forem explicitamente lançado por meio do comando throw: public class Excecao4 public void m1(int i) throws Exception if (i == 0 ) throw new NumberFormatException(); System.out.println(2/i);
120 Exemplo IV.XX- Repassando todas as exceções. 119 Comportamento do Sistema diante das Exceções Agora que já vimos como tratar e como lançar exceções podemos discutir como é o comportamento global do sistema diante das exceções. Ou seja, como é a sequência de busca por um tratador após a ocorrência de uma exceção? Quando ocorre uma exceção em um método o sistema verifica se o comando onde ocorreu a exceção está incluso em bloco try/catch com uma cláusula catch associada à exceção. Neste caso o controle é transferido para o bloco de tratamento da cláusula catch. Caso contrário, o sistema desempilha um nível na pilha de chamada de métodos e verifica se a chamada ao método anterior está inclusa em bloco try/catch com uma cláusula catch associada à exceção. O sistema prossegue desta forma até que um bloco tratador seja encontrado. Caso encontre o bloco tratador e este não possua nenhum comando de saída (return ou System.exit()), então o controle será retomado no nível de chamada onde foi tratado após o último bloco catch. e não onde ocorreu a exceção. Caso não encontre nenhum bloco tratador, ao atingir o nível mais alto da pilha de chamadas o programa é abortado. A figura IV.xx procura ilustrar o comportamento do sistema diante de uma exceção, tratada em um nível da pilha de chamada diferente de onde ocorreu. Pilha de chamadas m1 m2 m3 tratamento da exceção Código fonte public class Excecao5 public void m1(int i) try m2(i) catch (Exception e)...;... public void m2(int i) throws Exception m3(i); ocorrência da exceção public void m3(int i) throws Exception if (i == 0 ) throw new Exception(); System.out.println(2/i);
121 120 Figura IV.XX- Propagação das exceções. Criando suas próprias exceções Se o programador concluir que nenhuma exceção pré-existente se encaixa no tipo de erro que pretende sinalizar, então criar uma classe nova para representar essa exceção. A única condição é que a nova classe seja uma extensão da subclasse Throwable ou de uma de suas subclasses. Procure estender a classe Exception ou uma de suas subclasses, exceto o ramo da RuntimeException, uma vez que, como já foi dito, esta ramificação da hierarquia de exceções é utilizada para exceções do sistema. Por exemplo, podemos rescrever o exemplo IV.xx de modo que o método lance uma exceção definida pelo programador. import java.io.*; class NumPosException extends Exception public NumPosException(String m)super(m); class NumPos private int num; public NumPos(int anum) throws NumPosException if (anum < 1) throw new NumPosException ( Número não positivo ); num = anum; public class Excecao6 public static void main(string a[]) try NumPos np = new NumPos(Integer.parseInt(a[0])); catch(numposexception e) System.out.println(e.getMessage()); Exemplo IV.XX- Lançando exceções.
122 121 A cláusula finally Existe mais uma cláusula opcional do bloco try/catch. Esta é a cláusula finally. A cláusula finally abriga uma trecho de código que deve ser executado independente se ocorreu ou não uma exceção no bloco try/catch. Pode ser usada para realizar operações de finalizações, como por exemplo fechamento de arquivos e canais de comunicação. O exemplo IV.xx ilustra o uso da cláusula finally. import java.io.*; class Excecao7 public static void main(string a[]) try BufferedReader in = new BufferedReader( new InputStreamReader(System.in)); System.out.print("Entre um número:"); int i = Integer.parseInt(in.readLine()); catch(ioexception e) System.out.println( Erro de leitura! ); catch(numberformatexception e) System.out.println( Não é um número! ); finally() System.out.println( Terminou. ); Exemplo IV.XX- Usando a cláusula finally. No exemplo IV.xx se não ocorrer nenhuma exceção a cláusula finally é executada e a execução é reassumida após a cláusula finally. Caso ocorra alguma exceção no bloco try/catch e exista alguma cláusula catch associada à exceção então a cláusula finally é executada após o tratamento da exceção e a execução é reassumida após a cláusula finally. Caso ocorra alguma exceção no bloco try/catch e não exista alguma cláusula catch associada à exceção então a cláusula finally é executa antes da transferência do controle para o método chamador.
123 122 Documentando o código Uma das maiores dificuldades no desenvolvimento de software é conseguir que os programadores produzam uma documentação relacionada com os programas que estão desenvolvendo. Eles não gostam de interromper o desenvolvimento para escrever paralelamente a documentação e após o término da implementação não se animam a passar um grande período de tempo documentando todo o código escrito. Pensando nisso os projetistas de Java desenvolveram um meio do programador embutir a documentação no próprio código, de modo que, não precisasse interromper a implementação. A documentação é inserida como um comentário e usa uma sintaxe especial que pode ser interpretada pelo programa javadoc para gerar um documento HTML. Todo comentário que deve ser interpretado pelo javadoc deve ser iniciado por /** e terminado por */. O programador pode inserir a documentação de duas formas: 1. rótulos (tags) iniciados pelo e que denotam comandos de documentação; ou 2. por meio de código HTML embutido. Rótulos O javadoc relaciona os comentários com a estrutura codificada após o comentário. As estruturas relacionadas com os comentários são as classes, variáveis e métodos. A exceção do todos os rótulos estão relacionados com a documentação de classes ou métodos.
124 O é usado para gerar links para documentação de outras classes. Os links não serão verificados pelo javadoc. Os formatos possíveis são os <nome da <nome da classe>#<nome do método> 123 Rótulos para documentação de classes Os seguintes rótulos são usados na documentação de classes: Rótulo <versão> Usado para incluir informação sobre a <autor> Usado para incluir informação sobre o autor. Tabela IV.xx- Rótulos para documentação de classes. Rótulos para documentação de métodos Os seguintes rótulos são usados na documentação de métodos: <nome> <nome da classe> Descrição Usado para incluir informação sobre o parâmetro do método. Usado para incluir informação sobre o valor de retorno do método. Usado para incluir informação sobre a exceção que pode ser lançada pelo método. Incluído a partir da versão 1.1. Indica que o método pode não ser mantido nas próximas versões da linguagem. Tabela IV.xx- Rótulos para documentação de métodos. Exemplo de Documentação O exemplo IV.xx ilustra o uso dos rótulos para documentação do código.
125 /** Pessoa Mantem os dados pessoais de uma Alcione de 1.0 */ public class Pessoa private String Nome; private String Tel; private String End; 124 // Construtor n String contendo o t String contendo o e String contendo o não retorna valor */ public Pessoa(String n, String t, String e) Nome = n; Tel = t; End = e; /** Retorna o nome da uma String contendo o nome */ public String getnome()return Nome; /** Retorna o telefone da uma String contendo o telefone */ public String gettel()return Tel; /** Retorna o endereço da uma String contendo o endereço */ public String getend()return End; Exemplo IV.XX- Exemplo de documentação. HTML embutida
126 O programador pode incluir código HTML dentro dos comentários para documentação da mesma forma que faria em um documento da Web, como mostra o exemplo IV.xx. /** * <b>pessoa<\b> * Possui os seguintes objetivos * <ol> * <li> Objetivo1 * <li> Objetivo2 * </ol> */ public class Pessoa... Exemplo IV.XX- Uso de HTML embutida para documentar código. O javadoc irá ignorar os caracteres * no início de cada linha ao gerar a documentação. Não use rótulos de títulos como <h1> ou <hr>, uma vez que o javadoc irá inserir rótulos de títulos automaticamente. 125 Agenda Eletrônica versão Console 1.0 Iniciaremos agora a primeira versão de um programa que implementa uma agenda eletrônica de endereços e telefones. Este programa evoluirá ao longo do livro, de acordo com os conhecimentos que serão apresentados. Em sua primeira versão a agenda eletrônica terá uma interface baseada em linha de comando e não armazenará o seu conteúdo em algum dispositivo de memória permanente, o que não a torna muito útil em termos práticos. Nas versões posteriores a agenda eletrônica armazenará o seu conteúdo em arquivos e Banco de Dados, ganhará interface gráfica, poderá ser utilizada em navegadores de Web, terá uma arquitetura Cliente/Servidor, e outras facilidades. Esperamos com isso mostrar os vários aspectos da linguagem durante a evolução do programa. Outros exemplos serão apresentados de acordo com a necessidade, porém a agenda eletrônica permanecerá como exemplo principal. O diagrama de classes da figura III.xx mostra os objetos que compõe a versão console da agenda.
127 126 Agenda +inserir(pessoa p) +pessoas getpessoas() +Pessoa getpessoa(string Nome) 0..* Pessoa -String Nome -String Tel -String End +getnome() +gettel() +getend() AgendaInt +obterpessoa() +exibirlista() +exibirpessoa() Figura III. Diagrama de classes da versão console da agenda eletrônica. Os objetos da classe Pessoa armazenam os dados de uma pessoa. Ela fornece os métodos para o acesso a esses dados. O objeto da classe Agenda é composto por vários dados de pessoa. Este fato está representado no diagrama pela associação de agregação. A classe Agenda possui os métodos necessários para inserção e recuperação dos dados. O método inserir(), trata de inserir uma pessoa na lista de pessoas, o método getpessoas(), retorna uma lista contendo todos os objetos da classe Pessoa e o método getpessoa() retorna um objeto da classe Pessoa com o nome especificado. A Classe AgendaInt é responsável por fornecer os métodos que implementam a interface com o usuário. Ela foi criada por questões de projeto, uma vez que é importante manter separado o código das interfaces do sistema. Primeiramente apresentaremos o código da classe Pessoa. /** */ Pessoa public class Pessoa
128 private String Nome; private String Tel; private String End; Java na Prática // Construtor public Pessoa(String n, String t, String e) Nome = n; Tel = t; End = e; // Metodos public String getnome()return Nome; public String gettel()return Tel; public String getend()return End; 127 Por questões de simplicidade a classe pessoa possui apenas um construtor. As variáveis de instância são declaradas private para prevenir acessos que não sejam por meio dos métodos da classe. Segue abaixo o código da classe Agenda: /** */ AGENDA Versão Console 1.0. public class Agenda Pessoa pessoas[]; // Construtor public Agenda() pessoas = null; // Metodos /** inserir */ public void inserir(pessoa p) if (pessoas == null) pessoas = new Pessoa[1]; else AumentaCapacidade(); pessoas[pessoas.length-1] = p;
129 /** Consultar */ public Pessoa getpessoa(string nome) Pessoa aux = null; for (int i=0; i< pessoas.length; i++) if (pessoas[i].getnome().equals(nome)) aux = pessoas[i]; return aux; 128 /** listar */ public Pessoa[] getpessoas()return pessoas; /** AumentaCapacidade */ private void AumentaCapacidade () Pessoa aux[] = new Pessoa[pessoas.length+1]; for (int i=0;i< pessoas.length;i++) aux[i] = pessoas[i]; pessoas = aux; De modo a manter o programa com poucas linhas não inserimos código para tratar erros para testar os valores que são passados para os métodos ou retornados por eles. O leitor deve ter isto em mente se por acaso pretende utilizar trechos deste programa para aplicações profissionais. Existem várias formas de representar a associação entre duas classes. Escolhemos representar a agregação por meio de um array de objetos da classe Pessoa. Um outra forma, mais fácil de se trabalhar, será apresentada na seção xx. A variável que referencia o array é inicializada com null. Toda vez que é inserido um novo objeto o array precisa ser redimensionado. Isto é feito pelo método privado AumentaCapacidade(). Na verdade o método cria um novo array maior em uma unidade que o anterior. Esta não é uma solução muito eficiente. O melhor seria incrementar o array em várias unidades, de modo que o redimensionamento seria necessário em intervalos menores. Contudo, o código necessário para gerenciar a inserção seria bem mais complexo.
130 129 O método getpessoa() retorna um objeto com o dado nome igual ao passado ao método. Se não existir tal objeto é retornado null. Já o método getpessoas() retorna uma referencia para array de objetos do tipo Pessoa. /** */ AgendaInt Interface console da agenda. import java.io.*; public class AgendaInt Agenda ag; BufferedReader in; // Construtor public AgendaInt() ag = new Agenda(); in = new BufferedReader(new InputStreamReader(System.in)); // Metodos /** Exibirlista */ public void Exibirlista() Pessoa p[]=ag.getpessoas(); for (int i= 0; i<p.length; i++) System.out.println("\nNome:"+p[i].getNome()+"\nTelef one:" +p[i].gettel()+"\nendereço:"+p[i].getend()+"\n"); /** exibirpessoa */ public void exibirpessoa() String nome=null; try System.out.println("Entre com o nome:"); nome = in.readline();
131 if (nome.length()<1) System.exit(-1); catch(ioexception e) System.out.println(e.getMessage());System.exit(-1); Pessoa p = ag.getpessoa(nome); if (p!=null) System.out.println("\nNome:"+p.getNome()+"\nTelefone:" +p.gettel()+"\nendereço:"+p.getend()); /** obterpessoa */ public void obterpessoa() String nome; String tel; String end; 130 try System.out.println("Entre com o nome:"); nome = in.readline(); if (nome.length()<1) System.exit(-1); System.out.println("\nEntre com o Telefone:"); tel = in.readline(); System.out.println("\nEntre com o Endereço:"); end = in.readline(); ag.inserir(new Pessoa(nome,tel,end)); catch(ioexception e) System.out.println(e.getMessage());System.exit(-1); // main public static void main(string args[]) AgendaInt agint = new AgendaInt(); String opcao=""; ); for(;;) System.out.println( "\nagenda Tabajara\n***********************\n"); System.out.print( "Opcoes:\n(i)nserir\n(c)onsultar\n(l)istar\n(f)im=>" try opcao = agint.in.readline();
132 1); Java na Prática if (opcao.length()==0) continue; catch(ioexception e) System.out.println(e.getMessage());System.exit(- 131 switch(opcao.charat(0)) case 'f': System.exit(0); break; case 'i': agint.obterpessoa(); break; case 'c': agint.exibirpessoa(); break; case 'l': agint.exibirlista(); break; O método main() da classe AgendaInt cria um objeto da própria classe, que por sua vez possui um objeto da classe Agenda. A partir de então o programa entra em um laço, que aguarda e atende às solicitações do usuário. Todos os métodos da classe recebem e exibem dados por meio dos dispositivos de E/S padrão. Para ler os dados do dispositivo de entrada padrão, uma linha por vez, foi necessário encapsular o objeto System.in em objetos das classes BufferedReader e InputStreamReader. Não detalharemos aqui o uso destas classes, que será abordado no próximo capítulo. No momento basta observarmos que a entrada de dados será realizada por meio do método readline(), que retorna a linha digitada pelo usuário.
133 132 Capítulo V Entrada e Saída (java.io) Acesso Sequencial As funções de entrada e saída são suportadas por classes agrupadas no pacote java.io. Todas se baseiam no conceito de stream de bytes (corrente ou sequência), onde a entrada/saída é um duto onde se retira/coloca cada byte como em uma fila do tipo First in-first out. A figura V.1 ilustra este tipo de enfoque. Figura V.1 Stream de bytes. O produtor e o consumidor podem se selecionados de uma ampla variedade que vai desde arquivos e portas de protocolos TCP/IP até arranjos (arrays) de bytes e Strings. De modo a pode lidar com essa ampla variedade de fontes/consumidores de dados e prover todo o tipo de facilidade de leitura o pacote java.io possui um conjunto razoavelmente grande classes. Essas classes são combinadas, formando camadas, onde as classes das camadas inferiores fornecem serviços básicos como leitura/escrita de um byte, enquanto que as classes superiores fornecem serviços de leitura/escrita mais sofisticados, como leitura de linha, leitura de um tipo float, etc. A figura V.2 mostra este tipo de combinação. Camada n Camada 1 Classe I/O Básica Nível dos serviços
134 Figura V.2 Organização das classes de I/O. 133 Para complicar um pouco mais a situação, a linguagem Java Possui dois conjuntos distintos de classes para I/O. Um originado da versão 1.0 e outro introduzido na versão 1.1. A razão para isso é que as classes projetadas na versão 1.0 são orientadas para bytes e portanto não são adequadas para lidar códigos Unicodes que utilizam dois bytes. A versão 1.2 não introduziu grandes modificações, realizando apenas um aperfeiçoamento na java.io.file e algumas mudanças na assinatura de alguns métodos. O leitor deve então estar pensando que basta então utilizar o novo conjunto de classes e esquecer do antigo, porém a coisa não é tão simples assim. Primeiramente, é importante conhecer o conjunto antigo de classes uma vez que existe um grande número de programas escrito na versão 1.0 e o leitor pode se ver obrigado a ler ou dar manutenção no código destes programas. Em segundo lugar, algumas vezes será preciso combinar as funções antigas com as novas para obter algum tipo de funcionalidade. E em terceiro lugar a Sun adicionou novas facilidades ao conjunto antigo na versão 1.1, dando sinais que não pretende simplesmente abandoná-lo. Tudo isto faz com que o tratamento de entrada e saída em Java não seja algo muito simples de se dominar. As figuras V.3 e V.4 apresentam diagramas que mostram algumas das camadas dos dois conjuntos de classes, de modo que o leitor possa ter uma idéia da sua equivalência, embora nem sempre uma classe de um conjunto possua uma classe correspondente em outro conjunto. ints FilterInputStream DataInputStream BufferedInputStream LineNumberInputStream StringBufferInputStream ByteArrayInputStream PipedInputStream FileInputStream InputStream floatsetc. String bytes FilterReader BufferedReader LineNumberReader PipedReader CharArrayReader StringReader FileReader Reader ints floatsetc. String caractere Figura V.3 Comparação entre as classes de entrada.
135 134 ints FilterOutputStream PrintStream DataInputStream BufferedOutputStream floatsetc. Strings FilterWriter BufferedWriter PrintWriter ints floatsetc. Strings ByteArrayOutputStream PipedOutputStream FileOutputStream OutputStream bytes PipedWriter CharArrayWriter StringWriter FileWriter Writer caractere Figura V.4 Comparação entre as classes de saída. Note que não existe uma equivalência perfeita entre os dois conjuntos, nem entre o par entrada/saída em um mesmo conjunto. Por exemplo, a classe StringBufferedInputStream não possui uma classe equivalente StringBufferedOutputStream nem a DataInputStream possui uma equivalente DataWriter. Esta é uma das razões para o uso combinado dos dois conjuntos. Para realizar a conversão entre os dois conjuntos são fornecidas as classes InputStreamReader e OutputStreamWriter. As funções básicas são tratadas pelas classes abstratas básicas InputStream e OutputStream. Para as funções de entrada e saída com Buffers são utilizadas as classes BufferedInputStream, BufferedOutputStream, BufferedReader e BufferedWriter. O tamanho padrão do buffer é de 2048 bytes. A tradução entre InputStream, OutputStream e BufferedReader, BufferedWriter é feita por InputStreamReader e OutputStreamWriter. Para IO em arquivos podem ser utilizadas classes de baixo nível: FileInputStream, FileOutputStream, FileReader e FileWriter. De modo a possibilitar a comparação e o entendimento do uso dos dois conjuntos os exemplos abaixo mostram a mesma função implementada com cada grupo de classes. a) Streams b) I/O 1.1 import java.io.*; import java.io.*; class TesteIO101 public static void main(string a[]) class TesteIO111 public static void main(string a[])
136 135 try DataInputStream in = new DataInputStream( new BufferedInputStream( new FileInputStream(a[0]))); String str; while((str = in.readline())!= null) System.out.println(str); catch(ioexception e) System.out.println(e.getMessage()); try BufferedReader in = new BufferedReader( new FileReader(a[0])); String str; while((str = in.readline())!= null) System.out.println(str); catch(ioexception e) System.out.println(e.getMessage()); Exemplo V.1 Leitura não formatada de arquivos. O exemplo V.1 mostra a leitura de um arquivo passado pela linha de comando feita (a) por meio de stream e (b) por meio de reader. Em ambos os casos a leitura é utiliza buffers, de modo a otimizar os acessos a disco. No caso (b) basta utilizar um objeto da classe BufferedReader, uma vez que a leitura é não formatada, com o uso do método readline(). Já no caso (a), é preciso envolver o objeto da classe BufferedInputStream em um objeto da classe DataInputStream, apesar de não ser uma leitura formatada, uma vez que o método readline() pertence à classe DataInputStream. Mesmo assim o programa receberá a mensagem The method java.lang.string readline() in class java.io.datainputstream has been deprecated. significando que o método readline() está sendo descontinuado para a classe DataInputStream, tendendo a desaparecer nas próximas versões. a) Streams b) I/O 1.1 import java.io.*; import java.io.*; class TesteIO102 public static void main(string a[]) try PrintStream out = new PrintStream( new BufferedOutputStream( new FileOutputStream("saida.out"))); out.println("linha de teste 1"); out.println("linha de teste 2"); out.close(); class TesteIO112 public static void main(string a[]) try PrintWriter out = new PrintWriter( new BufferedWriter( new FileWriter("saida.out"))); out.println("linha de teste 1"); out.println("linha de teste 2"); out.close();
137 catch(ioexception e) System.out.println(e.getMessage()); Java na Prática Exemplo V.2 Escrita não formatada em arquivos. 136 catch(ioexception e) System.out.println(e.getMessage()); a) Streams import java.io.*; class TesteIO103 public static void main(string a[]) try DataOutputStream out = new DataOutputStream( new BufferedOutputStream( new FileOutputStream("a.out"))); out.writebytes("o valor de pi: \n"); out.writedouble( ); out.close(); DataInputStream in = new DataInputStream( new FileInputStream("a.out")); System.out.println(in.readLine()); System.out.println(in.readDouble()); catch(ioexception e) System.out.println(e.getMessage()); Exemplo V.3 Escrita e leitura formatada em arquivos.. A leitura/escrita formatada utiliza as classes DataInputStream e DataOutputStream. Como não existem as correspondentes DataReader e DataWriter, somos obrigados a usar o conjunto antigo de classes para realizar a leitura e escrita formatada. Os dispositivos de entrada e saída padrões (equivalentes na linguagem C++ a cin, cout e cerr), são mantidos pela por variáveis estáticas da classe System, referenciadas por System.in, System.out e System.error. Como essas variáveis referenciam a objetos das classes InputStream e OutputStream, se desejarmos manipular os dispositivos de E/S padrões com o conjunto de classes novos devemos utilizar as classes tradutoras InputStreamReader e OutputStreamWriter, como mostrado no exemplo V.4. import java.io.*;
138 137 public class TesteIO114 public static void main(string a[]) try BufferedReader in = new BufferedReader( new InputStreamReader(System.in)); System.out.print("Entre uma linha:"); System.out.println(in.readLine()); catch(ioexception e) System.out.println(e.getMessage()); Exemplo V.4 Leitura e Escrita nos dispositivos de saída e entrada padrões. Acesso Direto Até agora lidamos com dispositivos de entrada e saída que tratam sequência de bytes ou caracteres (streams). No entanto, alguns dispositivos como disco rígido e CDs permitem o acesso direto à um byte sem a necessidade de ler todos os outros bytes posicionados antes. Este tipo de acesso é chamado de acesso direto e Java permite este tipo acesso a arquivos armazenados em dispositivos que suportam o acesso direto por meio da classe RandomAccessFile. Esta classes não pertence a nenhuma das hierarquias apresentadas anteriormente uma vez que não acessa os dados na forma de sequência. A única semelhança com as outras hierarquias é o fato de implementar as interfaces InputStream e OutputStream. De fato, a classe RandomAccessFile torna muito conveniente o acesso a arquivos por implementar simultaneamente as duas interfaces, permitindo desta forma tanto a leitura quanto a escrita no arquivo. Os construtores da classe são os seguintes: public RandomAccessFile(File file, String mode) throws IOException public RandomAccessFile(String name, String mode) throws IOException O primeiro construtor recebe um objeto da classe File e um objeto da classe String especificando o modo de acesso do arquivo. O acesso direto será
139 feito sobre o objeto da classe Files. Os modos acesso possíveis são: modo de leitura, especificado pela String r e modo de leitura e escrita, especificado pela String rw. O segundo construtor recebe um objeto da classe String contendo o nome do arquivo e outro objeto da classe String especificando o modo de acesso do arquivo. Ambos os construtores lançam a exceção IOException. A maioria dos métodos implementados por RandomAccessFile são implementados pela classe FileInputStream ou pela classe FileOutputStream, uma vez que ela lida tanto com leitura como saída, porém, a classe RandomAccessFile possui alguns métodos adicionais que permitem definir a posição corrente no arquivo e estabelecer onde será lido/escrito o próximo dado. A tabela V.1 mostra os métodos da classe RandomAccessFile. Método Descrição close() Fecha o arquivo. getfd() Retorna o descritor arquivo objeto associado com este stream. getfilepointer() Retorna a posição corrente neste arquivo. length() Retorna o tamanho deste arquivo. read(byte[] b, int Lê até len bytes em um array de bytes. off, int len) read() read(byte[] b) readboolean() readbyte() readchar() readdouble() readfloat() readfully(byte[] b, int off, int len) readfully(byte[] b) readint() readline() readlong() readshort() readunsignedbyte() readunsignedshort() readutf() seek(long pos) Lê um byte. Lê até to b.length bytes em um array de bytes. Lê um boolean. Lê um byte. Lê um caracter Unicode. Lê um double. Lê um float. Lê exatamente len bytes no array byte. Lê b.length bytes no array byte. Lê um inteiro de 32-bit com sinal. Lê a próxima linha de texto. Lê um inteiro com sinal de 64-bits. Lê um inteiro com sinal de 16-bits. Lê um inteiro sem sinal de 8-bits. Lê um inteiro sem sinal de 16-bits. Lê um String usando a codificação UTF-8. Define pos como a posição em bytes no arquivo 138
140 skipbytes(int n) write(int b) write(byte[] b, int off, int len) write(byte[] b) writeboolean(boolean v) writebyte(int v) writebytes(string s) writechar(int v) writechars(string s) writedouble(double v) writefloat(float v) writeint(int v) writelong(long v) writeshort(int v) writeutf(string str) Java na Prática 139 onde será feita a próxima leitura/escrita. Salta exatamente n bytes. Escreve um byte. Escreve len bytes do array iniciando em off. Escreve b.length bytes do array. Escreve um boolean como um valor 1-byte. Escreve um byte como um valor 1-byte. Escreve uma String como uma sequência de bytes. Escreve um caractere como um valor 2-byte, byte alto primeiro. Escreve uma String como uma sequência de caracteres. Escreve um double como um valor de 8-bytes. Escreve um float como um valor de 4-bytes. Escreve um inteiro como quatro bytes. Escreve um long como oito bytes. Escreve um short como dois bytes. Escreve uma String usando a codificação UTF-8. Tabela V.1 Métodos públicos da classe RandomAccessFile. O Exemplo V.5 mostra o uso da classe RandomAccessFile para acessar o byte no meio do arquivo. import java.io.*; class RandomTeste public static void main (String args[]) if (args.length ==0) System.err.println("Forneça o nome do arquivo!"); System.exit(0); try RandomAccessFile f = new RandomAccessFile(args[0], "r"); long tam = f.length(); if (tam==0)
141 Java na Prática System.out.println("Arquivo vazio!"); else f.seek(tam>>1); System.out.println("O byte no meio é: "+f.read()); catch (Exception e) System.out.println("Erro: " + e.tostring()); Exemplo V.1 Uso da classe RandomAccessFile. 140
142 141 Capítulo VI java.util O pacote java.util reúne um conjunto interessante de classes e interfaces úteis ao desenvolvimento de aplicações. Contém recursos para lidar com coleções, data e hora, internacionalização, arrays de bits, tokenização de cadeias de caracteres e etc. Nesta seção abordaremos as classes e interfaces mais utilizadas. Lidando com Coleções Antigamente, era muito comum que, durante o desenvolvimento de um sistema, os programadores tivessem a necessidade de implementar uma estrutura de dados como uma lista, pilha ou tabela hash para agrupar e gerenciar um conjunto de elementos. Como as primeiras linguagens de programação não possuíam recursos para que se implementasse estas estruturas de forma genérica, de modo que pudessem ser usadas independentemente do tipo dos elementos, o programador era obrigado a implementar deste o início toda a estrutura para cada novo tipo de elemento. Com as linguagens de programação mais recentes este quadro mudou. As novas linguagens permitem que o programador implemente estruturas de dados genéricas, independentes de tipos. Além disso, a maioria delas já implementam, embutidas na linguagem ou na forma de bibliotecas, as estruturas de dados mais comuns. Este é o caso da linguagem Java que possui, dentro do pacote java.util classes e interfaces que implementam estruturas de dados como listas (List), arrays crescentes (Vector), pilhas (Stack), tabela hash (Hashtable) e etc. O pacote possui também interfaces para percorrer os elementos das estruturas (Iterator e Enumeration). Apresentaremos aqui apenas as classes e interfaces mais usadas. As Interfaces Iterator e Enumeration As interfaces Iterator e Enumeration são usadas na criação de objetos que tem por objetivo percorrer sequencialmente os elementos de uma coleção. Como ambas as interfaces possuem o mesmo objetivo o leitor deve
143 estar se perguntando sobre necessidade de existirem duas interfaces. O fato é que até a versão 1.1 da linguagem Java existia apenas a interface Enumeration. No entanto, como o nome deste tipo de objeto na comunidade de Engenharia de Software é iterator a Sun aproveitou para incluir uma interface com este nome na versão 1.2 e fazer algumas pequenas modificações. O métodos que devem ser implementados em cada interface são semelhantes e estão descritos nas tabelas VI.1 e VI.2. Método Descrição boolean hasmoreelements() Object nextelement() Testa se Enumeration possui mais elementos. Retorna o próximo elemento da Enumeration. Tabela VI.1 Métodos da interface Enumeration. 142 Método Descrição boolean hasnext() Object next() void remove() Testa se Iterator possui um próximo elemento. Retorna o próximo elemento do Iterator. Remove da coleção associada o último elemento retornado pelo Iterator. Tabela VI.2 Métodos da interface Iterator. Um objeto que implementa a interface Enumeration é retornado por alguma classe que implementa uma estrutura de dados sempre que invocamos o método elements() da classe. Por exemplo, suponha que desejamos imprimir os elementos reunidos por um objeto v da classe Vector. O trecho de código para cumprir essa tarefa poderia ter a seguinte forma: for (Enumeration e = v.elements() ; e.hasmoreelements() ;) System.out.println(e.nextElement()); Já no caso da interface é preciso usar o método iterator() herdado por toda subclasse da interface Collection. Neste caso o trecho de código para cumprir essa tarefa poderia ter a seguinte forma: for (Iterator it = v.iterator() ; it.hasnext() ;) System.out.println(it.next());
144 Java na Prática 143 Vector public class Vector extends AbstractList implements List, Cloneable, Serializable A classe Vector implementa um array dinâmico. Ou seja, um array que cresce ou diminui a medida da necessidade. O uso de objetos classe Vector é indicado para situações onde o programador precisa manter uma lista de elementos mas não sabe, a priori, o número de elementos que pertencerão à lista ou o número de elementos da lista irá variar durante o processamento. Hierarquia java.lang.object +--java.util.abstractcollection +--java.util.abstractlist +--java.util.vector Construtores Construtor Descrição public Vector() public Vector(Collection c) public Vector(int inicapac) public Vector(int inicapac, int inc) Constrói um Vector vazio com espaço inicial para 10 elementos e incremento 0. Constrói um Vector contendo os elementos do objeto do tipo Collection. Constrói um Vector vazio com espaço inicial para inicapac elementos e incremento 0. Constrói um Vector vazio com espaço inicial para inicapac elementos e incremento inc. Tabela VI.3 Construtores da classe Vector.
145 Atributos Java na Prática 144 Atributo Descrição protected int capacityincrement Quantidade que é incrementada à capacidade do Vector toda vez que o protected int elementcount protected Object[] elementdata tamanho se torna maior que a capacidade. Número de elementos referenciados Array onde são armazenadas as referencias aos elementos. Tabela VI.4 Atributos públicos da classe Vector. Métodos Devido ao grande número de métodos da classe Vector mostraremos apenas os mais usados. Método Descrição void add(int i, Object o) Adiciona um elemento na posição indicada por i. public boolean add(object Adiciona um elemento no final do Vector. o) public boolean Adiciona todos os elementos da coleção especificada addall (Collection c) no final do Vector. public void Adiciona um elemento no final do Vector. addelement(object o) public int capacity() Retorna a capacidade atual. public void clear() Remove todos os elementos do Vector. public Object clone() Retorna um clone do Vector. public boolean Testa se o objeto é um elemento do Vector. contains(object o) public boolean Retorna true se o Vector contém todos os containsall(collection c) elementos referenciados por c. public void Copia os elementos do Vector no array. copyinto (Object[] a) public Object Retorna o elemento na posição i. elementat(int i) public Enumeration Retorna um objeto do tipo Enumeration que elements() permite percorrer os elementos do Vector. public void Aumenta a capacidade do Vector para o valor min. ensurecapacity(int min) public boolean Compara se o é igual ao Vector. Retorna true equals(object o) somente se o objeto é também do tipo List, possui o mesmo tamanho e referencia os mesmos elementos na mesma ordem.
146 public Object Retorna o primeiro elemento. firstelement() public Object get(int i) Retorna o elemento da posição i. public int Retorna a posição da primeira ocorrência de o. O indexof(object o) método equals() do objeto é usado para o teste de public int indexof(object o,int i) public void insertelementat(object o, int i) public boolean isempty() public Object lastelement() public int lastindexof(object o) public Object remove(int i) public boolean removeall(collection c) public void removeallelements() public void removeelementat(int i) public boolean retainall(collection c) public Object set(int i, Object o) public void setsize(int s) public int size() public List sublist(int i, int f) public Object[] toarray() public String tostring() Exemplo 145 igualdade. Retorna a posição da primeira ocorrência de o a partir de i. O método equals() do objeto é usado para o teste de igualdade. Insere o objeto o na posição i. Testa se o Vector está vazio. Retorna o último elemento. Retorna a posição da última ocorrência de o. O método equals() do objeto é usado para o teste de igualdade. Remove o elemento da posição i. Remove todos os elementos que também pertencem à Collection. Remove todos os elementos. Remove o elemento da posição i. Mantém apenas os elementos que também pertencem à Collection. Substitui o objeto na posição i pelo objeto o. Define o tamanho do Vector. Retorna o tamanho do Vector. Retorna uma parte dos elementos do Vector entre fromindex, inclusive, e toindex, exclusive. Retorna um array contendo todos os elementos no Vector na ordem correta. Retorna uma representação na forma de String do Vector. Tabela VI.5 Métodos da classe Vector.
147 O exemplo a seguir mostra a inserção das strings passadas pela linha de comando em um objeto da classe Vector. Os elementos do objeto Vector podem ser exibidos na tela invocando o método print(). import java.util.*; public class TesteVector Vector v; public TesteVector(String a[]) v = new Vector(a.length); v.copyinto(a); public void print() for (Iterator it = v.iterator(); it.hasnext() ;) System.out.println((String)it.next()); 146 public static void main(string args[]) TesteVector teste = new TesteVector(args); teste.print(); Exemplo VI.xx Uso do Vector. Stack public class Stack extends Vector A classe Stack implementa a estrutura de dados pilha. Ou seja, uma estrutura de dados que segue a regra LIFO (last-in-first-out último a entrar, primeiro a sair). Esta regra impõe define uma lista que é acessada apenas por uma extremidade, de modo que o último elemento a ser inserido na pilha tenha que ser o primeiro elemento a sair.
148 147 Hierarquia java.lang.object +--java.util.abstractcollection +--java.util.abstractlist +--java.util.vector +--java.util.stack Construtor Construtor Descrição public Stack() Constrói um Stack vazio. Tabela VI.6 Construtor da classe Stack. Métodos Método Descrição public boolean empty() public Object peek() public Object pop() public Object push(object o) public int search(object o) Testa se a Stack está vazia. Retorna uma referência para o elemento no topo da Stack sem retirá-lo. Retorna uma referência para o elemento no topo da Stack e o retira. Insere o objeto o no topo da Stack. Retorna a distância do topo da pilha da primeira ocorrência de o. O elemento no topo da pilha possui a distância 1. O método equals() do objeto é usado para o teste de igualdade. Tabela VI.5 Métodos da classe Stack. Exemplo
149 O exemplo a seguir mostra a inserção das strings passadas pela linha de comando em um objeto da classe Stack. Os elementos do objeto Stack podem ser exibidos na tela invocando o método print(). import java.util.*; public class TesteStack Stack s; public TesteStack(String a[]) s = new Stack(); for (int i=0; i<a.length; i++) s.push(a); public void desempilha () String aux = (String)s.pop(); while (aux!=null) System.out.println(aux); 148 public static void main(string args[]) TesteStack teste = new TesteStack(args); teste.desempilha(); Exemplo VI.xx Uso do Stack. Hashtable public class Hashtable extends Dictionary implements Map, Cloneable, Serializable A classe Hashtable implementa uma estrutura de dados conhecida como tabela hash. Neste tipo de estrutura todo elemento armazenado possui uma
150 chave que permite recuperar diretamente o elemento, sem a necessidade de ir examinando cada elemento armazenado até encontrar o elemento desejado, como seria o caso em uma lista. Cada chave identifica apenas um elemento. Existem vários bons livros sobre estruturas de dados nas livrarias que o leitor pode consultar para se aprofundar no tema. 149 Hierarquia java.lang.object +--java.util.dictionary +--java.util.hashtable Construtores Construtor Descrição public Hashtable() public Hashtable(int capac) public Hashtable(int capac, float fatcarga) public Hashtable(Map m) Constrói um Hashtable vazio com fator de carga 0,75. Constrói um Hashtable com capacidade inicial capac e com fator de carga 0,75. Constrói um Hashtable com capacidade inicial capac e com fator de carga fatcarga. Constrói um Hashtable com os elementos de m. Tabela VI.xx Construtores da classe Hashtable. Métodos Devido ao grande número de métodos da classe Hashtable mostraremos apenas os mais usados. Método Descrição public void clear() public Object clone() public boolean contains(object o) public boolean containskey(object o) Remove todos os elementos do Hashtable. Retorna um clone raso do Hashtable. Testa se o objeto é acessado por alguma chave do Hashtable. Testa se o objeto é chave no Hashtable.
151 public boolean containsvalue(object o) Testa se o objeto é acessado por alguma chave do Hashtable. public Enumeration elements() Retorna um objeto do tipo Enumeration que permite percorrer os elementos do Hashtable. public Set entryset() Retorna um Set contendo os elementos do Hashtable. public boolean equals(object o) Compara se o é igual ao Hashtable. Retorna true somente se o objeto é também do tipo Map e faz o mesmo mapeamento. public Object get(object key) Retorna o elemento da posição mapeado pela chave key. public boolean isempty() Testa se o Hashtable está vazio. public Enumeration keys() Retorna um objeto do tipo Enumeration que permite percorrer as chaves do Hashtable. public Set keyset() Retorna um Set contendo as chaves do Hashtable. public Object put(object key,object o) Insere o objeto o no Hashtable tendo como chave key. protected void rehash() Reorganiza internamente o Hashtable para acessar e acomodar os elementos mais eficientemente. public Object Remove o elemento que tem como chave key. remove(object key) public int size() Retorna o número de chaves do Hashtable. public String tostring() Retorna uma representação na forma de String do public Collection values() Hashtable. Retorna um Collection contendo os elementos do Hashtable. Tabela VI.xx Métodos da classe Hashtable. 150 Exemplos O exemplo a seguir mostra a inserção das objetos da classe Integer em um objeto Hashtable. A chave usada para cada objeto é o número por extenso. Hashtable numbers = new Hashtable(); numbers.put("one", new Integer(1)); numbers.put("two", new Integer(2)); numbers.put("three", new Integer(3));... Integer n = (Integer)numbers.get("two"); if (n!= null) System.out.println("two = " + n);
152 ... for (Enumeration e = numbers.elements();e.hasmoreelements();) Integer i = (Integer)e.nextElement() System.out.println( i.tostring()); Exemplo VI.xx Uso do Hashtable. 151 O exemplo a seguir mostra a inserção das objetos da classe Pessoa em um objeto Hashtable. A chave usada para cada objeto é o nome da pessoa. class pessoa String nome; String tel; public pessoa(string nome, String tel) this.nome = nome; this.tel = tel; public String getnome()return nome; public String gettel()return tel;... Hashtable pessoas = new Hashtable();... pessoa P1 = new pessoa("pedro", " "); pessoas.put(p1.getnome(), P1);... pessoa P2 =(pessoa) pessoas.get("pedro"); if (P2!=null) System.out.println ("Nome:"+P2.getNome(), "\ntel:"+p2.gettel()); Exemplo VI.xx Uso do Hashtable. Miscelânea de Classes do pacote java.util Arrays public class Arrays extends Object
153 Esta classe foi inserida no pacote java.util a partir da versão 1.2 do SDK. É uma classe utilitária, fornecendo métodos de ordenação e busca em arrays. 152 Hierarquia java.lang.object +--java.util.arrays Métodos Método Descrição public static List aslist(object[] a) public static int binarysearch(byte[] a, byte key) public static int binarysearch(char[] a, char key) public static int binarysearch(double[] a, double key) public static int binarysearch(float[] a, float key) public static int binarysearch(int[] a, int key) public static int binarysearch(long[] a, long key) public static int binarysearch(object[] a, Object key) public static int binarysearch(short[] a, short key) public static int binarysearch(object[] a, Object key, Comparator c) public static boolean equals(boolean[] a, boolean[] a2) public static boolean equals(byte[] a, byte[] a2) public static boolean equals(char[] a, char[] a2) public static boolean equals(double[] a, double[] a2) public static boolean equals(float[] a, float[] a2) public static boolean equals(int[] a, int[] a2) public static boolean equals(long[] a, long[] a2) public static boolean equals(object[] a, Object[] a2) Retorna uma List criada a partir do array. Realiza uma pesquisa binária retornando a posição de key no array. Realiza uma pesquisa binária retornando a posição de key no array. O array tem que estar ordenado ascendentemente de acordo com o Comparator. Retorna true se os dois arrays são iguais.
154 153 public static boolean equals(short[] a, short[] a2) public static void fill(boolean[] a, boolean val) public static void fill(byte[] a, byte val) public static void fill(double[] a, double val) public static void fill(float[] a, float val) public static void fill(int[] a, int val) public static void fill(long[] a, long val) public static void fill(object[] a, Object val) public static void fill(short[] a, short val) public static void fill(boolean[] a, int ini, int fim, boolean val) public static void fill(byte[] a, int ini, int fim, byte val) public static void fill(double[] a, int ini, int fim, double val) public static void fill(float[] a, int ini, int fim, float val) public static void fill(int[] a, int ini, int fim, int val) public static void fill(long[] a, int ini, int fim, long val) public static void fill(object[] a, int ini, int fim, Object val) public static void fill(short[] a, int ini, int fim, short val) public static void sort(byte[] a) public static void sort(double[] a) public static void sort(float[] a) public static void sort(int[] a) public static void sort(long[] a) public static void sort(object[] a) public static void sort(short[] a) public static void sort(byte[] a, int ini, int fim) public static void sort(double[] a, int ini, int fim) public static void sort(float[] a, int ini, int fim) public static void sort(int[] a, int ini, int fim) public static void sort(long[] a, int ini, int fim) public static void sort(object[] a, int ini, int fim) public static void sort(short[] a, int ini, int fim) public static void sort(object[] a, Comparator c) public static void sort(object[] a, int ini, int fim, Comparator c) Preenche o array com o valor especificado. Preenche o array com o valor especificado a partir do elemento de índice ini inclusive até o elemento de índice fim exclusive. Ordena o array em ordem ascendente. Ordena o array em ordem ascendente a partir do elemento de índice ini inclusive até o elemento de índice fim exclusive. Ordena o array em ordem ascendente definida pelo objeto Comparator. Ordena o array em ordem ascendente definida pelo objeto Comparator a partir do elemento de índice ini inclusive
155 154 até o elemento de índice fim exclusive. Tabela VI.5 Métodos da classe Arrays. O algoritmo de ordenação O algoritmo de ordenação é uma variação quicksort apresentado por Jon L. Bentley e M. Douglas McIlroy's "Engineering a Sort Function", Software-Practice and Experience, Vol. 23(11) P (November 1993). Este algoritmo possui complexidade n*log(n) em conjunto de dados que causariam a degradação de outros algoritmos de quicksort para um desempenho de ordem quadrática. Exemplo O exemplo a seguir mostra a impressão ordenada das strings passadas pela linha de comando. import java.util.arrays; class TestArrays public static void main(string a[]) if(a.length>0) Arrays.sort(a); for(int i=0;i<a.length; i++) System.out.println(a[i]); Exemplo VI.xx Uso do Arrays. Date public class Date extends Object implements Serializable, Cloneable, Comparable
156 Esta classe é responsável pela representação da data com precisão de milisegundos. Antes da versão 1.1 do SDK a classe Date também tinha as funções de formatação e análise de datas no tipo String assim como de interpretar as datas em anos, meses, dias, horas, minutos e segundos. Devido à complexidade da internacionalização estas funções foram transferidas para as classes DateFormat e Calendar. O métodos da classe Date que realizavam essas funções tornaram-se deprecated. A data base padrão, a partir da qual são contados os milisegundos é 01/01/1970, 00:00:00 GMT. 155 Hierarquia java.lang.object +--java.util.date Construtores Os construtores deprecated não estão listados. Construtor Descrição public Date() public Date(long data) Constrói um Date representando a data do momento de sua alocação. Constrói um Date contendo a data calculada a partir número de millisegundos representados por data tendo como base a data 01/01/1970, 00:00:00 GMT. Tabela VI.xx Construtores da classe Date. Métodos Os métodos deprecated não estão listados. Método Descrição public boolean after(date d) public boolean before(date d) public Object clone() public int compareto(date d) Testa se a data é anterior à data passada como argumento. Testa se a data é posterior à data passada como argumento. Cria um clone do objeto. Compara duas datas. Retorna 0 se as duas
157 156 public boolean equals(object o) public long gettime() public void settime(long time) datas são iguais. Retorna um valor maior que 0 se a data passada como argumento é menor e um valor menor que 0 se data passada como argumento e maior. Retorna true se duas datas são iguais. Retorna o número de milisegundos desde 01/01/1970, 00:00:00 GMT até a data representada pelo objeto corrente. Define a data desde objeto usando o número de milisegundos desde 01/01/1970, 00:00:00 GMT passado como argumento. Tabela VI.5 Métodos da classe Date. Exemplo O exemplo a seguir exibe a representação da data corrente em milisegundos. import java.util.date; class TestDate public static void main(string a[]) System.out.println(new Date().getTime()); Exemplo VI.xx Uso da classe Date. Observable public class Observable extends Object A classe Observable permite criar objetos observáveis. Objeto observável é um objeto que, quando sofre alguma alteração, notifica outros objetos, chamados de observadores. Os objetos observáveis e observadores fazem parte do padrão de projeto conhecido como MVC (Modelo Visão Controle) introduzido na linguagem Smalltalk. A utilização deste padrão de
158 projeto facilita a implementação programas onde um modelo possui várias visões que devem ser atualizadas toda vez que o modelo muda. O controle atua sobre o modelo, ocasionando alterações. Por exemplo, suponha que modelo que armazene os valores de ações da bolsa. Podemos exibir várias visões desses dados, como gráficos de barras e em forma de pizza. Os controladores modificariam o modelo que em seguida notificaria as visões para refletirem o estado atual do modelo. As figuras VI.XX e VI.XX ilustram esquematicamente o padrão MVC. Na linguagem Java, para um objeto ser um observador é preciso implementar a interface Observer. 157 Visão Controle Modelo Visão Controle Figura VI.XX Esquema do padrão MVC. window X window X a b x a=30; b=45; a b Observável Figura VI.XX Duas formas de visualização de um modelo.
159 Hierarquia Java na Prática 158 java.lang.object +--java.util.observable Construtor Construtor Descrição public Observable() Constrói um Observable possuindo zero observadores. Métodos Tabela VI.XX Construtor da classe Observable. Método Descrição public void addobserver(observer o) protected void clearchanged() public int countobservers() public void deleteobserver(observer o) public void deleteobservers() public boolean haschanged() public void notifyobservers() public void notifyobservers(object arg) protected void setchanged() Adiciona um observador ao conjunto de observadores do objeto. Limpa a indicação de que o objeto mudou. Retorna o número de objetos observadores deste objeto. Remove o objeto passado como argumento do conjunto de objetos observadores. Remove todos os objetos observadores do conjunto de objetos observadores. Retorna true se o objeto sofreu uma mudança. Se o método haschanged() retornar true notifica todos os objetos observadores e chama o método clearchanged(). Se o método haschanged() retornar true notifica todos os objetos observadores e chama o método clearchanged(). O objeto arg é passado como argumento para o método update() do observador. Marca este objeto como tendo sofrido uma mudança. Tabela VI.XX Métodos da classe Observable.
160 Exemplo Java na Prática 159 O exemplo VI.xx mostra uma classe Observavel que muda de valor aleatoriamente. A classe implementa a interface Runnable, uma vez que será executada concorrentemente. A concorrência será abordada detalhadamente no Erro! A origem da referência não foi encontrada.. import java.util.*; public class Observavel extends Observable implements Runnable int valor =0; public void notifyobservers() setchanged(); super.notifyobservers(); public int getvalue()return valor; public void run() for(;;) if (Math.random()>0.5) valor =(new Double(Math.random()*200)).intValue(); this.notifyobservers(); try Thread.sleep(300); catch(exception e); Exemplo VI.xx Uma classe Observable. O exemplo a seguir mostra uma classe que define um observador. Para isso é necessário implementar a interface Observer, cujo único método a ser codificado é o método update(). Este método é chamado quando o objeto observado notifica alguma alteração. No exemplo o objeto observador simula uma barra de progresso que movimenta de acordo com a mudança do valor do objeto observado.
161 import java.awt.*; import java.util.*; Java na Prática 160 public class FrameOb extends Frame implements Observer Observavel ob = new Observavel(); Thread t = new Thread(ob); Canvas progress = new Canvas(); public FrameOb() setlayout(null); setsize(300,60); add(progress); progress.setbackground(color.blue); progress.setbounds(0,0,0,30); settitle("exemplo de Observável"); SymWindow asymwindow = new SymWindow(); this.addwindowlistener(asymwindow); ob.addobserver(this); t.start(); static public void main(string args[]) (new FrameOb()).setVisible(true); class SymWindow extends java.awt.event.windowadapter public void windowclosing(java.awt.event.windowevent event) System.exit(0); public void update(observable o, Object arg) progress.setsize(ob.getvalue(), 100); Exemplo VI.xx Uso da classe Observable. StringTokenizer
162 public class StringTokenizer extends Object implements Enumeration 161 A StringTokenizer é uma classe que permite quebrar uma cadeia de caracteres em subcadeias denominadas de tokens. um token é uma subcadeia cercada por caracteres denominados delimitadores. Portanto, o que é um token dependerá da especificação dos delimitadores. Por exemplo, dada cadeia de caracteres ola mundo,ola vida teremos os tokens ola mundo e ola vida caso o delimitador seja o caractere, e os tokens ola, mundo,ola e vida caso o delimitador seja o caractere Hierarquia java.lang.object +--java.util.stringtokenizer Construtores Construtor Descrição public Constrói um StringTokenizer para s. StringTokenizer(String s) public StringTokenizer(String s, String del) public StringTokenizer (String s, String del, boolean voltatokens) Constrói um StringTokenizer para s, usando como delimitadores os caracteres contidos em del. Constrói um StringTokenizer para s, usando como delimitadores os caracteres contidos em del. Se voltatokens possui o valor true, então os caracteres contidos em del também são retornados. Tabela VI.XX Construtores da classe StringTokenizer. Métodos Método Descrição
163 public int counttokens() public boolean hasmoreelements() public boolean hasmoretokens() public Object nextelement() public String nexttoken() public String nexttoken(string del) Java na Prática Retorna o número de tokens. Mesmo efeito que hasmoretokens(). 162 Retorna true se existir mais tokens. Retorna o próximo token como uma instância de Object. Retorna o próximo token como uma instância de String. Retorna o próximo token como uma instância de String usando como delimitadores os caracteres contidos em del. Tabela VI.XX Métodos da classe StringTokenizer. Exemplo import java.util.*; public class TestToken static public void main(string args[]) StringTokenizer st = new StringTokenizer("ola mundo louco"); while (st.hasmoretokens()) println(st.nexttoken()); Saída: ola mundo louco Exemplo VI.XX Uso da classe StringTokenizer. Agenda Eletrônica versão Console 2.0 Neste ponto já estamos preparados para fazer alguns aperfeiçoamentos na nossa agenda eletrônica de endereços. As alterações serão de dois tipos: passaremos a utilizar um objeto da classe Hashtable para agrupar as pessoas da agenda, e acrescentaremos métodos para gravar e recuperar os dados da
164 agenda em um arquivo. Estas modificações acarretarão mudanças apenas nos códigos das classes Agenda e AgendaInt. O diagrama de classes da figura IV.XX mostra as adições dos métodos gravaragenda() e recuperaragenda(), responsáveis pela gravação e recuperação dos dados em arquivo. 163 AgendaInt +obterpessoa() +exibirlista() +exibirpessoa() +gravaragenda() +recuperaragenda() Agenda +inserir(pessoa p) +getpessoas() +getpessoa(string Nome) 0..* Pessoa -String Nome -String Tel -String End +getnome() +gettel() +getend() Figura VI.xx Diagrama de classes da versão console 2.0 da agenda eletrônica. Como a classe Pessoa não sofrerá nenhuma modificação. Ela é apresentada abaixo apenas por questões de comodidade. /** */ Pessoa public class Pessoa private String Nome; private String Tel; private String End; // Construtor public Pessoa(String n, String t, String e) Nome = n; Tel = t; End = e; /** getnome */ public String getnome()return Nome; /** getnome
165 Java na Prática */ public String gettel()return Tel; /** getend */ public String getend()return End; 164 A classe Agenda será alterada de modo que o a associação de agrupamento com a classe Pessoa seja implementada por meio de um objeto da classe Hashtable e não por um array. /** AGENDA Versão Console 2.0. */ import java.util.*; public class Agenda Hashtable pessoas; // Construtor public Agenda() pessoas = new Hashtable(); /** inserir */ public void inserir(pessoa p) pessoas.put(p.getnome(),p); /** Consultar */ public Pessoa getpessoa(string nome) return (Pessoa) pessoas.get(nome); /** listar */ public Enumeration getpessoas()return pessoas.elements(); Podemos notar que o código fica bem mais simples com a adoção da instância da classe Hashtable. Foi alterada a assinatura do método getpessoas(), de modo que não mais retorna um array e sim um objeto da
166 classe Enumeration. A classe AgendaInt será alterada para tratar o retorno do método getpessoas() e receberá mais dois novos métodos. O método main() também será alterado para receber solicitações para a execução das novas funções. 165 /** */ AgendaInt Interface console da agenda. import java.io.*; import java.util.*; public class AgendaInt Agenda ag; BufferedReader in; // Construtor public AgendaInt() ag = new Agenda(); in = new BufferedReader(new InputStreamReader(System.in)); /** Exibirlista */ public void Exibirlista() Pessoa p; for(enumeration e = ag.getpessoas(); e.hasmoreelements();) p = (Pessoa) e.nextelement(); System.out.println("\nNome:"+p.getNome()+"\nTelefone:" +p.gettel()+"\nendereço:"+p.getend()+"\n"); /** exibirpessoa */ public void exibirpessoa() String nome=null; try System.out.println("Entre com o nome:");
167 Java na Prática nome = in.readline(); if (nome.length()<1) System.exit(-1); catch(ioexception e) System.out.println(e.getMessage());System.exit(-1); Pessoa p = ag.getpessoa(nome); if (p!=null) System.out.println("\nNome:"+p.getNome()+"\nTelefone:" +p.gettel()+"\nendereço:"+p.getend()); 166 /** obterpessoa */ public void obterpessoa() String nome; String tel; String end; try System.out.println("Entre com o nome:"); System.out.flush(); nome = in.readline(); if (nome.length()<1) System.exit(-1); System.out.println("\nEntre com o Telefone:"); System.out.flush(); tel = in.readline(); System.out.println("\nEntre com o Endereço:"); System.out.flush(); end = in.readline(); ag.inserir(new Pessoa(nome,tel,end)); catch(ioexception e) System.out.println(e.getMessage());System.exit(-1); /** gravar */ public void gravar() try Pessoa p; BufferedWriter fout = new BufferedWriter( new FileWriter("agenda.dat")); for (Enumeration e = ag.getpessoas(); e.hasmoreelements();)
168 Java na Prática p = (Pessoa) e.nextelement(); fout.write(p.getnome()+"\n"+p.gettel()+"\n" +p.getend()+"\n"); fout.flush(); fout.close(); catch(filenotfoundexception e) System.out.println("Arq. Não encontrado"); catch(ioexception e) System.out.println("Erro na gravação!"); 167 /** carregar */ public void carregar() try String nome; String tel; String end; BufferedReader fin = new BufferedReader( new FileReader("agenda.dat")); while ((nome = fin.readline())!= null) tel = fin.readline(); end = fin.readline(); ag.inserir(new Pessoa(nome,tel,end)); fin.close(); catch(filenotfoundexception e) System.out.println("Arq. Não encontrado"); catch(ioexception e) System.out.println("Erro na leitura!"); // main public static void main(string args[]) AgendaInt agint = new AgendaInt(); String opcao=""; for(;;) System.out.println(
169 "\nagenda Tabajara\n***********************\n"); System.out.print("Opcoes:\n(i)nserir\n(c)onsultar"+ "\n(l)istar\n(g)ravar\n(r)ecuperar\n(f)im=>"); System.out.flush(); try opcao = agint.in.readline(); if (opcao.length()==0) continue; catch(ioexception e) System.out.println(e.getMessage());System.exit(-1); 168 switch(opcao.charat(0)) case 'f': System.exit(0); break; case 'i': agint.obterpessoa(); break; case 'c': agint.exibirpessoa(); break; case 'l': agint.exibirlista(); break; case 'g': agint.gravar(); break; case 'r': agint.carregar(); break;
170 169 Capítulo VII - Serialização e Persistência Um objeto é serializável se podemos transformá-lo em uma sequência de bytes de modo que seja possível o seu armazenamento em arquivo ou envio através de um stream. Desta forma o estado do objeto pode ser preservado em memória não volátil. Neste caso dizemos que o objeto possui persistência, ou seja o estado do objeto persiste enter as execuções do programa. A persistência implementada pela serialização é chamada de persistência leve, uma vez que o programador deve providenciar o armazenamento. Em uma persistência completa o programador apenas informa ao sistema que o objeto é persistente, ficando a cargo do ambiente o armazenamento e recuperação do objeto de forma transparente. A serialização de objetos foi adicionada à linguagem Java a partir da versão 1.1 com o intuito de possibilitar a transmissão de objetos entre máquinas executando diferentes plataformas operacionais, viabilizando dessa forma o RMI (Remote Method Invocation) e permitir a utilização JavaBeans, que são componentes configuráveis em tempo de execução, e que mantém a configuração entre ativações. Para que os objetos de uma classe sejam serializáveis basta que a classe implemente a interface Serializable. A partir de então é possível armazenar e recuperar os objetos da classe por meio de instâncias das classes ObjectOutputStream e ObjectInputStream, respectivamente. O exemplo VII.1 mostra como armazenar e recuperar um objeto da classe Pessoa em um arquivo p.dat. class pessoa implements Serializable String Nome; String Tel; public pessoa(string Nome, String Tel) this.nome = Nome; this.tel = Tel;... Pessoa p = new pessoa( Ana, ); FileOutputStream fout = null; ObjectOutputStream out;
171 try fout = new FileOutputStream( p.dat"); out = new ObjectOutputStream(fout); out.writeobject(p); out.close(); catch(exception e) System.out.println( Erro:");return; FileInputStream fin = null; ObjectInputStream in; try fin = new FileInputStream( p.dat"); in = new ObjectInputStream(fin); p = (pessoa) in.readobject(); in.close(); catch(exception e) System.out.println( Erro: +e); return; 170 Exemplo VII.1 Armazenamento e recuperação de um objeto da classe Pessoa. Uma questão interessante é: o que acontece com os objetos que são referenciados pelo objeto que está sendo armazenado? Devem ser armazenados também ou não? Se os objetos referenciados não forem armazenados juntos com o objeto corrente, a recuperação do mesmo posteriormente poderá resultar em uma instância incompleta. Por exemplo, um objeto do tipo Pessoa como mostrado no exemplo VII.1, referencia dois objetos do tipo String que armazenam o nome e o telefone da pessoa representada pelo objeto. Se estes objetos não forem armazenados junto com a instância de Pessoa, qual o sentido da posterior recuperação do objeto? Portanto, quando um objeto é armazenado todos os objetos referenciados por ele também são armazenados, desde que os objetos implementem a interface Serializable. Caso o objeto referenciado também referencie outro objeto, este último também será armazenado, e assim por diante, até que toda os objetos da cadeia de referencias sejam armazenados. Isto pode fazer com que o objeto armazenado ocupe muito mais espaço do que o imaginado inicialmente pelo programador. Outra pergunta seria: o que acontece então quando um objeto é referenciado mais de uma vez ou quando acontece um ciclo no grafo de referências? Neste caso, o objeto é armazenado apenas uma vez.
172 Agenda Eletrônica versão Console Com o objetivo de exemplificar o que foi discutido neste capítulo, modificamos a agenda eletrônica para que a gravação e a carga do arquivo seja feita através da serialização do objeto Agenda. Primeiramente, foi modificada as classes Pessoa e Agenda de modo que implementassem a interface Serializable. Para isto basta importar a interface do pacote java.io e modificar a declaração das classes, como mostrado abaixo. Como o restante do código dessas duas classes permanece inalterado, não o repetimos aqui. O leitor deve se reportar às versões anteriores caso deseje recordar sobre o código omitido. /** Pessoa */ import java.io.*; public class Pessoa implements Serializable... Sem alteração... /** AGENDA Versão Console 2.1. */ import java.util.*; import java.io.*; public class Agenda implements Serializable... Sem alteração... A classe AgendaInt sofre modificações nos métodos gravar() e carregar(), que os tornam bem mais simples, uma vez que o trabalho de seguir as referências do objeto da classe Agenda para serializá-los e armazenálos é feito implicitamente. Segue abaixo apenas o código dos métodos modificados classe AgendaInt. Note que foi preciso usar a hierarquia stream
173 para gravar e recuperar os objetos, uma vez que não existem classes do tipo ObjectReader e ObjectWriter no novo conjunto de classes de E/S. 172 /** */ AgendaInt Interface console da agenda. import java.io.*; import java.util.*; public class AgendaInt... Sem alteração... /** gravar */ public void gravar() try ObjectOutputStream fout = new ObjectOutputStream( new FileOutputStream("agenda.dat")); fout.writeobject(ag); fout.close(); catch(filenotfoundexception e) System.out.println("Arq. Não encontrado"); catch(ioexception e) System.out.println("Erro na gravação!"); /** carregar */ public void carregar() try ObjectInputStream fin = new ObjectInputStream( new FileInputStream("agenda.dat")); ag = (Agenda) fin.readobject(); fin.close(); catch(filenotfoundexception e) System.out.println("Arq. Não encontrado"); catch(exception e) System.out.println("Erro na leitura!");
174 ... Sem alteração... Java na Prática 173
175 174 Capítulo VIII AWT (Abstract Window Toolkit) Até este ponto, todos o exemplos apresentados neste livro foram de aplicações em modo texto, também chamadas de aplicações não gráficas ou de console. Hoje em dia, não é possível lançar no mercado uma linguagem de ampla aplicação que não possua ferramentas para construção de interfaces gráficas sofisticadas. A linguagem Java além de ter a necessidade de atender este requisito possui a condição adicional de prover recursos gráficos que permitam o desenvolvimento de interfaces independentes de plataforma. Talvez, devido a esta forte exigência, a primeira versão do pacote que implementa o suporte gráfico acabou sendo conhecido como um excelente exemplo de como não se deve realizar um projeto. O pacote conhecido como AWT 1.0 (Abstract Window Toolkit), não era muito harmônico, tinha um conjunto muito pobre de componentes gráficos e possuía um modelo para tratamento de eventos muito ineficiente. A versão AWT 1.1 solucionou vários deste problemas, com o oferecimento de um novo modelo para tratamento dos eventos e com a introdução de um modelo para programação de componentes, denominados de JavaBeans. Na versão 1.2 da linguagem novas facilidades foram incorporadas com a adição de um conjuntos de componentes leves, denominados de Swing, ou JFC (Java Foundation Classes). A denominação de componentes leves para a JFC se deve ao fato dos componentes não utilizarem os componentes nativos da GUI (Graphical User Interface) do ambiente. Eles utilizam primitivas de baixo nível para sua construção na tela, obtendo assim um alto nível de portabilidade. Já a AWT utiliza os componentes da GUI (widgets), para sua exibição e por isso são chamados de componentes pesados. Por isso, para atingir uma portabilidade razoável, apenas componentes básicos estão presentes na AWT. Mesmo assim a portabilidade da AWT não é tão grande quanto a JFC, apresentando, em alguns casos, grandes diferenças de visualização entre ambientes operacionais. Neste capítulo, abordaremos a versão 1.1 do AWT. Os componentes da JFC/Swing serão tratados isoladamente no capítulo XX. A Hierarquia de componentes
176 O AWT é um pacote contendo classes e interfaces para a criação de objetos para interação em janelas gráficas. Com essas classes é possível criar e manter objetos tais como: Botões (Classe Button) Caixa de Escolha (Classe Choice) Diálogos (Classe Dialog) Janelas (Classe Window) Menus (Classe Menu) Barra de Rolamento (Classe Scrollbar) Etc. As classes se organizam em uma hierarquia. A figura VI.XX mostra a organização hierárquica das classes.! Color! Component " Button " Canvas " Checkbox " Choice " Container # Panel # ScrollPane # Window Dialog FileDialog Frame " Label " List " Scrollbar " TextComponent # TextArea # TextField! MenuComponent " MenuBar " MenuItem # Menu 175
177 176 Figura VI.1 Hierarquia das classes no pacote AWT. Olá Mundo AWT Para usar classes definidas na AWT é preciso importá-las: import java.awt.*; Algumas classes são usadas para criar objetos que servirão para conter outros objetos. Estas classes são descendentes da classe Container. Cada classe possui um conjunto de atributos que podem ser acessados por meio de métodos. Como de praxe, apresentaremos primeiramente a versão AWT do olá mundo para ilustrar os principais componentes de uma aplicação import java.awt.*; class OlaFrame extends Frame public OlaFrame() super( Ola ); setsize(100,50); add(new Label("ola mundo")); public static void main(string args[]) new OlaFrame().setVisible(true); Exemplo XX.XX Versão AWT do Olá mundo. Para criarmos uma aplicação gráfica precisaremos de algum tipo de janela para colocarmos os componentes. Por isso a classe OlaFrame do exemplo XX.XX é uma subclasse da classe Frame que, por sua vez, e subclasse da classe Window. A classe Frame implementa uma janela gráfica com título e margem, sendo que a área do Frame inclui a margem. No exemplo construtor da classe invoca o construtor da superclasse, passando como argumento o título da janela.
178 A classe Frame cria uma janela com 0 x 0 pixels e invisível. Para dimensioná-la e torná-la visível é necessário usar os métodos setsize() e setvisible(). A classe Label é usada para criar um objeto de texto que é adicionado ao Frame. O objeto é posicionado dentro do Frame de acordo com um esquema de posicionamento, denominado de layout. Se você executou este programa deve ter notado que não foi possível finalizar o programa a não ser por meio de um Ctrl-C na console ou algum outro método indireto como o kill da shell Unix. Isto acontece porque os objetos gráficos na linguagem Java não possuem tratadores implícitos de eventos. Ou seja, se você quer que algum evento seja tratado, então forneça o código para isto. Existem dois modelos para tratamento dos eventos que incidem sobre a interface. O primeiro foi introduzido na versão 1.0 do AWT e o segundo foi introduzido na versão 1.1 do AWT com o objetivo de substituir o primeiro, uma vez que ele sofre de deficiências de projeto e desempenho. Neste livro abordaremos apenas o modelo de eventos Tratamento de Eventos O estilo de programação relacionado com interfaces gráficas é a programação voltada para eventos. Ou seja, o usuário define a interface e um conjunto de procedimentos para lidar com os eventos que incidirão sobre a interface. A construção de uma aplicação gráfica exige a criação de pelo menos um objeto que defina uma área no vídeo para exibir os componentes da aplicação. Para isso pode-se utilizar um objeto da classe Window ou Frame. O exemplo XX.XX contém o código de um programa que exibe uma mensagem na tela. Posteriormente é necessário definir quem deve receber os eventos gerados pela interação com a interface. Cada linguagem apresenta uma solução distinta para o tratamento de eventos. A solução Java implementada no modelo de eventos 1.1 é explicada a seguir. Modelo de Eventos 1.1 O Modelo de eventos do AWT1.1 é um grande avanço sobre o modelo 1.0. Ele não só é mais flexível e orientado a objetos, como permitiu o desenvolvimento da interface de aplicação JavaBeans, que viabiliza a descrição de componentes visuais.
179 A idéia por trás do novo modelo é que uma fonte de eventos possa enviar os eventos para um ou mais objetos cadastrados para o recebimento do evento, denominados de event listeners. Geralmente, a fonte de evento é um componente, ou seja, um objeto da classe Component. Um listener é um objeto que implementa a interface EventListener ou alguma de suas subinterfaces. A figura IV.XX ilustra a estratégia adotada. 178 Componente 1 A Componente 2 B evento evento Listener Listener Listener Listener Listener Figura VI.XX Esquema de delegação de eventos AWT1.1. Note que um Listener pode estar registrado em mais de um componente. Um componente pode ter mais de uma lista de Listener, dependendo dos tipos de eventos que ele pode gerar. Uma ação sobre um componente pode gerar mais de um evento. Para ilustrar o tratamento de eventos, o exemplo XX.XX mostra como criar uma janela da classe Frame que trata o evento para fechar a janela, gerado quando o usuário clica o ícone, situado no canto superior direito do Frame. class FrameX extends Frame implements WindowListener public FrameX() this("framex"); public FrameX(String Titulo) super(titulo); addwindowlistener(this); setsize(100,50);
180 ; Java na Prática public void windowopened(windowevent we) public void windowclosed(windowevent we) public void windowiconified(windowevent we) public void windowdeiconified (WindowEvent we) public void windowactivated(windowevent we) public void windowdeactivated(windowevent we) public void windowclosing(windowevent we) setvisible(false); dispose(); System.exit(0); Exemplo XX.XX Código para tratar o evento para fechar a janela. 179 Para simplificar o exemplo o próprio componente (do tipo Frame) se registrou como um Listener para os eventos gerados por ele. De modo a se habilitar a receber os eventos gerados por um componente do tipo Window a classe deve implementar a interface WindowListener que é uma subinterface da EventListener. class FrameX extends Frame implements WindowListener É preciso também se registrar junto ao componente para receber os eventos. Como no exemplo VI.XX o componente é uma instância da própria classe que implementa a interface, basta então adicionar a linha addwindowlistener(this) no construtor da classe. Como a classe implementa a interface é necessário incluir código para implementar todos os métodos da interface. Cada método da interface trata um tipo de evento. No caso do exemplo VI.XX, estamos interessados em tratar eventos que solicitam o fechamento da janela. Portanto, é necessário incluir o código para tratamento do evento neste método. Os outros métodos são implementados sem linhas de código, para cumprir a exigência do compilador. Note que todos os métodos da classe FrameX recebem um tipo de evento: WindowEvent. No modelo de eventos 1.1, na versão SDK1.3, existem 14 classes para os eventos, definidas no pacote java.awt.event, todas subclasses da classe java.awt.event.awtevent e cada uma representando um conjunto de eventos relacionados, como mostrado na tabela VI.XX. Evento ComponentEvent Descrição Indica que um componente moveu, foi escondido, exibido ou mudou de tamanho.
181 FocusEvent Indica que um componente recebeu ou perdeu o foco. KeyEvent Indica que um componente recebeu uma entrada pelo teclado. MouseEvent Indica que um componente recebeu uma entrada pelo mouse. ActionEvent Indica que um componente foi ativado. AdjustmentEvent Indica que uma barra de rolamento ou componente similar foi movido. HierarchyEvent indica uma mudança na hierarquia de componentes a qual o componente pertence. InputEvent É o evento raiz de todos os eventos de entrada de componentes. InputMethodEvent Contém informação sobre o texto que está sendo editado. Sempre que o texto for alterado este evento será enviado. InvocationEvent O evento que executa o método run() em um tipo Runnable quando disparado pelo thread despachador de eventos da AWT. ItemEvent Indica que um item de um componente como lista, checkbox, ou similar foi selecionado. ContainerEvent Indica que um componente foi adicionado ou removido de um container. WindowEvent Indica que a janela mudou de estado. TextEvent Indica que uma mudança ocorreu em um campo de texto. Tabela VI.XX Classes de Eventos. O exemplo XX.XX mostra o código de uma janela contendo um botão. A classe que implementa a janela se cadastra junto ao componente Button para receber o eventos sobre os componente. Para diminuir a complexidade do código a classe FrameBotao é subclasse da FrameX definida anteriormente. Toda vez que o componente Button é acionado, gerando um evento ActionEvent, o método actionperformed() da instância FrameBotao é invocado. O método alterna o texto exibido pelo botão a cada chamada entre os textos Aperte aqui e Valeu. import java.awt.*; import java.awt.event.*; public class FrameBotao extends FrameX implements ActionListener Button b; public FrameBotao() setlayout(null); setsize(100,80); b = new Button("Aperte aqui"); 180
182 Java na Prática add(b); b.setbounds(10,40,80,30); b.addactionlistener(this); 181 public void actionperformed(actionevent e) if (b.getlabel().equals("aperte Aqui")) b.setlabel( Valeu ); else b.setlabel("aperte Aqui"); public static void main(string args[]) (new FrameBotao()).setVisible(true); Exemplo XX.XX Código para tratar um evento gerado em botão. Tratamento de Eventos com classes Internas Agora vamos supor que uma determinada classe necessitasse receber vários eventos de vários componentes. O código da classe poderia ficar um pouco confuso, além da lista enorme de interfaces que necessitariam de ser declarada após a palavra implements. Uma forma mais elegante de se lidar com estes casos é por meio de classes internas, ou inner classes. Desta forma, o código para tratar cada grupo de eventos fica localizado em uma única classe, e como as classes internas possuem acesso à classe externa, os atributos e métodos da classe externa podem ser diretamente referenciados. O exemplo XX.XX mostra o exemplo XX.XX alterado de forma que o evento ActionEvent gerado acionamento do componente Button seja tratado por uma classe interna. import java.awt.*; import java.awt.event.*; public class FrameBotao extends FrameX Button b; Public FrameBotao()
183 Java na Prática setlayout(null); setsize(100,80); b = new Button("Aperte Aqui"); add(b); b.setbounds(10,40,80,30); b.addactionlistener(new Bl()); 182 class Bl implements ActionListener public void actionperformed(actionevent e) if (b.getlabel().equals("aperte Aqui")) b.setlabel( Valeu ); else b.setlabel("aperte Aqui"); public static void main(string args[]) (new FrameBotao()).setVisible(true); Exemplo XX.XX Código para tratar um evento gerado em botão. Cada componente pode receber um determinado conjunto de listener para receber eventos. A Figura VI.XX mostra a relação entre alguns componentes e os eventos que podem ser gerados por eles.
184 183 Button ComponentEvent Frame FocusEvent Choice KeyEvent TextField MouseEvent Scrollbar Component Label ActionEvent AdjustmentEvent Dialog ItemEvent Panel WindowEvent Menu ContainerEvent MenuItem TextEvent Figura XX.XX Relação entre os componentes e os eventos.
185 As instâncias das classes de eventos mantém as informações relativas ao evento. Por exemplo, a classe MouseEvent possui os seguintes métodos para recuperação de informação sobre o evento: 184 Método int getid() int getx() int gety() Point getpoint() int getclickcount() boolean ispopuptrigger() void translatepoint(int x, int y) Descrição tipo do evento. Por exemplo: MouseEvent.MOUSE_CLICKED, MouseEvent.MOUSE_PRESSED coordenada x do evento. coordenada y do evento. coordenada x e y do evento. retorna o número de mouse clicks. indica se o evento é um disparo para menu pop-up. move a posição do evento por x e y. Tabela VI.XX Métodos da classe MouseEvent. Basicamente, para se codificar um tratador para um determinado evento basta seguir os seguintes passos: 1. Determine o grupo de eventos deseja tratar. 2. No nome do grupo de eventos substitua o termo trecho Event por Listener. Este é o nome da interface que você deve implementar. Crie uma classe interna que implementa a interface. 3. Codifique os métodos que tratam os eventos que você deseja tratar. Todos os métodos da interface tem que ser implementados, mas o corpo dos métodos que não interessam podem ser deixados vazios. 4. Registre um objeto da classe interna junto ao componente, por meio do método addxxx, onde XXX é o nome da interface implementada. Por exemplo, no exemplo VI.XX, para tratar o grupo de eventos ActionEvent foi criada uma classe interna que implementa a interface ActionListener, e um objeto da classe interna é registrado no componente por meio do método addactionlistener. Portanto, tendo o nome do grupo de eventos é fácil obter o nome da interface e do método de registro no componente. Segue abaixo uma lista com alguns métodos para registro de listeners. Button.addActionListener(ActionListener l)
186 Frame.addWindowListener(WindowListener l) Choice.addItemListener(ItemListener l) TextField.addTextListener(TextListener l) Scrollbar.addAdjustamentListener(AdjustamentListene r l) Component.addComponentListener(ComponentListener l) Component.addFocusListener(FocusListener l) Component.addKeyListener(KeyListener l) Algumas interfaces exigem a implementação de um grande número de métodos, como por exemplo a interface WindowListener, como foi visto no exemplo VI.XX. Isto pode tornar a implementação das interfaces uma tarefa um tanto tediosa. Com o objetivo de auxiliar esta tarefa, foi incluído no pacote java.awt.event seis classes abstratas, denominadas adapters que facilitam a implementação de Listener. O programador só precisa criar uma subclasse da classe adapter escolhida e sobrescrever o método desejado. Para determinar o nome da classe adapter basta substituir no nome da interface a subcadeia Listener por adapter. A tabela VI.XX apresenta os nomes das classes adapters. 185 ComponentAdapter ContainerAdapter FocusAdapter KeyAdapter MouseAdapter MouseMotionAdapter WindowAdapter Tabela VI.XX Classes adapters. O exemplo XX.XX mostra o exemplo XX.XX alterado de forma que o evento ActionEvent gerado acionamento do componente Button seja tratado por uma classe interna que é subclasse da classe MouseAdapter. import java.awt.*; import java.awt.event.*; public class Botao extends FrameX Button b; public Botao()
187 Java na Prática setlayout(null); setsize(100,80); b = new Button("Aperte Aqui"); add(b); b.setbounds(10,40,80,30); b.addactionlistener(new Bl()); class Bl extends MouseAdapter public void actionperformed(actionevent e) if (b.getlabel().equals("aperte Aqui")) b.setlabel("valeu"); else b.setlabel("aperte Aqui"); public static void main(string args[]) (new Botao()).setVisible(true); Exemplo XX.XX Código para tratar um evento gerado em botão. 186 No entanto, existem algumas desvantagens em se usar classes abstratas no lugar de interfaces. A primeira delas é que elimina a possibilidade da classe ser subclasse de outra classe e a segunda é que se o programador cometer algum erro na digitação do nome de algum método, ele será encarado como um novo método e o original não será sobrescrito. Exemplo Básico Apresentaremos agora um exemplo um pouco mais complexo, porém simples o suficiente para que seja facilmente entendido. O objetivo deste exemplo e servir de base para apresentarmos os principais componentes da biblioteca AWT. Portanto, a medida que novos componentes forem apresentados, iremos exemplificar o uso deste componentes acrescentando-os em nosso exemplo básico. A função de nosso exemplo é servir como ambiente para execução de comandos MS-DOS, tais como dir e type. O exemplo foi projetado para funcionar nos ambientes MS-WINDOWS 95 e MS-WINDOWS 98 mas pode ser facilmente adaptado para funcionar em outros ambientes operacionais. A figura XX.XX mostra a interface da primeira versão de nosso exemplo. Note que, apesar de simples, o nosso exemplo apresenta três componentes em sua
188 interface: um botão e duas caixas de texto, sendo uma com barras de rolamento e capacidade para múltiplas linhas. 187 Figura XX.XX Aparência da aplicação para execução de comandos MS- DOS. A caixa de texto com capacidade para uma linha de texto é um componente TextField e sua função é permitir a entrada de comandos dos para a execução. A caixa de texto com capacidade para múltiplas linhas de texto é um componente TextArea e sua função é exibir o resultado da execução do comando. Finalmente, o botão é implementado por um componente Button e sua função e gerar eventos para execução do comando digitado. O exemplo XX.XX mostra o código da aplicação, implementado pela classe Comando import java.io.*; import java.awt.*; import java.awt.event.*; public class Comando extends Frame private TextField tfcomando;
189 private TextArea tasaida; private Button btexec; private String com; public Comando() com = "command.com /c "; // Altere essa linha para outros ambientes operacionais tfcomando = new TextField(50); tasaida = new TextArea(20,60); btexec = new Button( Executa ); tasaida.seteditable(false); add(tfcomando,borderlayout.north); add(btexec,borderlayout.south); add(tasaida,borderlayout.center); addwindowlistener(new WindowAdapter() public void windowclosing(windowevent evt) System.exit(0); ); btexec.addactionlistener(new ActionListener() public void actionperformed(actionevent evt) btexecactionperformed(evt); ); pack(); private void btexecactionperformed(actionevent evt) String s = tfcomando.gettext(); if (s==null) return; try Process p = Runtime.getRuntime().exec(com+s); InputStream in = p.getinputstream(); BufferedReader br = new BufferedReader(new InputStreamReader(in)); String line; tasaida.settext(""); while ((line = br.readline())!= null) tasaida.append(line+'\n'); tasaida.append("fim!"); catch(exception e) tasaida.settext(e.getmessage()); return; public static void main(string args[]) new Comando().show();
190 Exemplo XX.XX Código que executa um comando DOS. Na linha 12 é criado um objeto String contendo o parte inicial do comando necessário para invocar a shell do MS-DOS. Essa parte inicial deve ser alterada para adaptar o exemplo para outros sistemas operacionais. Nas linhas 14 a 16 são criados os componentes que farão parte da interface. Na linha 14 o componente TextField é criado com espaço para exibir 50 caracteres. Na linha 15 o componente TextArea é criado com espaço para exibir 20 linhas e 50 caracteres por linha. Na linha 16 o componente Button é criado com um o texto Executa. Na linha 18 é chamado o método seteditable() do componente TextArea com o argumento false para impedir que o seu conteúdo seja editado. Isso ocorre porque a função do componente é apenas exibir o resultado da execução dos comandos. O leitor deve ter notado que a criação das classes que irão tratar os eventos é um pouco diferente do que mostramos até agora. Nas linhas 24 a 28 e 30 a 34 as declarações das classes são feitas no mesmo local onde os objetos são instanciados. Este tipo de classe interna é denominado de classe interna anônima. A classe é denominada de anônima pois nenhum nome é associado a ela. A classe anônima se torna automaticamente subclasse da classe mencionada após o operador new. É vantajoso usar este tipo de construção quando apenas uma instância da classe será gerada, uma vez que amarra a declaração da classe com seu uso. No entanto, em alguns casos esta técnica pode diminuir a legibilidade do código. Outro fato que pode intrigar o leitor é a distribuição dos componentes na janela. Aparentemente, pouco código foi dedicado ao posicionamento dos elementos na interface. Mesmo assim os componentes foram arranjados de forma razoavelmente ordenada na interface. Isto se deve ao fato de todos os componentes do tipo Container seguirem um arranjo predefinido (layout). Este arranjo é implementado pelas classes BorderLayout, CardLayout, FlowLayout, GridBagLayout e GridLayout, sendo o BorderLayout o arranjo default. Existe também a possibilidade de não se adotar nenhum layout. Neste caso é preciso posicionar os componentes em termos de coordenadas dentro do objeto do tipo Container. Os objetos de layout serão tratados mais adiante. No momento basta saber que o layout adotado é o default e que este tipo de layout arranja os componentes de acordo com os pontos cardeais (NORTH, SOUTH, EAST, WEST, CENTER). As linhas 20 a 22
191 mostram as adições de componentes dentro do Frame, com a indicação do seus posicionamentos relativos segundo o layout adotado. A adição de componentes é feita por meio do método add(). As linhas 38 a 52 mostram o código do método que executa o comando MS- DOS e exibe o resultado no componente TextArea. Na linha 42 o comando MS-DOS é executado por meio do método exec() do objeto Runtime. O objeto Runtime é obtido pelo método estático getruntime() da classe Runtime, uma vez que não pode ser instanciado diretamente. O processo filho, uma vez iniciado, é executado de forma concorrente com o processo pai. O método exec() retorna um objeto do tipo Process, tornando possível controlar o processo e obter informações sobre ele. No nosso caso estamos interessados em capturar a saída padrão do processo filho para exibi-la na interface. Isso é feito na linha 43 por meio do método getinputstream() do objeto Process. O objeto InputStream é canalizado para um objeto BufferedReader com o objetivo de facilitar sua manipulação. As linhas restantes do método tratam da leitura da saída padrão do processo filho e de sua exibição na TextArea. Na linha 46 é usado o método settext() para colocar um texto vazio no componente e, dessa forma, limpar a TextArea. Na linha 48 a String lida da saída padrão do processo filho é anexada na TextArea. 190 Acrescentando Cores Podemos alterar as cores de alguns componentes do exemplo XX.XX para torná-lo visualmente mais interessante por meio de objetos da classe Color. Esta classe encapsula a criação de cores por meio dos níveis RGB. Ela mantém também um conjunto de atributos constantes com valores de cores predefinidos. Podemos, por exemplo, alterar a cor de fundo do componente TextArea por meio do método setbackground(). tasaida.setbackground(color.lightgray); Podemos alterar também a cor de fundo do componente Button. Neste caso vamos obter para definir a cor por meio de um número que representa seus componentes RGB. btexec.setbackground(new Color(0x08FDDF10));
192 Para modificar a cor frontal é preciso utilizar o método setforeground(). Todos os componentes possuem métodos equivalentes a estes para alteração das cores. 191 Gerenciando o Layout O posicionamento de componentes dentro de objetos do tipo Container é determinado por uma instância de objeto denominado de gerenciador de layout (layout managers). A AWT fornece 5 classes para a implementação de layouts, descritas na tabela XX.XX. O layout default é o implementado pela classe BorderLayout. O pacote Swing acrescenta a classe BoxLayout ao conjunto de gerenciadores de layout. Gerenciador de Layout BorderLayout FlowLayout CardLayout GridBagLayout GridLayout Descrição Posiciona e redimensiona os componentes de um Container em cinco regiões: norte, sul, leste e oeste. Cada região é identificada respectivamente pelas constantes: NORTH, SOUTH, EAST, WEST, e CENTER. Posiciona os componentes de um Container em um fluxo da esquerda para a direita, como linhas em um parágrafo. Cada linha é centralizada. Trata cada componente do Container como uma a carta. Somente uma carta é visível em um determinado instante e o Container funciona como uma pilha de cartas. O primeiro componente adicionado é o primeiro visível quando o Container é exibido. É o gerenciador de layout mais flexível e o mais complexo. Ele arranja os componentes verticalmente e horizontalmente sem exigir que os componentes tenham o mesmo tamanho. Mantém um grade retangular de células, sendo que cada componente pode ocupar mais de uma célula. Posiciona e redimensiona os componentes de um Container em uma grade retangular. O Container é dividido em retângulos de tamanhos iguais e um componente é colocado em cada retângulo. Tabela XX.XX Gerenciadores de Layout.
193 Para definir o layout de um objeto do tipo Container basta utilizar o método setlayout() do objeto, passando como argumento um objeto gerenciador de layout, como mostrado abaixo: Frame f = new Frame(); f.setlayout(flowlayout); 192 É possível também não adotar layout algum, optando por posicionar os componentes em coordenadas absolutas dentro do Container. Para isso basta passar null como argumento para o método setlayout(). O posicionamento absoluto de componentes pode ser feito pelos métodos setlocation() ou setbounds() do componente, como mostrado abaixo. Frame f = new Frame(); f.setlayout(null); Button b = new Button( Teste ); f.add(b); b. setlocation(10, 10); Porém, a adoção de gerenciadores de layout permite o desenvolvimento que se adaptam mais facilmente à redimensionamentos e à mudanças de linguagens. Isto é o posicionamento e dimensionamento absoluto pode fazer com que componentes tornem-se parcialmente ou totalmente ocultos com o redimensionamento da janela. O mesmo pode ocorrer com os labels dos componentes quando forem traduzidos para outra língua. Já estes problemas não ocorrem com o posicionamento e dimensionamento relativo. Exemplo com BorderLayout O exemplo XX.XX utiliza este gerenciador de layout para distribuir os componentes na interface. O exemplo abaixo mostra distribuição de cinco botões em um Frame por meio deste tipo de gerenciador de layout. import java.awt.*; public class BorderTeste extends FrameX public BorderTeste() setlayout(new BorderLayout()); add(new Button("Norte"), BorderLayout.NORTH);
194 193 add(new Button("Sul"), BorderLayout.SOUTH); add(new Button("Leste"), BorderLayout.EAST); add(new Button("Oeste"), BorderLayout.WEST); add(new Button("Centro"), BorderLayout.CENTER); public static void main(string a[])new BorderTeste().show(); Exemplo XX.XX Frame com BorderLayout. A figura XX.XX mostra o resultado da execução do código do exemplo XX.XX. Figura XX.XX Aparência do Frame com BorderLayout. Exemplo com FlowLayout O exemplo abaixo mostra distribuição de cinco botões em um Frame por meio deste tipo de gerenciador de layout. import java.awt.*; public class FlowTeste extends FrameX public FlowTeste() setlayout(new FlowLayout());
195 194 add(new Button("UM")); add(new Button("DOIS")); add(new Button("TRÊS")); add(new Button("QUATRO")); add(new Button("CINCO")); setsize(200,100); public static void main(string a[])new FlowTeste().show(); Exemplo XX.XX Frame com FlowLayout. A figura XX.XX mostra o resultado da execução do código do exemplo XX.XX. Note que quando acaba o espaço em uma linha os componentes são posicionados na linha de baixo seguindo da esquerda para a direita. Note também que cada linha é centralizada no Container. Figura XX.XX Aparência do Frame com FlowLayout. Exemplo com CardLayout O exemplo abaixo mostra usar o CardLayout para alternar a exibição de componentes. Um botão é adicionado à interface para receber eventos. Quando o botão é pressionado o tratador de eventos alterna a exibição dos componentes do CardLayout por meio de chamadas ao método next() do gerenciador de layout. A JFC fornece o componente JtabbedPane que apresenta maiores facilidades. import java.awt.*; import java.awt.event.*; public class CardTeste extends FrameX
196 195 Panel p; CardLayout cl; Button b; public CardTeste() setlayout(new BorderLayout()); p = new Panel(); b = new Button("Muda carta"); cl = new CardLayout(); p.setlayout(cl); p.add(new Label("Este Label está na primeira carta"),"um"); p.add(new Label("Este Label está na segunda carta"),"dois"); add(b, BorderLayout.NORTH); add(p, BorderLayout.SOUTH); b.addactionlistener( new ActionListener() public void actionperformed(actionevent evt) cl.next(p); ); pack(); public static void main(string a[])new CardTeste().show(); Exemplo XX.XX Frame com CardLayout. Note que ao adicionar um componente ao Container é preciso passar um identificador. A figura XX.XX mostra o resultado da execução do código do exemplo XX.XX. Figura XX.XX Aparência do Frame com CardLayout.
197 Exemplo com GridLayout Java na Prática 196 O exemplo abaixo mostra distribuição de cinco botões em um Frame por meio deste tipo de gerenciador de layout. import java.awt.*; public class GridTeste extends FrameX public GridTeste() setlayout(new GridLayout(3,2)); add(new Button("UM")); add(new Button("DOIS")); add(new Button("TRÊS")); add(new Button("QUATRO")); add(new Button("CINCO")); public static void main(string a[])new GridTeste().show(); Exemplo XX.XX Frame com GridLayout. A figura XX.XX mostra o resultado da execução do código do exemplo XX.XX. Figura XX.XX Aparência do Frame com GridLayout.
198 197 Exemplo com GridBagLayout Este gerenciador é o mais complexo, mas é também o que permite um maior controle sobre o posicionamento dos elementos, sem perder as vantagens de um posicionamento relativo. O exemplo XX.XX mostra distribuição de nove botões em um Frame por meio deste tipo de gerenciador de layout. import java.awt.*; public class GridBagTeste extends FrameX public GridBagTeste() Button b; GridBagLayout gridbag = new GridBagLayout(); GridBagConstraints c = new GridBagConstraints(); setlayout(gridbag); for (int i=1;i<10;i++) switch(i) case 1: c.fill = GridBagConstraints.BOTH; c.weightx = 1.0; break; case 6: case 3: c.gridwidth = GridBagConstraints.REMAINDER; c.weightx = 1.0; break; case 4: c.weightx = 0.0; break; case 5: c.gridwidth = GridBagConstraints.RELATIVE; c.weightx = 1.0; break; case 7: c.gridwidth = 1; c.gridheight = 2; c.weighty = 1.0; break; case 8: c.gridwidth = GridBagConstraints.REMAINDER; c.weighty = 0.0; c.gridheight = 1; break; b = new Button("Botão "+i); gridbag.setconstraints(b, c); add(b);
199 198 pack(); public static void main(string args[]) new GridBagTeste().show(); Exemplo XX.XX Frame com GridBagLayout. O posicionamento relativo dos componentes é controlado por meio de um objeto da classe GridBagConstraints. Ele possui vários atributos públicos que orientam o gerenciador de layout no momento de posiciona e reorganizar os componentes. O objeto é associado a cada componente por meio do método setconstraints() da instância GridBagLayout. Os atributos utilizados no exemplo foram: fill usado quando a área para a exibição do componente é maior que a área é requisitada pelo componente. Os valores possíveis (definidos na classe GridBagConstraints) são NONE (default), HORIZONTAL (torna o componente largo o suficiente para preencher a área horizontalmente), VERTICAL (torna o componente alto o suficiente para preencher a área na vertical), e BOTH (preenche nos dois sentidos). weightx, weighty determina como o espaço excedente é distribuído, definindo o comportamento para o redimensionamento. Ou seja define a proporção do espaço que excedente que será distribuído entre os componentes. Se não for especificado para pelo menos uma linha (weightx) e coluna (weighty), todos os componentes serão agrupados no centro do Container, uma vez que o peso de cada um é zero (default). gridwidth, gridheight Especifica o numero de células em um linha (gridwidth) ou coluna (gridheight) ocupadas pela área de exibição de um componente. O valor default é 1. Os valores possíveis (definidos na classe GridBagConstraints) são REMAINDER para especificar que o componente deve ser o último da linha (para gridwidth) ou coluna (para gridheight). RELATIVE para especificar que o componente deve ser o vizinho do último na linha (para gridwidth) ou coluna (para gridheight). Ou valores inteiros especificando o número de linhas e colunas ocupadas.
200 199 A figura XX.XX mostra o resultado da execução do código do exemplo XX.XX. Figura XX.XX Aparência do Frame com GridBagLayout. Utilizando Listas Muitas vezes é necessário que o usuário selecione um item a partir de uma lista de itens disponíveis. A AWT torna possível a seleção de um item em uma lista de itens por meio do componente Choice. Este componente possui métodos para a adição, remoção e acesso a itens. O exemplo abaixo mostra a alteração do exemplo XX.XX que usa uma lista de escolha para armazenar os comandos anteriores. Desta forma o usuário economiza na digitação de comandos repetidos import java.io.*; import java.awt.*; import java.awt.event.*; public class Comando2 extends java.awt.frame private TextField tfcomando; private TextArea tasaida; private Button btexec; private Panel panel; private Choice ch; private String com; public Comando2()
201 com = "command.com /c "; tfcomando = new TextField(50); tasaida = new TextArea(20,60); panel = new Panel(); btexec = new Button("Executa"); ch = new Choice(); ch.setvisible(false); btexec.setbackground(new Color(0x08FDDF10)); tasaida.setbackground(color.lightgray); tasaida.seteditable(false); add(panel,borderlayout.north); panel.add(tfcomando,borderlayout.north); panel.add(ch,borderlayout.center); add(btexec,borderlayout.south); add(tasaida,borderlayout.center); addwindowlistener(new WindowAdapter() public void windowclosing(windowevent evt) System.exit(0); ); btexec.addactionlistener(new ActionListener() public void actionperformed(actionevent evt) btexecactionperformed(evt); ); ch.additemlistener( new ItemListener() public void itemstatechanged(itemevent evt) if (evt.getstatechange() == ItemEvent.SELECTED) tfcomando.settext(ch.getselecteditem()); ); pack(); private void btexecactionperformed(actionevent evt) String s = tfcomando.gettext(); if (s==null) return; boolean inclui =true; for (int i= ch.getitemcount()-1;i>=0;i--) if (s.equals(ch.getitem(i))) inclui=false; if (inclui) ch.add(s); ch.setvisible(true);
202 pack(); try Process p = Runtime.getRuntime().exec(com+s); InputStream in = p.getinputstream(); BufferedReader br = new BufferedReader(new InputStreamReader(in)); String line; tasaida.settext(""); while ((line = br.readline())!= null) tasaida.append(line+'\n'); tasaida.append("fim!"); catch(exception e) tasaida.settext(e.getmessage()); return; public static void main(string args[]) new Comando2().show(); Exemplo XX.XX Aplicação para execução de comandos MS-DOS com lista de escolha. Na linha 21 é usado o método setvisible() para que o componente Choice fique invisível inicialmente. Isto é feito porque no início a lista de itens está vazia e, portanto, não é preciso exibi-la. Nas linhas 45 a 50 é adicionado um objeto ItemListener à lista de listener do objeto Choice. Este objeto receberá os eventos de seleção de itens na lista de escolha. Nas linhas 58 a 64 é verificado se o comando digitado já está incluído na lista de itens. Caso não esteja o item é incluído e o método pack() do Frame é chamado, para que a janela se ajuste ao tamanho dos componentes. A figura XX.XX mostra o resultado da execução do código do exemplo XX.XX.
203 202 Figura XX.XX Aparência da aplicação para execução de comandos MS-DOS com lista de escolha. Trabalhando com Menus e Diálogos Outros componentes importantes para criação de interfaces com o usuário são os menus e as caixas de diálogos. De forma a ilustrar o uso desses recursos modificaremos o exemplo XX.XX para conter uma barra de menus com apenas um menu. O menu conterá dois itens de menu. O acionamento do primeiro item de menu ocasionará a exibição de um pequeno texto de ajuda no componente TextArea. O acionamento do segundo item de menu ocasionará a exibição de uma caixa de diálogo contendo informações sobre a aplicação import java.io.*; import java.awt.*; import java.awt.event.*; public class Comando3 extends java.awt.frame private TextField tfcomando;
204 private TextArea tasaida; private Button btexec; private Panel panel; private Choice ch; private String com; public Comando3() com = "command.com /c "; tfcomando = new TextField(50); tasaida = new TextArea(20,60); panel = new Panel(); btexec = new Button("Executa"); ch = new Choice(); ch.setvisible(false); btexec.setbackground(new Color(0x08FDDF10)); tasaida.setbackground(color.lightgray); tasaida.seteditable(false); Menu menu = new Menu("Ajuda"); menu.add("resumo"); menu.addseparator(); menu.add("sobre"); MenuBar mb = new MenuBar(); mb.add(menu); setmenubar(mb); add(panel,borderlayout.north); panel.add(tfcomando,borderlayout.north); panel.add(ch,borderlayout.center); add(btexec,borderlayout.south); add(tasaida,borderlayout.center); addwindowlistener(new WindowAdapter() public void windowclosing(windowevent evt) System.exit(0); ); btexec.addactionlistener(new ActionListener() public void actionperformed(actionevent evt) btexecactionperformed(evt); ); ch.additemlistener( new ItemListener() public void itemstatechanged(itemevent evt) if (evt.getstatechange() == ItemEvent.SELECTED)
205 ); tfcomando.settext(ch.getselecteditem()); menu.addactionlistener(new ActionListener() public void actionperformed(actionevent evt) if (evt.getactioncommand().equals("sobre")) mostrasobre(); else if (evt.getactioncommand().equals("resumo")) tasaida.settext("digite o Comando e pressione + o botão <executa> para executar."); ); pack(); private void mostrasobre() Dialog d = new Dialog(this,"Sobre Comando"); d.setmodal(true); Label lb1 = new Label("Comando Versão 1.2", Label.CENTER); Label lb2 = new Label("Alcione de Paiva Oliveira", Label.CENTER); lb1.setfont(new Font ("Dialog", Font.BOLD Font.ITALIC, 18)); lb2.setfont(new Font ("Courier New", 0, 16)); d.add(lb1,borderlayout.north); d.add(lb2,borderlayout.south); d.setbackground(color.cyan); d.addwindowlistener(new WindowAdapter() public void windowclosing(windowevent evt) evt.getwindow().dispose(); ); d.pack(); d.show(); private void btexecactionperformed(actionevent evt) String s = tfcomando.gettext(); if (s==null) return; boolean inclui =true; for (int i= ch.getitemcount()-1;i>=0;i--) if (s.equals(ch.getitem(i))) inclui=false; if (inclui) ch.add(s); ch.setvisible(true); pack(); try Process p = Runtime.getRuntime().exec(com+s); InputStream in = p.getinputstream(); BufferedReader br = new BufferedReader(new InputStreamReader(in));
206 String line; tasaida.settext(""); while ((line = br.readline())!= null) tasaida.append(line+'\n'); tasaida.append("fim!"); catch(exception e) tasaida.settext(e.getmessage()); return; public static void main(string args[]) new Comando3().show(); Exemplo XX.XX Aplicação para execução de comandos MS-DOS com barra de menus e uma caixa de diálogo. As linhas 27 a 33 contém o código para a inserção da barra de menus e do menu. Na linha 27 é criado um componente Menu com o label Ajuda. Nas linhas 28 a 30 são adicionados dois itens de menu e um separador ao componente Menu. Nas linha 31 e 32 é criado um componente MenuBar e o componente Menu é adicionado ao MenuBar. Na linha 33 o componente MenuBar é definida como a barra de menu da janela corrente. Nas linhas 60 a 67 é adicionado um objeto ActionListener à lista de Listener do objeto Menu. Este objeto receberá os eventos de seleção de itens do menu. Caso o item de menu selecionado seja o Sobre então o método mostrasobre() é invocado. Caso o item de menu selecionado seja o Resumo então é exibido um texto de ajuda no componente TextArea. As linhas 71 a 88 contém o código do método mostrasobre(). Este método é responsável pela montagem e exibição da caixa de diálogo com informações sobre a aplicação. Na linha 72 é criada uma instância da classe Dialog. O construtor desta classe exige que seja passado como parâmetro um objeto da classe Frame ou Dialog para deter a posse do objeto a ser criado. Quando o objeto possuidor é tornado invisível ou minimizado a janela Dialog é automaticamente tornada invisível ou minimizada. No caso do exemplo o Frame corrente é passado como parâmetro. Na linha 73 é invocado o método setmodal() para torna a o diálogo modal. O diálogo modal bloqueia toda entrada em todas as janelas no contexto da aplicação, exceto pelas janelas criadas tendo o diálogo como possuidor. A figura XX.XX mostra o resultado da execução do código do exemplo XX.XX.
207 206 Figura XX.XX Aparência da aplicação para execução de comandos MS-DOS com barra de menus. Capturando Eventos do Teclado Principais Classes Color Esta classe encapsula a criação de cores por meio dos níveis RGB. Ela mantém também um conjunto de atributos constantes com valores de cores predefinidos.
Linguagem de Programação JAVA. Técnico em Informática Professora Michelle Nery
Linguagem de Programação JAVA Técnico em Informática Professora Michelle Nery Agenda Regras paravariáveis Identificadores Válidos Convenção de Nomenclatura Palavras-chaves em Java Tipos de Variáveis em
Leia maisProgramação de Computadores - I. Profª Beatriz Profº Israel
Programação de Computadores - I Profª Beatriz Profº Israel As 52 Palavras Reservadas O que são palavras reservadas São palavras que já existem na linguagem Java, e tem sua função já definida. NÃO podem
Leia maisOrientação a Objetos
1. Domínio e Aplicação Orientação a Objetos Um domínio é composto pelas entidades, informações e processos relacionados a um determinado contexto. Uma aplicação pode ser desenvolvida para automatizar ou
Leia maisJava. Marcio de Carvalho Victorino www.dominandoti.eng.br
Java Marcio de Carvalho Victorino www.dominandoti.eng.br 3. Considere as instruções Java abaixo: int cont1 = 3; int cont2 = 2; int cont3 = 1; cont1 += cont3++; cont1 -= --cont2; cont3 = cont2++; Após a
Leia maisLinguagens de. Aula 02. Profa Cristiane Koehler cristiane.koehler@canoas.ifrs.edu.br
Linguagens de Programação III Aula 02 Profa Cristiane Koehler cristiane.koehler@canoas.ifrs.edu.br Linguagens de Programação Técnica de comunicação padronizada para enviar instruções a um computador. Assim
Leia maisCURSO DE PROGRAMAÇÃO EM JAVA
CURSO DE PROGRAMAÇÃO EM JAVA Introdução para Iniciantes Prof. M.Sc. Daniel Calife Índice 1 - A programação e a Linguagem Java. 1.1 1.2 1.3 1.4 Linguagens de Programação Java JDK IDE 2 - Criando o primeiro
Leia maisUniversidade da Beira Interior Cursos: Matemática /Informática e Ensino da Informática
Folha 1-1 Introdução à Linguagem de Programação JAVA 1 Usando o editor do ambiente de desenvolvimento JBUILDER pretende-se construir e executar o programa abaixo. class Primeiro { public static void main(string[]
Leia maisAula 09 Introdução à Java. Disciplina: Fundamentos de Lógica e Algoritmos Prof. Bruno Gomes http://www.profbrunogomes.com.br/
Aula 09 Introdução à Java Disciplina: Fundamentos de Lógica e Algoritmos Prof. Bruno Gomes http://www.profbrunogomes.com.br/ Agenda da Aula Java: Sintaxe; Tipos de Dados; Variáveis; Impressão de Dados.
Leia maisJava 2 Standard Edition Como criar classes e objetos
Java 2 Standard Edition Como criar classes e objetos Helder da Rocha www.argonavis.com.br 1 Assuntos abordados Este módulo explora detalhes da construção de classes e objetos Construtores Implicações da
Leia maisProgramação de Computadores - I. Profª Beatriz Profº Israel
Programação de Computadores - I Profª Beatriz Profº Israel Ambiente de Desenvolvimento Orientação a Objetos É uma técnica de desenvolvimento de softwares que consiste em representar os elementos do mundo
Leia maisIntrodução a Java. Hélder Nunes
Introdução a Java Hélder Nunes 2 Exercício de Fixação Os 4 elementos básicos da OO são os objetos, as classes, os atributos e os métodos. A orientação a objetos consiste em considerar os sistemas computacionais
Leia mais3 Classes e instanciação de objectos (em Java)
3 Classes e instanciação de objectos (em Java) Suponhamos que queremos criar uma classe que especifique a estrutura e o comportamento de objectos do tipo Contador. As instâncias da classe Contador devem
Leia maisARRAYS. Um array é um OBJETO que referencia (aponta) mais de um objeto ou armazena mais de um dado primitivo.
Cursos: Análise, Ciência da Computação e Sistemas de Informação Programação I - Prof. Aníbal Notas de aula 8 ARRAYS Introdução Até agora, utilizamos variáveis individuais. Significa que uma variável objeto
Leia maisIntrodução à Programação
Introdução à Programação Introdução a Linguagem C Construções Básicas Programa em C #include int main ( ) { Palavras Reservadas } float celsius ; float farenheit ; celsius = 30; farenheit = 9.0/5
Leia maisCurso Adonai QUESTÕES Disciplina Linguagem JAVA
1) Qual será o valor da string c, caso o programa rode com a seguinte linha de comando? > java Teste um dois tres public class Teste { public static void main(string[] args) { String a = args[0]; String
Leia maisProgramação Orientada a Objetos
Programação Orientada a Objetos Engenharia da Computação Professor: Rosalvo Ferreira de Oliveira Neto Dados Pessoais Rosalvo Ferreira de Oliveira Neto MSc. em ciência da computação (UFPE) rosalvo.oliveira@univasf.edu.br
Leia maisCAPÍTULO 3 - TIPOS DE DADOS E IDENTIFICADORES
CAPÍTULO 3 - TIPOS DE DADOS E IDENTIFICADORES 3.1 - IDENTIFICADORES Os objetos que usamos no nosso algoritmo são uma representação simbólica de um valor de dado. Assim, quando executamos a seguinte instrução:
Leia maisAlgoritmos e Programação (Prática) Profa. Andreza Leite andreza.leite@univasf.edu.br
(Prática) Profa. Andreza Leite andreza.leite@univasf.edu.br Introdução O computador como ferramenta indispensável: Faz parte das nossas vidas; Por si só não faz nada de útil; Grande capacidade de resolução
Leia maisJSP - ORIENTADO A OBJETOS
JSP Orientação a Objetos... 2 CLASSE:... 2 MÉTODOS:... 2 Método de Retorno... 2 Método de Execução... 2 Tipos de Dados... 3 Boolean... 3 Float... 3 Integer... 4 String... 4 Array... 4 Primeira:... 4 Segunda:...
Leia maisEXERCÍCIOS SOBRE ORIENTAÇÃO A OBJETOS
Campus Cachoeiro de Itapemirim Curso Técnico em Informática Disciplina: Análise e Projeto de Sistemas Professor: Rafael Vargas Mesquita Este exercício deve ser manuscrito e entregue na próxima aula; Valor
Leia maisProgramação por Objectos. Java
Programação por Objectos Java Parte 2: Classes e objectos LEEC@IST Java 1/24 Classes (1) Sintaxe Qualif* class Ident [ extends IdentC] [ implements IdentI [,IdentI]* ] { [ Atributos Métodos ]* Qualif:
Leia maisATRIBUTOS PRIVADOS 6. ENCAPSULAMENTO MÉTODOS PRIVADOS MÉTODOS PRIVADOS
ATRIBUTOS PRIVADOS Podemos usar o modificador private, para tornar um atributo privado, obtendo um controle centralizado Definimos métodos para implementar todas as lógicas que utilizam ou modificam o
Leia maisAlgoritmos I Aula 13 Java: Tipos básicos, variáveis, atribuições e expressões
Algoritmos I Aula 13 Java: Tipos básicos, variáveis, atribuições e expressões Professor: Max Pereira http://paginas.unisul.br/max.pereira Ciência da Computação Primeiro Programa em Java public class OlaPessoal
Leia maisAula 2. Objetivos Conceitos; Instalação do Text Pad; Entendendo o código java do AloMundo1 Codificação do AloMundo2,AloMundo3 e AloMundo4.
Aula 2 Objetivos Conceitos; Instalação do Text Pad; Entendendo o código java do AloMundo1 Codificação do AloMundo2,AloMundo3 e AloMundo4. Conceitos O software controla os computadores(freqüentemente conhecido
Leia maisAULA 02. 1. Uma linguagem de programação orientada a objetos
AULA 02 TECNOLOGIA JAVA O nome "Java" é usado para referir-se a 1. Uma linguagem de programação orientada a objetos 2. Uma coleção de APIs (classes, componentes, frameworks) para o desenvolvimento de aplicações
Leia mais2 Orientação a objetos na prática
2 Orientação a objetos na prática Aula 04 Sumário Capítulo 1 Introdução e conceitos básicos 1.4 Orientação a Objetos 1.4.1 Classe 1.4.2 Objetos 1.4.3 Métodos e atributos 1.4.4 Encapsulamento 1.4.5 Métodos
Leia maisFBV - Linguagem de Programação II. Um pouco sobre Java
FBV - Linguagem de Programação II Um pouco sobre Java História 1992: um grupo de engenheiros da Sun Microsystems desenvolve uma linguagem para pequenos dispositivos, batizada de Oak Desenvolvida com base
Leia maisProgramação Orientada a Objetos Prof. Rone Ilídio UFSJ/CAP
Programação Orientada a Objetos Prof. Rone Ilídio UFSJ/CAP 1) Introdução Programação Orientada a Objetos é um paradigma de programação bastante antigo. Entretanto somente nos últimos anos foi aceito realmente
Leia maisJavaScript 2.0X 1.0 3.0X 1.1 4.0 4.05 1.2 4.06 4.61 1.3 5.0 1.4 6.0 1.5
JavaScript Diego R. Frank, Leonardo Seibt FIT Faculdades de Informática de Taquara Fundação Educacional Encosta Inferior do Nordeste Av. Oscar Martins Rangel, 4500 Taquara RS Brasil difrank@terra.com.br,
Leia maisAnálise e Projeto Orientados por Objetos
Análise e Projeto Orientados por Objetos Aula 01 Orientação a Objetos Edirlei Soares de Lima Paradigmas de Programação Um paradigma de programação consiste na filosofia adotada na
Leia maisSobre o Professor Dr. Sylvio Barbon Junior
5COP088 Laboratório de Programação Aula 1 Java Prof. Dr. Sylvio Barbon Junior Sylvio Barbon Jr barbon@uel.br 1 Sobre o Professor Dr. Sylvio Barbon Junior Formação: Ciência e Engenharia da Computação (2005
Leia maisEspecialização em desenvolvimento para web com interfaces ricas. Tratamento de exceções em Java Prof. Fabrízzio A. A. M. N. Soares
Especialização em desenvolvimento para web com interfaces ricas Tratamento de exceções em Java Prof. Fabrízzio A. A. M. N. Soares Objetivos Conceito de exceções Tratar exceções pelo uso de try, catch e
Leia maisAula 2. Objetivos. Encapsulamento na linguagem Java; Utilizando a referência this.
Aula 2 Objetivos Encapsulamento na linguagem Java; Utilizando a referência this. Encapsulamento, data hiding é um conceito bastante importante em orientação a objetos. É utilizado para restringir o acesso
Leia maisConceitos de Linguagens de Programação
Conceitos de Linguagens de Programação Aula 07 Nomes, Vinculações, Escopos e Tipos de Dados Edirlei Soares de Lima Introdução Linguagens de programação imperativas são abstrações
Leia maisTécnicas de Programação II
Técnicas de Programação II Aula 06 Orientação a Objetos e Classes Edirlei Soares de Lima Orientação a Objetos O ser humano se relaciona com o mundo através do conceito de objetos.
Leia maisA Linguagem Java. Alberto Costa Neto DComp - UFS
A Linguagem Java Alberto Costa Neto DComp - UFS 1 Roteiro Comentários Variáveis Tipos Primitivos de Dados Casting Comandos de Entrada e Saída Operadores Constantes 2 Comentários /** Classe para impressão
Leia maisCONCEITOS DE LINGUAGEM DE PROGRAMAÇÃO CARACTERÍSTICAS. João Gabriel Ganem Barbosa
CONCEITOS DE LINGUAGEM DE PROGRAMAÇÃO CARACTERÍSTICAS João Gabriel Ganem Barbosa Sumário Motivação História Linha do Tempo Divisão Conceitos Paradigmas Geração Tipos de Dados Operadores Estruturada vs
Leia maisLista de Contas: Assinatura. Lista de Contas. Listas de Contas: Descrição. Listas de Contas: Descrição. Listas de Contas: Descrição
Lista de Contas Lista de Contas: Assinatura null Quais são os métodos necessários? class ListaDeContas { void inserir (Conta c) { void retirar (Conta c) { Conta procurar (String num) { Listas de Contas:
Leia maisUML Aspectos de projetos em Diagramas de classes
UML Aspectos de projetos em Diagramas de classes Após ser definido o contexto da aplicação a ser gerada. Devemos pensar em detalhar o Diagrama de Classes com informações visando uma implementação Orientada
Leia maisCriar a classe Aula.java com o seguinte código: Compilar e Executar
Introdução à Java Prof. Bruno Gomes bruno.gomes@ifrn.edu.br Programação Orientada a Objetos Código Exemplo da Aula Criar a classe Aula.java com o seguinte código: public class Aula { public static void
Leia maisDiagrama de Classes. Um diagrama de classes descreve a visão estática do sistema em termos de classes e relacionamentos entre as classes.
1 Diagrama de Classes Um diagrama de classes descreve a visão estática do sistema em termos de classes e relacionamentos entre as classes. Um dos objetivos do diagrama de classes é definir a base para
Leia maisAnálise e Desenvolvimento de Sistemas ADS Programação Orientada a Obejeto POO 3º Semestre AULA 03 - INTRODUÇÃO À PROGRAMAÇÃO ORIENTADA A OBJETO (POO)
Análise e Desenvolvimento de Sistemas ADS Programação Orientada a Obejeto POO 3º Semestre AULA 03 - INTRODUÇÃO À PROGRAMAÇÃO ORIENTADA A OBJETO (POO) Parte: 1 Prof. Cristóvão Cunha Objetivos de aprendizagem
Leia maisOrganização de programas em Java. Vanessa Braganholo vanessa@ic.uff.br
Organização de programas em Java Vanessa Braganholo vanessa@ic.uff.br Vamos programar em Java! Mas... } Como um programa é organizado? } Quais são os tipos de dados disponíveis? } Como variáveis podem
Leia maisIntrodução ao Paradigma Orientado a Objetos. Principais conceitos
Introdução ao Paradigma Orientado a Objetos Principais conceitos Paradigmas de Programação PROGRAMAÇÃO ESTRUTURADA X PROGRAMAÇÃO ORIENTADA A OBJETOS Paradigma Programação estruturada Na programação estrutura
Leia maisSintaxe Geral Tipos de Dados. Prof. Angelo Augusto Frozza, M.Sc.
Sintaxe Geral Tipos de Dados Comentários Comentários: De linha: // comentário 1 // comentário 2 De bloco: /* linha 1 linha 2 linha n */ De documentação: /** linha1 * linha2 */ Programa Exemplo: ExemploComentario.java
Leia maisMC102 Algoritmos e programação de computadores Aula 3: Variáveis
MC102 Algoritmos e programação de computadores Aula 3: Variáveis Variáveis Variáveis são locais onde armazenamos valores na memória. Toda variável é caracterizada por um nome, que a identifica em um programa,
Leia maisESQUEMA AULA PRÁTICA 1 Familiarização com o Ambiente de Desenvolvimento NetBeans Introdução à Linguagem de Programação JAVA
P. Fazendeiro & P. Prata POO FP1/1 ESQUEMA AULA PRÁTICA 1 Familiarização com o Ambiente de Desenvolvimento NetBeans Introdução à Linguagem de Programação JAVA 0 Iniciar o ambiente de desenvolvimento integrado
Leia maisAlgoritmia e Programação APROG. Linguagem JAVA. Básico. Nelson Freire (ISEP DEI-APROG 2012/13) 1/31
APROG Algoritmia e Programação Linguagem JAVA Básico Nelson Freire (ISEP DEI-APROG 2012/13) 1/31 Linguagem Java Estrutura de um Programa Geral Básica Estruturas de Dados Variáveis Constantes Tipos de Dados
Leia maisPROGRAMAÇÃO ORIENTADA A OBJETOS -TRATAMENTO DE EXCEÇÕES. Prof. Angelo Augusto Frozza, M.Sc. frozza@ifc-camboriu.edu.br
PROGRAMAÇÃO ORIENTADA A OBJETOS -TRATAMENTO DE EXCEÇÕES Prof. Angelo Augusto Frozza, M.Sc. frozza@ifc-camboriu.edu.br ROTEIRO 5. Tratamento de Exceções Introdução e conceitos Capturando exceção usando
Leia maisIntrodução à Linguagem Java
Introdução à Linguagem Java Histórico: Início da década de 90. Pequeno grupo de projetos da Sun Microsystems, denominado Green. Criar uma nova geração de computadores portáveis, capazes de se comunicar
Leia maisIntrodução a Linguagem
Introdução a Linguagem Prof. Edwar Saliba Júnior Fevereiro de 2011 Unidade 03 Introdução a Linguagem Java 1 Conteúdo Máquina Virtual (JVM) Histórico de Java Case Sensitive Tipos Primitivos Tipo String
Leia maisLição 9 Trabalhando com bibliotecas de classes
Lição 9 Trabalhando com bibliotecas de classes Introdução à Programação I 1 Objetivos Ao final da lição, o estudante deverá estar apto a: Explicar o que é programação orientada a objetos e alguns de seus
Leia maisMódulo 06 Desenho de Classes
Módulo 06 Desenho de Classes Última Atualização: 13/06/2010 1 Objetivos Definir os conceitos de herança, polimorfismo, sobrecarga (overloading), sobreescrita(overriding) e invocação virtual de métodos.
Leia maisSintaxe Básica de Java Parte 1
Sintaxe Básica de Java Parte 1 Universidade Católica de Pernambuco Ciência da Computação Prof. Márcio Bueno poonoite@marcioubeno.com Fonte: Material da Profª Karina Oliveira Estrutura de Programa Um programa
Leia maisTabela de Símbolos. Análise Semântica A Tabela de Símbolos. Principais Operações. Estrutura da Tabela de Símbolos. Declarações 11/6/2008
Tabela de Símbolos Análise Semântica A Tabela de Símbolos Fabiano Baldo Após a árvore de derivação, a tabela de símbolos é o principal atributo herdado em um compilador. É possível, mas não necessário,
Leia maisAULA 4 VISÃO BÁSICA DE CLASSES EM PHP
AULA 4 VISÃO BÁSICA DE CLASSES EM PHP Antes de mais nada, vamos conhecer alguns conceitos, que serão importantes para o entendimento mais efetivos dos assuntos que trataremos durante a leitura desta apostila.
Leia maisSlide 1 Deitel/Deitel, 8e. Java Como programar Copyright 2010 Pearson Education
Java Como Programar, 8/E Slide 1 Slide 2 Slide 3 Métodos genéricos e classes genéricas (e interfaces) permitem especificar, com uma única declaração de método, um conjunto de métodos relacionados ou, com
Leia maisCurso de Aprendizado Industrial Desenvolvedor WEB
Curso de Aprendizado Industrial Desenvolvedor WEB Disciplina: Programação Orientada a Objetos II Professor: Cheli dos S. Mendes da Costa Servidor de Aplicações WEB Tomcat Servidor Tomcat Foi desenvolvido
Leia maisUnidade IV: Ponteiros, Referências e Arrays
Programação com OO Acesso em Java a BD Curso: Técnico em Informática Campus: Ipanguaçu José Maria Monteiro Pontifícia Universidade Católica do Rio de Janeiro PUC-Rio Departamento Clayton Maciel de Informática
Leia maisLP II Estrutura de Dados. Introdução e Linguagem C. Prof. José Honorato F. Nunes honorato.nunes@ifbaiano.bonfim.edu.br
LP II Estrutura de Dados Introdução e Linguagem C Prof. José Honorato F. Nunes honorato.nunes@ifbaiano.bonfim.edu.br Resumo da aula Considerações Gerais Introdução a Linguagem C Variáveis e C Tipos de
Leia maisCapítulo 14. Herança a e Polimorfismo. Rui Rossi dos Santos Programação de Computadores em Java Editora NovaTerra
Capítulo 14 Herança a e Polimorfismo Objetivos do Capítulo Apresentar os conceitos de herança e de polimorfismo. Explorar os diversos recursos disponíveis no Java para a aplicação da herança e do polimorfismo
Leia mais2. OPERADORES... 6 3. ALGORITMOS, FLUXOGRAMAS E PROGRAMAS... 8 4. FUNÇÕES... 10
1. TIPOS DE DADOS... 3 1.1 DEFINIÇÃO DE DADOS... 3 1.2 - DEFINIÇÃO DE VARIÁVEIS... 3 1.3 - VARIÁVEIS EM C... 3 1.3.1. NOME DAS VARIÁVEIS... 3 1.3.2 - TIPOS BÁSICOS... 3 1.3.3 DECLARAÇÃO DE VARIÁVEIS...
Leia maisUniversidade da Beira Interior Cursos: Engenharia Informática, Matemática /Informática e Ensino da Informática
Programação Orientada a Objectos - 28/29; P. Prata, P. Fazendeiro 2 A tecnologia Java Uma ideia base da linguagem JAVA é a de que um programa em JAVA deve poder ser executado em qualquer tipo de computador
Leia maisArquitetura de Rede de Computadores
TCP/IP Roteamento Arquitetura de Rede de Prof. Pedro Neto Aracaju Sergipe - 2011 Ementa da Disciplina 4. Roteamento i. Máscara de Rede ii. Sub-Redes iii. Números Binários e Máscara de Sub-Rede iv. O Roteador
Leia maisEngenharia de Software III
Engenharia de Software III Casos de uso http://dl.dropbox.com/u/3025380/es3/aula6.pdf (flavio.ceci@unisul.br) 09/09/2010 O que são casos de uso? Um caso de uso procura documentar as ações necessárias,
Leia maisPrototype, um Design Patterns de Criação
Prototype, um Design Patterns de Criação José Anízio Pantoja Maia Este artigo tem como finalidade compreender o funcionamento do padrão de projeto prototype, serão abordados os participantes que compõe
Leia maisESQUEMA AULA PRÁTICA 1 Familiarização com o Ambiente de Desenvolvimento Eclipse Introdução à Linguagem de Programação JAVA
P. Fazendeiro & P. Prata POO FP1/1 ESQUEMA AULA PRÁTICA 1 Familiarização com o Ambiente de Desenvolvimento Eclipse Introdução à Linguagem de Programação JAVA 0 Inicie o ambiente de desenvolvimento integrado
Leia maisPOO Programação Orientada a Objetos. Classes em Java
+ POO Programação Orientada a Objetos Classes em Java + Classes 2 Para que a JVM crie objetos: Ela precisa saber qual classe o objeto pertence Na classe estão definidos os atributos e métodos Programamos
Leia maisDEFINIÇÃO DE MÉTODOS
Cursos: Análise, Ciência da Computação e Sistemas de Informação Programação I - Prof. Aníbal Notas de aula 2 DEFINIÇÃO DE MÉTODOS Todo o processamento que um programa Java faz está definido dentro dos
Leia mais1.6. Tratamento de Exceções
Paradigmas de Linguagens I 1 1.6. Tratamento de Exceções Uma exceção denota um comportamento anormal, indesejado, que ocorre raramente e requer alguma ação imediata em uma parte do programa [GHE 97, DER
Leia maisProgramação Estruturada e Orientada a Objetos. Fundamentos Orientação a Objetos
Programação Estruturada e Orientada a Objetos Fundamentos Orientação a Objetos 2013 O que veremos hoje? Introdução aos fundamentos de Orientação a Objetos Transparências baseadas no material do Prof. Jailton
Leia maisComponentes da linguagem C++
Componentes da linguagem C++ C++ é uma linguagem de programação orientada a objetos (OO) que oferece suporte às características OO, além de permitir você realizar outras tarefas, similarmente a outras
Leia mais1. Apresentação. 1.1. Objetivos
1.1. Objetivos 1. Apresentação Neste capítulo estão descritos os objetivos gerais do livro, os requisitos desejáveis do estudante para que possa utilizá-lo eficientemente, e os recursos necessários em
Leia maisReuso com Herança a e Composiçã
Java 2 Standard Edition Reuso com Herança a e Composiçã ção Helder da Rocha www.argonavis.com.br 1 Como aumentar as chances de reuso Separar as partes que podem mudar das partes que não mudam. Exemplo:
Leia maisJava - Introdução. Professor: Vilson Heck Junior. vilson.junior@ifsc.edu.br
Java - Introdução Professor: Vilson Heck Junior vilson.junior@ifsc.edu.br Agenda O que é Java? Sun / Oracle. IDE - NetBeans. Linguagem Java; Maquina Virtual; Atividade Prática. Identificando Elementos
Leia maisMemória Flash. PdP. Autor: Tiago Lone Nível: Básico Criação: 11/12/2005 Última versão: 18/12/2006. Pesquisa e Desenvolvimento de Produtos
TUTORIAL Memória Flash Autor: Tiago Lone Nível: Básico Criação: 11/12/2005 Última versão: 18/12/2006 PdP Pesquisa e Desenvolvimento de Produtos http://www.maxwellbohr.com.br contato@maxwellbohr.com.br
Leia maisProgramação Orientada a Objetos Herança Técnico em Informática. Prof. Marcos André Pisching, M.Sc.
Herança Técnico em Informática, M.Sc. Herança 2 Herança Reutilização de código Exemplo Banco: Um banco oferece diversos serviços que podem ser contratados individualmente pelos clientes. Quando um serviço
Leia maisEMENTA DO CURSO. Tópicos:
EMENTA DO CURSO O Curso Preparatório para a Certificação Oracle Certified Professional, Java SE 6 Programmer (Java Básico) será dividido em 2 módulos e deverá ter os seguintes objetivos e conter os seguintes
Leia mais1. NÍVEL CONVENCIONAL DE MÁQUINA
1. NÍVEL CONVENCIONAL DE MÁQUINA Relembrando a nossa matéria de Arquitetura de Computadores, a arquitetura de Computadores se divide em vários níveis como já estudamos anteriormente. Ou seja: o Nível 0
Leia maisBSI UFRPE Prof. Gustavo Callou gcallou@gmail.com
BSI UFRPE Prof. Gustavo Callou gcallou@gmail.com HelloWorld.java: public class HelloWorld { public static void main (String[] args) { System.out.println( Hello, World ); } } Identificadores são usados
Leia maisResumo da Matéria de Linguagem de Programação. Linguagem C
Resumo da Matéria de Linguagem de Programação Linguagem C Vitor H. Migoto de Gouvêa 2011 Sumário Como instalar um programa para executar o C...3 Sintaxe inicial da Linguagem de Programação C...4 Variáveis
Leia maisLaboratório de Computação VI JAVA IDL. Fabricio Aparecido Breve - 981648-9
Laboratório de Computação VI JAVA IDL Fabricio Aparecido Breve - 981648-9 O que é Java IDL? Java IDL é uma tecnologia para objetos distribuídos, ou seja, objetos em diferentes plataformas interagindo através
Leia maisModelagemde Software Orientadaa Objetos com UML
Modelagemde Software Orientadaa Objetos com UML André Maués Brabo Pereira Departamento de Engenharia Civil Universidade Federal Fluminense Colaborando para a disciplina CIV 2802 Sistemas Gráficos para
Leia maisCurso de Java. Orientação a objetos e a Linguagem JAVA. TodososdireitosreservadosKlais
Curso de Java Orientação a objetos e a Linguagem JAVA Roteiro A linguagem Java e a máquina virtual Objetos e Classes Encapsulamento, Herança e Polimorfismo Primeiro Exemplo A Linguagem JAVA Principais
Leia maisINTRODUÇÃO AO C++ SISTEMAS DE INFORMAÇÃO DR. EDNALDO B. PIZZOLATO
INTRODUÇÃO AO C++ SISTEMAS DE INFORMAÇÃO DR. EDNALDO B. PIZZOLATO Tópicos Estrutura Básica B de Programas C e C++ Tipos de Dados Variáveis Strings Entrada e Saída de Dados no C e C++ INTRODUÇÃO O C++ aceita
Leia maisAula 4 Pseudocódigo Tipos de Dados, Expressões e Variáveis
1. TIPOS DE DADOS Todo o trabalho realizado por um computador é baseado na manipulação das informações contidas em sua memória. Estas informações podem ser classificadas em dois tipos: As instruções, que
Leia maisIntrodução. à Linguagem JAVA. Prof. Dr. Jesus, Edison O. Instituto de Matemática e Computação. Laboratório de Visão Computacional
Introdução à Linguagem JAVA Prof. Dr. Jesus, Edison O. Instituto de Matemática e Computação Laboratório de Visão Computacional Vantagens do Java Independência de plataforma; Sintaxe semelhante às linguagens
Leia maisImplementando uma Classe e Criando Objetos a partir dela
Análise e Desenvolvimento de Sistemas ADS Programação Orientada a Obejeto POO 3º Semestre AULA 04 - INTRODUÇÃO À PROGRAMAÇÃO ORIENTADA A OBJETO (POO) Parte: 2 Prof. Cristóvão Cunha Implementando uma Classe
Leia maisProgramação Orientada a Objetos
Programação Orientada a Objetos Universidade Católica de Pernambuco Ciência da Computação Prof. Márcio Bueno poonoite@marciobueno.com Fonte: Material da Profª Karina Oliveira Introdução ao Paradigma OO
Leia maisLógica de Programação
Lógica de Programação Unidade 4 Ambiente de desenvolvimento Java QI ESCOLAS E FACULDADES Curso Técnico em Informática SUMÁRIO A LINGUAGEM JAVA... 3 JVM, JRE, JDK... 3 BYTECODE... 3 PREPARANDO O AMBIENTE
Leia maisVIII. VARIÁVEIS. Tabela I ARQUITETURA DA MEMÓRIA. 0x0000 0x34 0x0001 0xB0 0x0002 0x23. 0xFFFF 0x00
Fundamentos da Programação 32 A. Conceito Variáveis contém dados: VIII. VARIÁVEIS de entrada que o computador precisa manipular; de saída que o computador precisa imprimir; e temporários, utilizados de
Leia mais2 Diagrama de Caso de Uso
Unified Modeling Language (UML) Universidade Federal do Maranhão UFMA Pós Graduação de Engenharia de Eletricidade Grupo de Computação Assunto: Diagrama de Caso de Uso (Use Case) Autoria:Aristófanes Corrêa
Leia maisA Linguagem Algorítmica Estrutura de Repetição. Ex. 2
Estrutura de Repetição. Ex. 2 A ESTRUTURA Enquanto faça{} É MELHOR UTILIZADA PARA SITUAÇÕES ONDE O TESTE DE CONDIÇÃO (V OU F) PRECISA SER VERIFICADO NO INÍCIO DA ESTRUTURA DE REPETIÇÃO.
Leia maisProfessor: Macêdo Firmino Disciplina: Sistemas Operacionais de Rede
Professor: Macêdo Firmino Disciplina: Sistemas Operacionais de Rede O sistema de nome de domínio (DNS) é um sistema que nomeia computadores e serviços de rede e é organizado em uma hierarquia de domínios.
Leia mais3.1 Definições Uma classe é a descrição de um tipo de objeto.
Unified Modeling Language (UML) Universidade Federal do Maranhão UFMA Pós Graduação de Engenharia de Eletricidade Grupo de Computação Assunto: Diagrama de Classes Autoria:Aristófanes Corrêa Silva Adaptação:
Leia maisJava para Desktop. Classes Utilitárias I java.lang.string JSE
Java para Desktop Classes Utilitárias I java.lang.string JSE A classe String representa caracteres strings. Todos os caracteres literais no Java são representados por esta classe. Podemos criar uma String
Leia maisChamadas Remotas de Procedimentos (RPC) O Conceito de Procedimentos. RPC: Programa Distribuído. RPC: Modelo de Execução
Chamadas Remotas de Chamada Remota de Procedimento (RPC) ou Chamada de Função ou Chamada de Subrotina Método de transferência de controle de parte de um processo para outra parte Procedimentos => permite
Leia maisCurso: Técnico de Informática Disciplina: Redes de Computadores. 1- Apresentação Binária
1- Apresentação Binária Os computadores funcionam e armazenam dados mediante a utilização de chaves eletrônicas que são LIGADAS ou DESLIGADAS. Os computadores só entendem e utilizam dados existentes neste
Leia mais