Escolar Documentos
Profissional Documentos
Cultura Documentos
Apresentação .....................................................................................................8
Comentários .....................................................................................................31
Prática ...............................................................................................................36
Blocos
Controle for
Controle break
Controle continue
Controle return
Capítulo 9 - Arrays
Classe String
Classe StringBuffer
Classe Arrays
Classe System
Classe Runtime
G etters e Setters
Checkers e Converters
Modificadores de acesso
Criando construtores
Sobrecarga de construtores
Método finalize
Atributo static
Método static
Associação (simples)
Agregação
Composição
Herança (Generalização)
Capítulo 16 - Pacotes
Membros de um pacote
Declarações de pacotes
Criando um j ar
Introdução
Capítulo 19 - Coleções
O Framework Collections
j ava.util .Collection
java.util.List
j ava.util Set .
j ava.util .Map
Drivers e URLs
Para a plataforma Java, porém, algumas características inerentes a ela dificultam o indivíduo
autodidata em evoluir nos estudos, entre algumas, podemos destacar:
Com essa linha de raciocínio, este livro está voltado não só a apresentar a sintaxe Java, iniciar na
programação orientada a objetos e indicar as principais APIs, mas também se preocupa em ensinar a
melhor forma de aprender e onde encontrar as melhores e mais completas informações relacionadas à
plataforma Java.
Com a evolução da tecnologia, tanto no desenvolvimento do hardware como no de aplicações, o
aumento de capacidades e recursos se torna inevitável e essencial.
Quando se trata de softwares, a satisfação dos usuários, bem como suas necessidades, não está
relacionada a métricas exatas, e sim a características como confiabilidade, manutenabilidade,
operabilidade, extensibilidade, escalabilidade, portabilidade entre outras. Essas características são
cada vez mais determinantes para a escolha do software a ser usado, e estão relacionadas
diretamente com a exigência dos usuários.
Por sua vez, para que os desenvolvedores possam fazer a melhor negociação nesse mercado de
oferta e procura, é necessário conhecer o que há de melhor em plataformas de desenvolvimento, bem
como as principais características que cada uma delas pode oferecer. Essas plataformas incluem:
• compiladores;
• servidores de aplicação;
• APIs e frameworks.
Para que se tenha uma idéia mais ampla das tecnologias e opções existentes no mercado é
necessário o conhecimento de diversos itens, em que se destacam a evolução tecnológica e suas
relações, os paradigmas de programação e os ambientes de desenvolvimento.
Paradigmas de programação
Paradigma Procedural
• extremamente técnica.
Na orientação a objetos, o programa é composto por objetos, com propriedades e operações que
podem ser executadas por eles, ou seja, a estrutura de dados é definida juntamente com as funções,
nesse caso chamadas de métodos, que poderão ser executadas.
Todas as funcionalidades e atributos de cada entidade do sistema são armazenados em classe que
representam esta entidade. Normalmente, é utilizada a Unified Modeling Language (UML) para
modelar soluções orientadas a objetos, podendo até utilizar ferramentas CASE para geração de
códigos.
Existem diversas linguagens que são associadas a algum ambiente de desenvolvimento, como é o
caso da:
• Java
• PHP
• C/C++
Interpretadas
Como o próprio nome diz, são interpretadas linha a linha em tempo de execução. Nessa categoria
normalmente o código é armazenado como texto puro sendo transformado em instruções apenas
quando são executados, dessa forma, os códigos são expostos a possíveis indivíduos mal-
intencionados.
Têm-se como exemplos de linguagens interpretadas: Perl, ASP (Active Server Pages), JavaScript,
PHP e Basic.
Compiladas
0 compilador traduz o programa fonte apenas uma vez para linguagem compilada (executável) não
importando quantas vezes o programa irá ser executado. No processo de compilação, o código fonte
é submetido á análise sintática, léxica e semântica. Caso algum erro seja encontrado, o arquivo
executável não é gerado, e os erros são apontados pelo compilador.
Muitos erros são eliminados durante o processo de compilação, como por exemplo os seguintes
erros sintáticos:
• caracteres inválidos;
Por outro lado, erros lógicos não são capturados no processo de compilação, gerando algum tipo
de erro apenas ao ser executado, como por exemplo:
Híbridas
As plataformas que implantam o conceito híbrido tem como objetivo a segurança das verificações
existentes em um processo de compilação e a portabilidade dos ambientes interpretados.
A plataforma Java utiliza essa abordagem, contendo os seguintes passos em seu processo de
desenvolvimento.
• os bytecodes (.class) são utilizados para executar a aplicação, sendo interpretados pela
Máquina Virtual Java (JVM).
Outros ambientes também adotam essa codificação intermediária por meio de bytecodes, como é
o caso do Microsoft.NET.
Com o Java, a Sun Microsystems foi responsável pela primeira linguagem de programação que
não estava amarrada a nenhum sistema operacional ou microprocessador em particular.
Nesse âmbito, será apresentada uma breve história da plataforma Java, seguida da apresentação
dos três principais segmentos, J2SE, J2EE e J2ME, sendo o primeiro mais detalhado, por ser o foco
desse livro.
Mitos da linguagem
Devido à grande fama e vasta utilização da plataforma Java, muitas questões e mitos surgem,
algumas com certa finalidade de denegrir a imagem, outras por simples receio de mudança. Dentre as
principais questões envolvidas, podem-se destacar as seguintes:
Java é da SUN?
A especificação Java foi criada pela SUN, no entanto, a linguagem é mantida pelo Java
Community Proccess (JCP) que reúne experts em Java, empresas e universidades que, por meio de
processos democráticos, definem a evolução da linguagem.
Java não é apenas uma linguagem direcionada para Web, apesar de atualmente ser bastante
conhecida e divulgada por seus dotes para desenvolvimento de aplicações Web, Java é uma
linguagem completa, como C++, Pascal e Basic.
Não. Java é compilada, JavaScript é interpretada pelo interpretador contido no browser. Java é
uma criação da SUN, JavaScript é uma criação da Netscape.
Java é lento?
0 Java, como aplicação stand alone, é mais lento que uma linguagem compilada com código
nativo (como a linguagem C, por exemplo), pois para ser portável não interage diretamente com o
servidor gráfico do sistema operacional. No entanto a afirmação de que Java é lento é completamente
falsa para softwares distribuídos (em servidores), em que bibliotecas gráficas não são necessárias
para gerar respostas aos usuários (Servlet, JSP e RMI).
Java 2 Standard Edition (J2SE)
A Figura 3.1 mostra uma representação em blocos da arquitetura da plataforma Java 2 SE versão
1.4, indicando as ferramentas e APIs pertencentes ao ambiente de execução JRE (Java Runtime
Environment) e as ferramentas do kit de desenvolvimento SDK (Software Development Kit).
Figura 3.1.
A Sun organiza as tecnologias J2SE em três grupos conceituais: Core Java, Desktop Java e J2SE
Embedded.
• Desktop Java: oferece um grupo de recursos para auxiliar a construção de aplicações desktop
que proporcionam ricas experiências aos usuários. Desktop Java consiste em produtos de
implantação (deploy) como o Java Plug-in, APIs de modelagem de componentes como o
JavaBeans, APIs de interface gráfica de usuário (GUI), como as Java Foundation Classes (JFC) e
Swing, e APIS multimídia, como o Java3D;
• J2SE Embedded: oferece recursos para programação destinada a pequenos dispositivos, a qual
é denominada computação embarcada.
Como as tecnologias Core Java e Desktop Java se relacionam?
As tecnologias do Core Java e Desktop Java são interdependentes: o Core Java provê a base para
segurança, conectividade a banco de dados, entre outros. Desktop Java permite escrever software
cliente altamente interativo e rico.
Como é distribuído?
A Sun distribui o J2SE como um kit de desenvolvimento (J2SE Development Kit - JDK) e um
ambiente de execução Java (Java Runtime Environment - JRE).
0 ambiente de execução do Java (Java Runtime Environment - JRE) provê bibliotecas, a Máquina
Virtual Java (JVM), e outros componentes para rodar applets e aplicações escritas na linguagem de
programação Java. Em acréscimo, duas tecnologias-chave de implantação fazem parte do JRE: Java
Plug-in, a qual permite que applets sejam executadas em navegadores populares, e o Java Web Start,
que permite a implantação de aplicações stand alone através da rede.
A Máquina Virtual Java não tem conhecimento algum da linguagem Java propriamente dita, ela
apenas conhece um formato binário particular, o formato de arquivo class. Um arquivo class contém
instruções para a Máquina Virtual Java (bytecodes) e uma tabela de símbolos, assim como outras
informações específicas.
0 kit de desenvolvimento da Plataforma Java contém o ambiente para executar aplicações Java
descrito na seção anterior (JRE), acrescido das ferramentas necessárias ao desenvolvimento Java,
incluindo:
• Javac: compilador dos códigos-fonte;
• Javah: ferramenta para gerar cabeçalho em C de uma classe. Usado com JNI;
• Rmic: compilador rmi usado para gerar stubs, skeletons e ties para objetos remotos;
• Rmid: inicia o sistema servidor de ativação que permite objetos remotos serem registrados.
Garbage Collector
Outro recurso fundamental do Java é o heap com coletor de lixos (garbage collector), que se
responsabiliza por liberar parte da memória alocada que não é mais referenciada. Por causa da
coleta de lixo do heap em Java, os programadores não precisam, e nem podem, liberar
explicitamente memória anteriormente alocada.
0 nome coleta de lixo implica que objetos que não são mais necessários para o programa em
execução são lixos e podem ser jogados fora. Uma outra metáfora para esse recurso é reciclagem de
memória. Quando um programa não mais referencia um objeto, o espaço no heap por ele ocupado
deve ser reciclado para que seja disponibilizado para novos objetos subseqüentes.
Primeiro, a coleta de lixo alivia os programadores da questão de liberar memória alocada, uma
vez que a liberação explícita de memória alocada pode ser um tanto quanto complicada.
Uma segunda vantagem da coleta de lixo é que facilita a garantia de integridade do programa. A
coleta de lixo é uma parte importante da estratégia de segurança da plataforma Java, uma vez que
programadores Java estão desabilitados a acidentalmente (ou propositalmente) causar uma falha na
JVM por meio de liberação incorreta de memória.
0 aprendizado de qualquer linguagem de programação envolve várias etapas e procedimentos que
devem ser tomados para facilitar, agilizar e melhorar a qualidade do ensino.
Como tudo começou com a Sun, é por meio dela que se encontram os melhores materiais e
ferramentas para o aprendizado e desenvolvimento em Java, sendo assim, serão adotados para este
curso os materiais e ferramentas fornecidos pela Sun.
Quando se trata da plataforma Java, qualquer material deve ser buscado inicialmente no site
mantido pela Sun para o Java, seguido dos sites de outras empresas e grupos abertos de
desenvolvimento que concentram grande parte dos materiais atuais. De uma forma geral, os sites a
seguir apresentam praticamente todo conteúdo essencial para começar a trabalhar com o Java:
Além de sites que concentram projetos variados. A seguir, alguns desenvolvidos em Java:
Uma das principais ajudas para quem trabalha com Java é a documentação gerada pelo Javadoc
para a API do Java. É essencial ter familiaridade com a apresentação do conteúdo, de maneira a
agilizar e facilitar as consultas que se tornam cada vez mais constantes ao se desenvolver em Java.
A API da última versão disponível (Java 1.5 ou Java 5) está disponível em:
Para baixar o SDK 1.5 e instruções para instalação, acesse respectivamente os sites a seguir:
fava -version
2. Algo semelhante com o texto a seguir deverá ser mostrado.
Principais IDEs
Para baixar o NetBeans 4.1 e também as instruções para instalação, acesse, respectivamente, os
sites a seguir:
1. Abra algum editor de texto simples e crie o arquivo OiMundoApplet.html com o seguinte
código:
A Linguagem Java
A linguagem de programação Java é relativamente de alto nível, uma vez que detalhes da
representação da máquina não ficam disponíveis por meio da linguagem. Ela inclui gerenciamento
automático de armazenamento, tipicamente usa coletor de lixo para evitar problemas com liberação
de memória (como no free do C e delete do C++).
Palavras reservadas
As seguintes seqüências de caracteres, formadas por letras ASCII, são reservadas para o uso
como palavras-chave e não podem ser utilizadas como identificadores:
As palavras-chave const e goto são reservadas, porém, não são utilizadas. Isso permite ao
compilador Java produzir melhores mensagens de erro caso essas palavras do C++ apareçam erradas
nos programas.
Enquanto true e false podem aparecer como palavras-chave, elas são tecnicamente literais
booleanos. Similarmente, o null é um literal nulo.
Convenções de código
• o nome de classes deve começar com letra maiúscula (Exemplo: class Pessoa);
• o nome de métodos, atributos e variáveis deve começar sempre com letra minúscula (Exemplo:
get () );
• o nome de constantes deve utilizar todas as letras maiúsculas (Exemplo: final int ERRO);
• separar nome composto com letras maiúsculas ao invés de caracteres especiais (Exemplo:
MinhaClasse e minhaVariável).
As variáveis locais e de parâmetros utilizando nomes curtos e abreviados para os tipos primitivos
iguais à primeira letra (Exemplo: byte b, char c, double d etc) e utilizando as primeiras letras de cada
palavra para classes com nomes compostos (Exemplo: DataInputStream dis).
Comentários
A seguir, dois tipos de comentários referentes aos tradicionais e aos de fim de linha:
2. // texto Um comentário de fim de linha: todo o texto a partir dos caracteres ASCII // até o final
da linha é ignorado (assim como no C++).
Uma variável é um local de armazenamento em memória que está associado a um tipo, algumas
vezes chamado de compile-time type, que pode ser um tipo primitivo ou uma referência. Uma
variável sempre contém um valor atribuído compatível com seu tipo. 0 valor de uma variável é
modificado por meio de uma atribuição (=) ou por operadores prefixados ou pós-fixados de
incremento (++) ou decremento (--).
Tipos primitivos
Uma variável de tipo primitivo sempre armazena um valor do seu tipo exato, realizando qualquer
conversão necessária antes da atribuição.
Esses são os casos do tipo byte (byte), dos números inteiros (short, int, long), dos pontos
flutuantes (float, double), dos caracteres (char) e do tipo booleano (boolean).
• para o tipo byte (1 byte), o valor padrão é zero, ou seja, valor de (byte) 0;
• para o tipo short (2 bytes), o valor padrão é zero, ou seja, valor de (short) 0;
• para o tipo float (4 bytes), o valor padrão é positivo zero, ou seja, O.Of;
• para o tipo double (8 bytes), o valor padrão é positivo zero, ou seja, 0.0d.
Caracteres
Booleanos
Referência (Reference)
A variável do tipo referência pode armazenar um dos seguintes valores:
2. Variável de instância: é um campo declarado em uma classe sem o uso da palavra-chave static.
Se uma classe T tem um campo a que é uma variável de instância, então uma nova variável de
instância a é criada e iniciada com um valor padrão como parte de cada objeto novo criado da classe
T ou de qualquer subclasse de T. A variável de instância deixa de existir quando o objeto associado
não é mais referenciado, e qualquer finalização do objeto tenha sido completada.
3. Componentes arrays: são variáveis sem nome que são criadas e iniciadas com valores padrões
sempre que um novo objeto que é um array é criado. Os componentes array deixam de existir quando
um array não é mais referenciado.
4. Parâmetros de métodos: nomeiam valores de argumentos passados para um método. Para todo
parâmetro declarado em um método, uma nova variável de parâmetro é criada toda vez que um
método é chamado. Uma nova variável é iniciada com o valor do argumento correspondente da
invocação do método. Um parâmetro de método deixa de existir quando a execução do corpo do
método tiver sido completada.
6. Parâmetro de tratamento de exceção: é criado cada vez que uma exceção é capturada por uma
cláusula catch de um try. A nova variável é iniciada com o objeto associado com a exceção que está
sendo tratada. 0 parâmetro de tratamento de exceção deixa de existir quando a execução do bloco
associado com a cláusula catch é completada.
7. Variáveis locais: são declaradas por comandos de declaração de variáveis. Sempre que um
controle de fluxo entra em um bloco ou comando for, uma nova variável é criada para cada variável
local declarada por meio de comandos de declarações imediatamente contido em um bloco ou
comando for. Uma declaração de variável local pode conter uma expressão que inicia a variável.
Uma variável local deixa de existir quando a execução de um bloco ou de um comando for tenha sido
completado.
Prática
Operadores unários
Os operadores unários incluem +, -, ++, --, _, ! , cast. Vale lembrar que, expressões compostas
com operadores unários são agrupados da direita para a esquerda, ou seja, -x é o mesmo que -(-x).
Operador de negação: !
0 tipo de expressão utilizada com o sinal unário ! deve ser do tipo boolean, ou será gerado um
erro de compilação, da mesma maneira que o resultado final da operação.
Em tempo de execução, o valor de uma expressão complementar lógica unária será true se o valor
do operador for false e false se o valor do operador for true.
Exemplo:
Uma expressão unária precedida pelo operador ++ é uma expressão de incremento prefixada. 0
resultado de uma expressão deste tipo deve ser do tipo numérico, ou um erro de compilação será
gerado. 0 tipo de expressão de incremento prefixado é do mesmo tipo de sua variável, sendo ele um
valor e não uma variável.
Uma variável declarada como final não pode ser incrementada, pois quando ela é utilizada em
uma expressão, a mesma é considerada como um valor e não variável, como é necessário para o uso
desse operador.
Exemplo:
0 tipo de expressão utilizada com o operador unário + deve ser do tipo primitivo numérico, caso
contrário um erro de compilação será gerado. Esse operador não tem muita utilidade como operador
unário, pois mantém o mesmo valor positivo inteiro, a única questão é que convertem variáveis do
tipo byte, short e char para o tipo int.
Por outro lado, o operador - também precisa de tipos primitivos numéricos, mas troca o sinal do
valor utilizado. Mais formalmente o operador de expressão menos (-) é a negação aritmética do valor
do operador utilizado.
Para valores inteiros, a negação é o mesmo que uma subtração de zero. E para todo valor inteiro
x, -x é igual a (-x) +1.
Para valores de ponto flutuante, a negação não é o mesmo que uma subtração de zero, isso porque
se x vale +0.0 , então 0.0-x é + 0.0, enquanto -x vale -0.0.
Exemplo:
Operador de inversão: -
0 tipo de expressão utilizada com o operador unário - deve ser de um tipo primitivo inteiro,
gerando erro caso contrário. Esse operador faz o complemento bit a bit do operando.
Como exemplo, para o número binário 00110101 (0x35) seu complemento será 11001010
(OxCA). Observe que, em todos os casos, -x é iguala (-x) -1.
Exemplo
Uma expressão cast converte, em tempo de execução, um valor de um tipo numérico para um
valor similar de outro tipo numérico. Caso se trate de um tipo boolean simplesmente confirmará o
seu tipo ou poderá gerar um erro, já que para variáveis do tipo referência, poderá converter para uma
classe compatível com a especificada pela referência atual.
Exemplos:
Operadores aritméticos: +, -, *, / e %
Soma e subtração: + e -
Os operadores + e - são chamados operadores aditivos. Eles têm a mesma precedência e são
sintaticamente associativos à esquerda (agrupam da esquerda para direita).
Vale lembrar que se um dos operandos do operador + for uma String, então o operador será um
concatenador de caracteres.
0 operador binário + realiza uma adição quando aplicado a dois operadores do tipo numérico e o
operador binário - realiza uma subtração nos operandos.
Exemplo:
Multiplicação e divisão: * e /
Exemplo:
Resto da divisão: %
0 operador binário é dito como o resto da divisão de seus operandos, sendo o da esquerda o
dividendo e o da direita o divisor.
Em C e C++, o operador de resto de divisão só aceita operandos inteiro, mas na linguagem Java
são aceitos também operandos pontos flutuantes.
Exemplo:
Eles são sintaticamente associativos à esquerda, ou seja, são agrupáveis da esquerda para a
direita. 0 operando à esquerda do sinal de deslocamento é o valor a sofrer a operação e o operando à
direita especifica a distância do deslocamento a ser realizado.
0 valor de n»s é o n com deslocamento à direita de s posições de bits com extensão de sinal
(considera bit mais significativo como sinal). Isso equivale a dividir por dois com potência de s.
Exemplo: n»4 = n / 24 = n / 16.
0 valor de n»>s é o n com deslocamento à direita de s posições de bits com extensão de zero
(considera bit mais significativo como bit normal). Se o valor de n for positivo, então o resultado é o
mesmo que n»s, se for negativo, o resultado é igual à expressão n»s)+(2«-s.
Exemplo:
• o operador < retorna true se o operando da esquerda tiver valor menor que o da direita e false
caso contrário;
• o operador <= retorna true se o operando da esquerda tiver valor menor ou igual ao da direita e
false caso contrário;
• o operador > retorna true se o operando da esquerda tiver valor maior que o da direita e false
caso contrário;
• o operador >= retorna true se o operando da esquerda tiver valor maior ou igual ao da direita e
false caso contrário;
• o operador == retorna true se o operando da esquerda tiver valor igual ao da direita e false caso
contrário;
• o operador != retorna true se o operando da esquerda tiver valor diferente que o da direita e
false caso contrário.
Vale ressaltar que a comparação de igualdade == e diferença != com operandos tipo String devem
ser utilizados de maneira cautelosa, pois fará a comparação da referência e não do conteúdo. Para
garantir a comparação do conteúdo, utilizar o método equals () da classe.
Exemplos:
0 tipo do operando ExpressãoRelacional da expressão deverá ser uma variável de tipo referência,
com valor de alguma instância de classe ou o valor nulo atribuído, caso contrário será gerado um
erro de compilação. 0 TipoReferência citado depois do operador deverá denotar um tipo de
referência, gerando um erro de compilação caso contrário.
Por outro lado, se a classe Point for uma subclasse de Element, como no código a seguir:
Os operadores bit a bit e os operadores lógicos incluem a operação AND &, OR inclusivo 1 e
OR exclusivo ^.
Esses operadores têm precedência diferente, com o & tendo maior precedência e o 1 com menor
precedência.
Cada um dos operadores são sintaticamente associativos à esquerda, agrupando da esquerda para
a direita. Os operadores são comutativos se os operandos não tiverem efeito de ordem.
Exemplo:
Operadores AND, OR e XOR inteiros bit a bit (&, I e ^)
Quando ambos operandos de um operador &, 1 ou ^ são de um tipo passível de conversão para
um tipo primitivo numérico, será realizada primeiramente uma promoção numérico binária nos
mesmos.
• para o &, o valor resultante é uma operação lógica AND entre os valores dos operandos;
• para o ^, o valor resultante é uma operação lógica XOR (exclusivo) entre os valores dos
operandos;
• para o I, o valor resultante é uma operação lógica OR (inclusivo) entre os valores dos
operandos.
Exemplos:
Quando ambos operandos de um operador &, 1 ou ^ são de um tipo boolean OU Boolean, então a
expressão de operação bit a bit será boolean, e não inteira.
• para &, o resultado será true se ambos operandos forem também true; caso contrário o resultado
é false;
• para ^, o resultado será true se os operandos forem diferentes, caso contrário o resultado é false;
• para I, o resultado será true se pelo menos um dos operandos for também true; caso contrário o
resultado é false.
Operadores condicionais
2. Com o operador &&, o operando da direita só será avaliado se o operando da esquerda for
verdadeiro (troe).
Operador OR (11)
2. Com o operador 11 o operando da direita só será avaliado se o operando da esquerda for falso
(false).
Operadores de atribuição:
Existem ao todo 12 operadores de atribuição, sendo todos associativos à direita, ou seja, são
avaliados da direita para a esquerda (Exemplo: a=b=c significa a= (b=c) ).
Os operadores de atribuição são divididos em dois grupos, atribuição simples com o único
operador =, e as atribuições compostas formadas pelos demais operadores no formato op=.
Atribuição simples (=)
0 operador de atribuição simples (=) associa o valor contido na expressão à direita do operador
na variável não final à esquerda do operador.
Um erro de compilação ocorre se o tipo do operando à direita não puder ser automaticamente
convertido para o tipo da variável à esquerda.
Uma expressão de atribuição composta da forma El op= E2 é equivalente a El = (T) (El op E2),
onde T é o tipo de El.
Exemplo:
0 operador condicional ternário ? : usa o valor booleano de uma expressão para decidir qual das
outras duas expressões serão avaliadas e utilizadas.
Exemplo:
A seqüência de execução de um programa é controlada por statements (ou comandos), os quais
são executados com determinados objetivos e não têm valores específicos.
Alguns statements contêm outros statements como parte de sua estrutura, assim como outros
comandos são subcomandos de um comando.
Se todos os passos são percorridos como descrito, sem a indicação de finalização bruta, o
statement é considerado como finalizado normalmente. Porém, alguns eventos podem interromper a
finalização normal:
• um comando break, continue, e return causa a transferência de controle de fluxo que interrompe
a finalização normal do statement que o contém;
• a avaliação de certo conjunto de expressões podem lançar exceções da Máquina Virtual Java,
além de uma chamada explícita ao comando throw, que também resulta em uma exceção.
Se um desses eventos ocorrer, então a execução de um ou mais comandos serão terminados antes
de todos os passos terem sido completados como normalmente, neste caso têm-se os statements
chamados de finalização bruta.
Blocos
Um bloco é executado por meio de cada uma das declarações locais de variáveis e demais
comandos do primeiro para o último (esquerda para direita). Se todos os comandos do bloco
terminam normalmente, então o bloco termina normalmente. Caso algum comando do bloco termine
brutalmente por qualquer razão, então o bloco termina da mesma forma pelo mesmo motivo.
A Expression deve ser do tipo boolean OU Boolean, ou será gerado um erro de compilação.
• se o valor for false, então nada mais é executado e o comando termina normalmente.
Controle switch
0 controle switch transfere o controle para um dos inúmeros statements, dependendo do valor de
uma expressão.
0 tipo da expressão deve ser char, byte, short, int, Character, Byte, Short, Integer, ou será gerado
um erro de compilação.
0 corpo de um controle switch é conhecido como bloco switch. Qualquer comando imediatamente
contido no bloco switch deve ser rotulado com um ou mais rótulos case ou default. Os rótulos (ou
label) são associados ao controle switch, assim como as expressões constantes seguidas a cada
rótulo case.
Todas as condições a seguir devem ser satisfeitas para que não seja gerado um erro de
compilação:
• toda expressão constante case de um switch associado a um controle deve conter o mesmo tipo
da expressão do comando switch;
• nenhum par de expressões constantes de um case associado a um mesmo controle switch poderá
ter o mesmo valor;
• pelo menos um rótulo default pode estar associado a um mesmo controle switch.
Exemplo:
Resultado com os breaks:
Controle while
0 controle while executa a expressão condicional e o statement repetidamente até que o valor da
expressão seja false.
Exemplo:
0 resultado será:
0 controle do while executa o statement e a expressão condicional repetidamente até que o valor
da expressão seja false.
0 resultado será:
Exemplo 2:
Controle for
1. Uma executa algum tipo de código de inicialização que é executado apenas uma vez.
2. Outra verifica a condição para a repetição, a qual é verificada no início de cada iteração.
3. E a última contém algum código de atualização da variável de controle, a qual será executada
no final de cada iteração.
Sintaxe:
Exemplo:
0 controle de laço de repetição for diferenciado é uma funcionalidade acrescida no Java 1.5 e é
apresentado como a seguir:
0 conteúdo de Expression deverá conter uma variável de referência com uma instância de alguma
subclasse de Iterable ou também poderá conter um tipo array, caso contrário ocorrerá um erro de
compilação.
Controle break
0 comando break transfere o controle de execução para fora do statement ao qual está associado.
Além disso, há dois tipos de break, um sem rótulo e outro rotulado.
0 comando break sem rótulo transfere o controle para o statement switch, while, do, ou for mais
próximo (ou interno) a ele. Se não existir nenhum statement switch, while, do, ou for no método
construtor ou iniciador mais próximo do comando break, será gerado um erro de compilação.
0 comando break rotulado com um identificador transfere o controle para o statement com o
mesmo identificador como rótulo. Esse comando, chamado de alvo do break, termina imediatamente
de maneira normal o bloco. Nesse caso o comando break não precisa estar apontado para um
comando while, do, for, ou switch, bastando apontar para um bloco que esteja dentro e que tenha o
mesmo rótulo.
Exemplo:
Controle continue
Um comando continue pode ser usado apenas em um controle de laço de repetição while, do, ou
for, os quais são também chamados de iteration statements. 0 controle é passado para o ponto de
continuação do loop de um controle de iteração, o qual se repetirá normalmente.
0 comando continue sem rótulo transfere o controle para o comando while, do, ou for mais
próximo (estando dentro), em um mesmo método ou bloco. Já o comando continue rotulado retorna o
controle para o laço com o mesmo rótulo, dessa forma a iteração corrente é imediatamente finalizada
e uma nova é iniciada.
Exemplo:
Controle return
0 comando return retorna o controle para a linha de comando que chamou o método ou construtor.
Um comando return sem expressão deve estar contido em um corpo de método que tenha sido
declarado usando a palavra-chave void, a qual não retorna nenhum valor, ou também de dentro de um
construtor.
Um comando return com expressão de retorno deve estar contido em um corpo de método que
tenha sido declarado com algum tipo de retorno, caso contrário retorna um erro de compilação. 0 tipo
do valor associado ao comando return deve ser do mesmo tipo da declaração do método, gerando um
erro em caso contrário.
Exemplo:
Na linguagem de programação Java os arrays são objetos dinamicamente criados, e que podem
ser associados a variáveis do tipo Object. Todos os métodos da classe Object podem ser invocados
por meio de um array.
Um objeto de array contém um número de variáveis, o qual pode ser zero, sendo chamado de
vetor vazio. As variáveis contidas nos vetores não têm nome, sendo referenciadas por expressões de
acesso a array que usam valores de índices inteiros e não negativos. Essas variáveis são chamadas
de componentes de um array, sendo que, se um array tem n componentes, é dito que n é o
comprimento (length) do array, e esses componentes são referenciados usando índices inteiros de 0 a
n-1, inclusive.
Exemplos:
Os [1 podem aparecer como parte do tipo, no começo da declaração ou como parte dos
declarantes para uma variável particular, ou em ambos, como no exemplo a seguir:
Exemplo:
0 resultado será:
São aqueles que possuem duas dimensões, como uma tabela com linhas e colunas.
Exemplo:
0 resultado será:
Seguido por uma exceção do tipo NullPointerException por tentar acessar o segundo componente
do array ia, cujo valor é uma referência nula.
Membros de um array
• o campo public final length, que contém o número de componentes de um array (pode ser 0 ou
outro valor positivo);
• o método public clone, o qual sobre-escreve o método de mesmo nome da classe Object e não
lança exceções de che cagem. 0 tipo de retorno de um método clone do tipo T[] será sempre T[].
Exemplo:
0 resultado será:
Classes utilitárias são aquelas que são normalmente encontradas na maioria dos programas em
Java e oferecem recursos e funcionalidades básicas de interação com o usuário.
Classe String
Instâncias da classe string representam seqüências de caracteres Unicode. Um objeto string possui
um valor constante (imodificável). Literais string são referências para instâncias da classe String.
A classe String é a única classe com tratamento especial, ao qual pode ser instanciada sem a
utilização da palavra reservada new. Quando se atribui um valor a uma variável do tipo string
usando apenas as a Máquina Virtual criará uma instância nova por meio de um comando new.
0 operador de concatenação de string + implica na criação de uma nova instância de objeto string
quando não for constante no momento da compilação.
Exemplo:
Principais métodos
Comparando string
Lembrando que String é uma classe, qualquer objeto (ou variável) que seja dessa classe conterá
uma referência para a instância, dessa forma a comparação deve ser realizada por meio do método
equals () para que não se tenha problema em obter a resposta false para cadeias de caracteres iguais;
Exemplo:
0 resultado será:
Classe StringBuffer
Os buffer de string são seguros para serem utilizados por múltiplas threads, uma vez que seus
métodos são sincronizados, e necessários para garantir que todas as operações em qualquer instância
possam ser executadas em ordem sequencial como se estivesse executando em apenas uma thread.
E cria um novo buffer de string inicialmente vazio, anexando a representação string de cada
operando ao buffer, e então convertendo o conteúdo do buffer de string para uma string. Isso evita a
criação de muitas strings temporárias.
A classe Math contém métodos para realizar operações matemáticas básicas como exponencial,
logaritmo, raiz quadrada e funções trigonométricas como seno e cosseno.
Classe Arrays
Essa classe contém vários métodos para manipulação de arrays, tais como ordenação,
comparação, preenchimento e busca, e também contém métodos de fábrica estáticos que permitem as
arrays serem visualizados como listas.
Todos os métodos dessa classe lançam a exceção NullPointerException se o array em questão for
uma referência nula.
Classe System
A classe System contém vários campos e métodos úteis para a programação. Essa classe não
pode ser instanciada e dentre as facilidades por ela oferecida, é possível destacar acesso às streams
para entrada padrão de dados, saída padrão de dados, e saída de erros; acesso às propriedades
definidas externamente; manipulação de carga de arquivos; bibliotecas; e métodos para cópia rápida
de porções de array.
Classe Runtime
Toda aplicação Java tem uma única instância da classe Runtime que permite a aplicação
interfacear com o ambiente no qual está rodando. A runtime corrente pode ser obtida pelo método
estático getRuntime () dessa classe.
Dos principais métodos oferecidos por essa classe, destacam-se aqueles para verificar
informações do sistema, para executar processos externos, terminar a execução da JVM, carregar
bibliotecas e executar o coletor de lixo (GarbageCollector).
Introdução à criação de classes
UML
0 primeiro deles, conhecido como diagrama de classes, será o alvo das seções apresentadas neste
livro, cujas notações serão indicadas separadamente, permitindo que a assimilação e habitualidade
com o uso ocorram de maneira mais natural. Esse diagrama modela relacionamentos estáticos de
classes que representam a arquitetura fundamental do sistema. Note que esses diagramas descrevem
relacionamentos entre classes, não entre objetos específicos instanciados dessas classes. Contudo, o
diagrama aplica-se a todos os objetos do sistema.
Classes
Uma classe é um tipo definido pelo usuário que possui especificações (características ou estados,
comportamentos e identidade) que o identifiquem. De uma maneira mais objetiva, podemos dizer que
a classe é um molde que será usado para construir elementos da vida real. Classe é a soma de
características ou estados, comportamentos e identidade, definidas assim:
• as características/estado de um objeto são os atributos que possue e a maneira como ele reage
ao ser aplicado um método;
• o comportamento de um objeto é o que se pode fazer com o mesmo, em outras palavras, são os
métodos que podem ser executados utilizando-o;
Tal molde é gerado através da observação e agrupamento de elementos que possuam as mesmas
características e comportamentos sob uma mesma denominação. Normalmente esta análise é feita em
um ambiente predefinido. Exemplificando, em uma loja online (ambiente), identificamos um elemento
chamado produto (classe), que possui um conjunto de características como a identificação e o preço
(atributos). Cada produto poderia ser materializado com as informações abaixo:
No entanto, os atributos nem sempre são úteis individualmente. Convém definirmos algumas ações
e comportamentos que esse elemento possa executar dentro do ambiente modelado usando os
atributos existentes para a Classe.
Esses comportamentos são denominados Métodos e definem as ações previstas para essa classe.
Ainda no exemplo da loja, poderíamos atribuir aos produtos as funcionalidades como aumentar ou
diminuir o preço e alterar a identificação. Note que normalmente as ações estão associadas aos
atributos da classe (preço e identificação).
Note que nos exemplos acima (Core Java, R$ 150,00; Camiseta, R$ 15,00) representam os casos
particulares da classe produto. Eles são denominados instâncias das classes, ou objetos, e eles têm
vida independente entre si, apesar de compartilharem o mesmo molde (Classe).
Objetos
Objetos seguem o molde da classe a partir da qual ele foi gerado. Toda vez que um objeto é
construído (por meio do operador new) o espaço necessário de memória para o objeto é alocado.
0 Garbage Collector libera o espaço de memória alocado para o objeto quando ele não está mais
sendo utilizado.
Utilizamos objetos para modelar elementos do mundo real, assim como conceitos abstratos como,
por exemplo, um evento, o qual é criado em aplicações gráficas quando o usuário pressiona um botão
ou uma tecla.
Modelando classes
0 primeiro passo na modelagem das classes é a definição das principais classes do sistema, as
quais são facilmente abstraídas, sendo que as demais classes acabam surgindo naturalmente no
decorrer do desenvolvimento.
Tendo definidas as classes, é necessário criar seus atributos essenciais e em seguida os métodos
aplicáveis a elas. Como exemplo, serão mostradas as classes Carro e NumeroComplexo.
Classe Carro
Considere uma classe chamada carro representando um elemento de um jogo de corrida, devendo
ter os principais atributos para sua aparência e funcionamento, e os métodos para que possa ser
utilizado.
Classe NumeroComplexo
• Nome da classe: um identificador para a classe, que permite referenciá-la posteriormente -- por
exemplo, no momento da criação de um objeto;
• Métodos: o conjunto de funcionalidades da classe, sendo que para cada método, especifica-se
sua assinatura.
Atributos
Os atributos de uma classe em Java podem ser tanto as variáveis de classe como as variáveis de
instância, cujas características de declaração, iniciação e uso são descritas a seguir:
Declaração de atributos
Os atributos de uma classe em Java devem ser declarados em qualquer local dentro do corpo da
classe (entre as chaves), mas deve estar fora de qualquer método.
• o modificador public indica que o atributo estará acessível a qualquer outra classe;
• o modificador private indica que o atributo estará acessível apenas à própria classe;
• o modificador protected indica que o atributo estará acessível a qualquer outra classe
pertencente ao mesmo pacote da classe referida ou por uma subclasse dela;
• o modificador static determina se o atributo é uma variável de classe (com static) ou se é uma
variável de instância (maiores detalhes nos capítulos a seguir.
• nome de uma classe que esteja no mesmo pacote ou cujo pacote já tenha sido importado;
Exemplo:
Para acessar os atributos de uma classe a partir de outra classe alguns dos seguintes pré-
requisitos são necessários:
• o atributo temo modificador protected, e a classe a ser acessada tem de estar no mesmo pacote,
ou seja, tem de ser uma subclasse dela.
Sendo satisfeita a condição, basta que a classe usuária contenha em algum método ou construtor
uma chamada de um dos seguintes tipos:
Exemplo:
Inicialização de atributos
Ao declarar um atributo, já é possível iniciá-lo com algum valorpadrão, bastando apenas colocar
uma expressão de atribuição na própria declaração.
Exemplo:
Em UML os atributos ficam na porção intermediária de sua representação gráfica como mostrado
a seguir:
Ela apresenta o conjunto de propriedades da classe, sendo que para cada propriedade, especifica-
se:
Métodos
os métodos de uma classe em Java podem ter diferentes modificadores, retornar ou não valor, e
ter ou não parâmetros de entrada. As características de declaração e uso são descritas a seguir:
Declaração de métodos
Assim como os atributos, os métodos de uma classe em Java devem ser declarados em qualquer
local dentro do corpo da classe (entre as chaves).
• o modificador public indica que o método estará acessível a qualquer outra classe;
• o modificador private indica que o método estará acessível apenas à própria classe;
• o modificador protected indica que o método estará acessível a qualquer outra classe
pertencente ao mesmo pacote da classe referida ou por uma subclasse dela;
• o modificador static determina se o método pertence à classe (com static) ou se pertence a uma
instância dela.
Vale lembrar que todo método estático só poderá acessar atributos também estáticos da classe.
Exemplo:
Retorno de métodos
Todo e qualquer método deve ter sua assinatura indicando o tipo de retorno, mesmo que o método
seja apenas de operação e não retorne valores. Para isso seguem as regras:
• para métodos que retornam um tipo primitivo, basta colocar a palavra-chave correspondente
(Exemplos: byte, int, long etc.);
• para métodos que retornam uma instância de determinada classe, basta conter o nome da classe
que esteja no mesmo pacote ou cujo pacote já tenha sido importado;
• ou utilizar o nome completo de uma classe, contendo seu pacote (Exemplo: java.util.Hashtable).
Vale lembrar que todo método cujo tipo de retorno seja uma classe, este poderá retornar o
conteúdo nulo nu11.
Passagem de parâmetros
É possível criar métodos que não recebem nenhum valor como parâmetro na chamada ou que
recebam um ou mais parâmetros, sendo que a quantidade e tipo devem ser definidos em sua
assinatura. Exemplo:
Para acessar os métodos de uma classe a partir de outra classe alguns dos seguintes pré-requisitos
são necessários:
• o método tem o modificador protected; e a classe a ser acessada tem de estar no mesmo pacote,
ou seja, tem de ser uma subclasse dela.
Sendo satisfeita a condição, basta que a classe usuária contenha em algum método ou construtor
uma chamada de um dos seguintes tipos:
• para método não estático (de instância): nomeObjeto. metodo ([varl] [,var2] *).
Exemplo:
Em UML os métodos ficam na porção inferior de sua representação gráfica como mostrado a
seguir:
Esse segmento apresenta o conjunto de funcionalidades da classe. Para cada método, especifica-
se sua assinatura, composta por:
• lista de argumentos: quando o método recebe parâmetros para sua execução, o tipo e um
identificador para cada parâmetro;
• visibilidade: como para atributos, define o quão visível é um método a partir de objetos de
outras classes.
Método main
0 método main é um método comum como qualquer outro, com a única diferença que a Máquina
Virtual Java, por meio do comando fava, tentará executar, passando todos os argumentos recebidos
na linha de comando por meio de um array de strings.
Na prática, ao executar o comando java NomeClasse argl arg2 arg3 ... a Máquina Virtual procura
um método público es tático chamado main na classe Nomeclasse que não retorne valor (void) e que
receba como parâmetro um array de strings. Formalmente a JVM faz a busca por um método com a
assinatura exata:
Em outras palavras, se uma variável inteira de valor 5 é passada para um método, este poderá
usar seu valor por meio da cópia criada, não alterando o valor original da variável utilizada na
chamada, como mostrado a seguir:
Passagem de parâmetro de tipos referência (objetos e arrays)
Em outras palavras, se um objeto da classe StringBuffer for passado para um método, o qual
adiciona algum texto ao conteúdo, ao término o objeto conterá a nova string concatenada, como
mostrado a seguir:
Observação: vale lembrar que expressões de atribuições não modificam o objeto externo, uma
vez que apenas a referência é alterada e não o objeto. Apenas modificações diretas nos atributos
ou modificações causadas pelas chamadas a métodos modificam o objeto. Dessa forma, como
toda instância da classe String tem seu valor constante, o seu uso se assemelha ao de uma
passagem de tipo primitivo.
Exemplo:
Sobrecarga de métodos
Quando dois métodos com o mesmo nome são criados, mas com parâmetros diferentes, temos a
chamada sobrecarga de métodos.
Essa característica é muito utilizada na criação de classes cujos métodos utilizam parâmetros
opcionais, exigindo a criação de métodos com diferentes números e tipos de parâmetros.
Como exemplo, tem-se os métodos print () e println () da classe =OutputStream () ;
0 principal objetivo do encapsulamento de classes é proteger seus atributos de mudanças
realizadas por outras classes. A idéia consiste em garantir que qualquer mudança nos atributos de
uma classe seja realizada somente pela própria classe, por meio de seus métodos, sendo estes usados
pelas demais classes para possíveis interações que possam ser realizadas.
Getters e Setters
São métodos criados para cada um dos atributos com o objetivo de permitir a interação com
outras classes, sem que tenha que utilizar atributos públicos.
Métodos getX
É o método utilizado para retornar o valor de cada um dos atributos de uma classe. Para cada
atributo é utilizado um método getX, substituindo o x pelo nome do atributo. Vale ressaltar que o tipo
de retorno do método deve ser exatamente o mesmo do tipo do atributo.
Método setx
É o método utilizado para definir o valor de cada um dos atributos de uma classe. Para cada
atributo é utilizado um método setX, substituindo o x pelo nome do atributo. Vale ressaltar que o tipo
de retorno do método sempre será void e o mesmo sempre receberá como parâmetro um objeto com
exatamente o mesmo tipo do atributo.
Método isx
0 método isx tem o objetivo de verificar alguma característica do objeto, retornando um valor do
tipo boolean, com o valor true se a característica estiver ativa e false caso contrário. Esse método
pode retornar o valor de um atributo já do tipo booleano da classe ou retornar o resultado de alguma
expressão de comparação entre outros atributos.
Exemplo:
Método tox
0 método tox é utilizado para converter a instância do objeto utilizada na chamada para outro tipo
conhecido, sendo este último substituído pelo x.
Um caso especial é o método tostring () o qual é chamado pelo método print () e println () da
classe OutputStream, comumente utilizada para interação com o usuário. Neste caso, para que se
possa imprimir um conteúdo mais amigável para uma classe ao invés de sua referência, basta
estender esse método, o qual pertence à classe Object.
Exemplo:
Modificadores de acesso
Como já visto anteriormente, os atributos e métodos de uma classe podem ser declarados
precedidos de um modificador de acesso que indica quais outras classes poderão acessá-los. Caso
não haja nenhum modificador, entende-se como se o atributo ou método seja tipo protected.
Modificador public
0 modificador public é o único deles que pode ser executado tanto em uma classe, em um atributo
de classe ou método de classe. Para qualquer dos alvos do modificador, sua presença indica que o
alvo estará acessível por qualquer outra classe.
Mais particularmente, se o modificador public for utilizado em uma classe, esta mesma classe
poderá ser instanciada por qualquer outra classe (Exemplo: Classe obj = new Classe () ; ).
Se o modificador public for utilizado em um atributo de uma classe, este mesmo atributo poderá
ser acessado por qualquer outra classe (Exemplo: obj.atributo = novoValor; ).
Por outro lado, se o modificador public for utilizado em um método de uma classe, este mesmo
método poderá ser acessado por qualquer outra classe (Exemplo: obj.metodo (param) ;).
Modificador private
0 modificador private só pode ser executado em um atributo ou método de uma classe. Para
qualquer um dos casos, sua presença indica que o alvo estará acessível apenas para a própria classe.
Se o modificador private for utilizado em um atributo de uma classe, este mesmo atributo poderá
ser acessado apenas pela própria classe (Exemplo: atributo = novoValor; ou this.atributo =
novoValor;).
Por outro lado, se o modificador private for utilizado em um método de uma classe, este mesmo
método poderá ser acessado apenas pela própria classe (Exemplo: metodo (param) ; ou this.
metodo(param);).
Modificador protected
0 modificador protected só pode ser executado em um atributo ou método de uma classe. Para
qualquer um dos casos, sua presença indica que o alvo estará acessível para qualquer subclasse ou
classe pertencente ao mesmo pacote da identificada.
Se o modificador protected for utilizado em um atributo de uma classe, este mesmo atributo
poderá ser acessado por uma subclasse (Exemplo: super.atributo = novoValor;) ou por uma classe do
mesmo pacote (Exemplo: obj. atributo = novoValor; ).
Por outro lado, se o modificador protected for utilizado em um método de uma classe, este mesmo
método poderá ser acessado por uma subclasse (Exemplo: super.metodo (param) ;) ou por uma
classe do mesmo pacote (Exemplo: obj.metodo (param);).
Apesar de o modificador protected ter a finalidade de garantir que nenhuma classe estranha ao
sistema (fora do pacote) acesse diretamente um atributo ou método, nada realmente garante que isso
acontecerá, uma vez que qualquer programador pode desenvolver uma classe má intencionada e
adicioná-la ao mesmo pacote. Esse modificador, porém, tem grande utilidade para classes da API
base do Java, uma vez que por pertencer ao pacote java. (restrito), não há como criar uma classe e
adicioná-la a ele, tendo assim a real proteção desejada.
Não há restrições quanto ao uso dos modificadores private e public para declarações de atributos
e métodos, porém algumas técnicas de programação orientada a objetos sugerem declarar todos
atributos de uma classe como private e permitir que outras classes as acessem e as modifiquem por
meio de métodos getx e setX.
Essa técnica, chamada de encapsulamento, descrita anteriormente, garante maior controle pela
própria classe das modificações realizadas em seus atributos, sejam eles de característica ou de
estado. Como exemplo, é possível fazer controle de acesso de modificação de atributos, não
existente normalmente, ou contar o número de alterações realizadas, ou também fazer log de
execuções.
Quando se trata de métodos, é importante saber que qualquer método utilizado internamente pela
classe para realização de alguma tarefa, como por exemplo, um método público metodoA() realiza
alguma tarefa repetidamente localizada no metodoB (), o qual não poderá ser executado por outras
classes, esse metodoB () deve ser declarado como private.
Quando se instancia uma classe, criando um objeto a partir de sua especificação, na realidade é
executado um método que, após reservar o espaço de memória ocupado pelo objeto, inicia todos os
atributos com os respectivos valores padrões.
Lembrando que em Java não é possível forçar a destruição de um objeto, não há a possibilidade
de criar os chamados destrutores como em outras linguagens (Exemplo: C++). Por outro lado, caso
haja a necessidade de executar alguma operação quando o Garbage Collector for liberar o objeto da
memória, há a possibilidade de definir os comandos que serão executados, por meio da
implementação de um método finalize 0.
Criando construtores
De uma forma geral, todas as classes que não declararam nenhum construtor são vistas como
tendo um construtor sem parâmetros:
Caso o construtor receba algum tipo de parâmetro, o código ficará como a seguir:
E sua instanciação:
Importante: a partir do momento que se tem algum construtor explicitamente implementado pela
classe, o construtor sem parâmetros automático deixa de existir, sendo assim, a criação de um
objeto da classe NomeClasse2 mostrada anteriormente, por meio do comando NomeClasse2 obj
= new Classe2 () ; gera um erro de compilação. Para que não ocorra esse erro, é necessário
declarar explicitamente um construtor sem parâmetros como na classe NomeClasse.
Exemplo:
Sobrecarga de construtores
Assim como na criação de métodos comuns, quando dois construtores com o mesmo nome são
criados, mas com parâmetros diferentes, temos a chamada sobrecarga de construtores.
Essa característica é muito utilizada na criação de classes cujos construtores utilizam parâmetros
opcionais, exigindo a criação de construtores com diferentes números e tipos de parâmetros.
Exemplo:
Método finalize
A classe Object tem um método protected chamado finalize, o qual pode ser sobrescrito pelas
demais classes (que estendem a classe Object). A definição do método finalize que pode ser
chamado por um objeto é chamado de finalizer do mesmo. Antes que o Garbage Colletor tente liberar
o espaço de memória ocupado pelo objeto, a Máquina Virtual Java chamará o seu finalizer.
A assinatura do método finalize segue abaixo, sendo também importante chamar o finalize da
superciasse, pois este poderá já realizar alguma liberação importante:
Os finalizadores oferecem a oportunidade de liberar alguns recursos que não podem ser liberados
automaticamente pelo gerenciador de memória. Nessas situações, simplesmente liberando a memória
usada pelo objeto não garante que os recursos por ele utilizados sejam também liberados.
Como já visto anteriormente, é possível criar atributos e métodos com o uso do modificador
static. Em ambos os casos, o atributo ou método fica associado à classe e não ao objeto ou à
instância da mesma classe, sendo comum entre todas as instâncias e podendo ser acessado sem a
criação de nenhum objeto.
Atributo static
2. Tem valor comum entre todas as instâncias (Exemplo: Para qualquerobjl e obj2
objl.atributoStatic == obj2.atributoStatic, será sempre verdade).
Só deve utilizar atributos estáticos quando uma determinada classe contém alguma característica
mutável durante a execução, e cujo valor deva ser igual entre todas as instâncias da classe. Como
exemplo, têm-se atributos que contêm contagem de instâncias e números de seqüência. Ou também
vale usar atributos estáticos para constantes que devam ser acessados sem que haja a necessidade de
instanciar um objeto da classe, como é o caso das constantes representantes de cores da classe
java.awt.Color.
Método static
Um método estático declarado com static é chamado de método de classe. Um método de classe é
sempre chamado sem a referência a um objeto em particular, dessa forma, uma tentativa de
referenciar o objeto corrente usando as palavras-chave this ou super gera um erro de compilação.
2. Os comandos contidos em seu corpo só poderão acessar atributos também estáticos da classe,
caso contrário, um erro de compilação será gerado.
Métodos estáticos devem ser utilizados para manipular os atributos estáticos de uma classe e para
executar tarefas independentes dos valores de algum atributo da classe. Como exemplo, têm-se os
métodos valueof() da classe String, os quais não efetuam nenhuma operação no objeto e sim retornam
um novo objeto String baseado no parâmetro passado a ele.
Qualquer bloco static, também chamado de iniciador estático (static initializers), declarado em
uma classe é executado quando a classe é iniciada e, em conjunto com outros iniciadores de atributos
das classes, deve ser utilizado para iniciar os atributos de classe (estáticas).
Exemplo:
• Associação;
• associação (simples);
• agregação;
• composição;
• Generalização;
• herança;
• implementação (realização);
• Dependência.
Associação (simples)
A associação representa a habilidade de uma instância mandar uma mensagem para outra
instância. Essa característica é tipicamente implementada, criando em uma determinada classe um
atributo de referência a outra classe a qual está associada.
Na UML, a associação é uma linha sólida com uma seta aberta na extremidade que representa um
relacionamento tipo tem _ . A seta aponta da classe que contém para a classe que está contida.
Alguns autores e ferramentas não utilizam seta, desenhando apenas uma linha sólida entre as classes.
Veja Figura 15.1.
Exemplo:
Encapsulamento de associações
• criação do método getx retornando a referência para a instância da classe associada e método
setx recebendo uma referência da classe como parâmetro;
• criação do método getx retornando a referência para a instância da classe associada e métodos
setx recebendo os mesmos parâmetros utilizados nos construtores da classe associada,
normalmente os atributos contidos nela.
Exemplo:
Agregação
Em outras palavras, a agregação modela a noção de que um objeto usa algum outro objeto sem ser
dono dele e, dessa forma, não é responsável por sua criação e destruição.
Na UML, a agregação é representada pela mesma linha da associação com um losango (ou
diamante) vazio no outro extremo. Se utilizar um ponteiro de seta na representação de associação, a
mesma também será utilizada na agregação, e não será caso contrário. Veja a Figura 15.2.
Em Java, a agregação é implementada por meio de um atributo contendo algum tipo de listagem de
objetos da classe contida, na classe que a contém. Apesar de ser possível implementar utilizando
arrays, uma vez que esses possuem dimensão constante, eles não são usados. Assim sendo,
normalmente a agregação é implementada utilizando alguma classe de agrupamento como
java.util.Vec tor e java.util.Hastable ou qualquer outra classe do framework Collections.
Exemplo:
Encapsulamento de agregações
Por se tratar de um relacionamento de classes do tipo todo/parte, cujas partes têm existência
independente própria, um objeto da classe que agrega (todo) é construído vazio e tendo as partes
agregadas com o decorrer da execução do programa.
• método add (Tipo obj) ou addTipo (Tipo obj) para adicionar um novo objeto da classe Tipo à
coleção representada em algum atributo da classe que agrega;
• método remove (Tipo obj) ou removeTipo (Tipo obj) para remover um objeto já existente na
coleção representada em algum atributo da classe que agrega;
• método remove (int indice) ou removeTipo (int indice) para remover um objeto já existente na
coleção da classe que agrega, e que esteja no índice indicado;
• método get(int indice) ou getTipo(int indice) para retornar um objeto já existente na coleção da
classe que agrega, e que esteja no índice indicado.
Exemplo:
Composição
Em outras palavras, a composição modela a noção de dependência, na qual um objeto usa algum
outro objeto sendo seu dono, normalmente sendo responsável por sua criação e destruição.
Na UML, a composição é representada pela mesma linha da associação com um losango (ou
diamante) cheio no outro extremo. Se utilizar um ponteiro de seta na representação de associação, a
mesma também será utilizada na composição, e não será caso contrário. Veja a Figura 15.3.
A diferença entre uma composição e uma agregação é conceitual quanto à dependência das partes
perante o todo. Em outras palavras, destaca a questão do todo controlar a criação e destruição das
partes.
De uma forma geral, a implementação é feita da mesma maneira que na agregação, por meio de
um atributo contendo algum tipo de coleção de objetos da classe contida na classe que a contém (ver
Representação de agregação em Java). Sendo assim, o controle dos ciclos de vidas das partes em
relação ao todo vai depender da aplicação que as utiliza, e não das classes compostas.
Exemplo:
Encapsulamento de composições
Exemplo:
Herança (Generalização)
Generalização e herança são abstrações poderosas para compartilhar similaridades entre classes
e ao mesmo tempo preservar suas diferenças.
Na UML a herança é representada por uma linha cheia com uma ponteira de seta cheia apontando
da subclasse para superclasse. Veja a Figura 15.4.
Em Java, quando uma classe Filho herda de uma classe Pai, dizemos que a classe Filho estende a
classe Pai. Essa nomenclatura é dada, uma vez que a herança é normalmente utilizada por classes
"filhas" que reutilizam um código de outras classes "pais", criando novas funcionalidades por meio
de novos atributos e métodos, ou mesmo por mudanças comportamentais nos já existentes.
Exemplo:
Os modificadores de acesso public, protected e private permanecem com o comportamento entre
uma superclasse e sua subclasse semelhante a uma classe com qualquer outra classe de seu pacote.
Ou seja, o acesso de um atributo ou método com os modificadores segue as características:
• modificador protected, a subclasse tem acesso ao recurso mesmo que esteja em um pacote
diferente da superciasse;
Uma vez tendo acesso, caso a subclasse queira acessar explicitamente um atributo ou método de
sua superclasse, será possível utilizar a palavra-chave super, a qual se assemelha à palavra-chave
this para acesso aos recursos do próprio objeto. Essa opção é bastante utilizada quando há a
sobreposição de métodos ou atributos e a subclasse queira chamar o equivalente da superclasse.
Exemplo:
Sobreposição/Sobrescrita de métodos
Quando uma subclasse tem um método com exatamente a mesma assinatura que um método da
superciasse, dizemos que houve a sobreposição ou sobrescrita do método.
Lembrando que existem métodos de instâncias, associados aos objetos de uma classe, e métodos
de classe associados à classe propriamente dita, a sobreposição tem comportamento diferente, sendo
chamada de overriding para métodos de instância e hide para métodos de classe.
Os métodos de instância são aqueles que são declarados sem a utilização da palavra-chave static
e devem ser executados por meio de um objeto da classe. A sobreposição faz com que o método
implementado na subclasse seja sempre executado no lugar do implementado na superclasse.
Exemplo:
Imprime:
Os métodos de classe são aqueles que são declarados utilizando da palavra-chave static e podem
ser executados por meio de um objeto da classe ou diretamente na classe. A sobreposição faz com
que o método executado seja o implementado na classe do tipo da referência, independentemente de
qual objeto tenha sido instanciado.
Exemplo:
Imprime:
Uma interface é uma coleção de assinaturas de métodos e/ou definições de atributos que define
idealmente um conjunto de comportamentos coesos. Interfaces são implementadas, realizadas no
palavreado da UML, por classes e componentes.
Para realizar uma interface, uma classe ou componente deve implementar os métodos e atributos
definidos pela interface. Qualquer classe ou componente deve implementar zero ou mais interfaces e
uma ou mais classes ou componentes podem implementar uma mesma interface.
As interfaces são normalmente utilizadas como uma forma de contrato entre duas ou mais classes,
ou seja, é quando existe um determinado serviço a ser oferecido, sendo necessário estipular uma
interface de interação entre a classe que oferece e aquela qual utiliza, para que o desenvolvimento de
uma não dependa da outra.
Para a representação de interfaces em UML é usada a mesma caixa utilizada para representação
de classes, acrescentando apenas o estereótipo «interface» no segmento reservado para o nome, ou
também colocando o nome da interface entre os símbolos « e ».
As realizações são um tipo de relacionamento entre classes e interfaces e são representadas por
uma linha tracejada, com uma seta ponteira sólida direcionada da classe para a interface que ela
implementa. Veja a Figura 15.5.
Figura 15.5: Realização (interface).
A definição de uma interface em Java é semelhante a uma definição de classe, sendo realizada em
um arquivo separado, só que com as seguintes diferenças:
Exemplo:
Uma classe em Java pode implementar uma ou mais interfaces, para isso basta anexar à definição
da classe a palavra-chave implementa seguida das interfaces separadas por vírgula.
É importante lembrar que, uma vez que uma classe implementa uma determinada interface, é
necessário criar a assinatura e o corpo de todos os métodos existentes na interface.
Exemplo:
Estendendo uma interface
Assim com uma classe em Java, uma interface também pode ser estendida por uma subinterface de
maneira a especializar ainda mais as funcionalidades oferecidas.
Diferentemente das classes, as quais contêm as implementações dos métodos, uma subinterface
não pode sobrepor um método de sua superclasse, conseqüentemente, as subinterfaces normalmente
acrescentam novos métodos ou atributos, exigindo que as classes que as implementam tenham que ter
todos os métodos contidos em ambas interfaces.
Exemplo:
Dependência
A dependência é representada na UML com uma linha tracejada com uma seta ponteira aberta que
mostra que uma entidade depende do comportamento de outra. A seta tem a direção de quem depende
para a outra. Veja a Figura 15.6.
Exemplo:
Dentre as questões abordadas, destacam-se o uso dos modificadores final e abstract na definição
de classes, métodos e atributos; Cast de objetos e polimorfismo.
Modificador final
0 modificador final pode ser aplicado a variáveis, métodos e classes. 0 comportamento descrito
pelo operador final varia de elemento para elemento, mas tem um conceito em comum, o conceito de
imutabilidade, isto é, elementos finais não podem ser mudados.
Modificador final na declaração de classes
No caso de uma classe, quando dizemos que esta é final, dizemos que ela não pode ser herdada ou
estendida. Considere a definição da classe Imutavel no código a seguir:
Qualquer tentativa de estender (criar herança) a partir da classe Imutavel irá causar um erro de
compilação. Como exemplo o código a seguir gera um erro de compilação.
Métodos final não podem ser redefinidos, ou seja, caso exista uma subclasse que estenda uma
outra classe contendo métodos com esse modificador, esses métodos não podem ser redefinidos na
subclasse. Os mesmos objetivos que motivam o uso do modificador final em classes também
motivam seu uso em métodos.
0 último elemento a que o modificador final pode ser aplicado é a atributos. Atributos final não
podem ter seus valores mudados, ou seja, são constantes, a partir do momento que se define o seu
valor inicial, esse será o seu valor durante todo o ciclo de vida do objeto.
Quando se trabalha com objetos, é importante notar que apenas o valor do objeto, ou seja, sua
referência, não pode ser mudada, atributos não final de uma instância podem ser mudados conforme é
mostrado no código a seguir:
Modificador abstract
0 modificador abstract pode ser aplicado somente a classes e métodos. 0 uso com interfaces é
antigo e não mais utilizado, uma vez que essas são inerentemente abstratas.
Classes abstratas
Classes abstratas provêm um modo de adiar a implementação de métodos para subclasses. Uma
classe abstrata não pode ser instanciada, ou seja, não é possível chamar seus construtores.
Veja como exemplo o código a seguir. A classe Figura pode ser herdada para criar classes tais
como, Retangulo, Triangulo, Quadrado, circulo entre outros. Nesse caso a classe Figura teria
atributos comuns entre esses objetos, mas não seria possível aplicar o operador new à classe Figura,
ou seja, não poderia criar uma instância dessa classe.
No código anterior foi declarado um objeto do tipo Figura, o qual não causa erro, já que a
restrição em relação a classes abstratas é que estas não podem ser instanciadas, como é o caso do
comando seguinte, mas podem sim, ser declaradas.
Normalmente, elas são utilizadas para implementar métodos comuns às possíveis subclasses e
apenas assinar métodos que já se sabe que deverão ser particulares a cada subclasse.
Por outro lado, as interfaces só possuem declarações de atributos e assinaturas de métodos, sem
nenhuma implementação, assim sendo, elas são utilizadas para definir um contrato de interação entre
diferentes classes. Na prática, são criadas por classes que oferecem algum tipo de serviço
dependente de outra classe, cujas características e métodos devem obedecer ao padrão da interface.
Uma analogia à vida real seria uma pessoa (classe) que oferece o serviço de digitação (método),
exigindo que passe para ela (parâmetro) um computador com teclado ABNT (interface). Note que
não foi especificado um objeto em si e nem sequer uma implementação, apenas que a interface ou
padrão do teclado seja igual ao estabelecido pela ABNT.
Observação: se for definir um componente que deverá ser estendido, mas que já se pode
implementar algum método comum a todas possíveis subclasses, devem ser utilizadas classes
abstratas. Caso seja necessário apenas indicar os métodos pelos quais as subclasses deverão
implementar, devem ser utilizadas interfaces.
Métodos abstratos
Métodos abstratos são métodos que em um determinado nível de programação ainda não se sabe
como implementá-los, mas é sabido que eles serão necessários, veja, por exemplo, o método
desenhar() na classe Figura. No momento de design da classe não é possível ainda conhecer como
desenhar instâncias desses objetos, mas se saberá no momento que criar uma subclasse. Como por
exemplo, para definir uma classe circulo, já se tem todas as informações necessárias para
implementação desse método.
Classes devem ser declaradas abstratas quando qualquer uma das afirmativas a seguir forem
verdadeiras:
• a classe herda de classes abstratas e não define todos os seus métodos abstratos e nem estende
nenhuma classe que os define;
• a classe implementa uma interface, mas não declara todos os seus métodos.
Note que essas são condições em que a classe obrigatoriamente deve ser declarada como
abstrata, mas uma classe pode não cair em nenhuma das três afirmativas anteriores e ainda ser
declarada como abstrata.
De uma forma geral, uma classe deve ser declarada abstrata apenas quando sua definição está
incompleta e a intenção é que alguma subclasse possa ser criada para completar sua implementação.
Se a intenção for simplesmente prevenir a instanciação de uma classe, a maneira apropriada de
expressar é declarando um construtor sem argumentos, com o modificador private, que não é
chamado por nenhum outro método da classe, e garantir que nenhum outro construtor seja declarado
na mesma classe. Uma classe desse formato usualmente contém apenas métodos e atributos de classe,
como é o caso da classe Math, a qual não pode ser instanciada e cuja declaração parece com a
seguinte:
A conversão de objetos é um pouco mais complicada que a de tipos primitivos. Nessa seção
serão apresentadas as regras que controlam a conversão de objetos. É importante saber nesse
momento, que quando é citado conversão de objetos, este é na verdade conversão de referências a
objetos. A conversão de tipos ocorre, tipicamente, nas mesmas situações da conversão de primitivos,
ou seja, durante a atribuição e chamada de métodos. Não ocorre, contudo, conversão de objetos em
operações aritméticas.
Conversão de objetos ocorre quando é atribuído a uma referência de uma determinada classe o
valor de um objeto de outra classe. Existem três tipos de referências a objetos:
• classes;
• interfaces;
• arrays.
Como ClasseTipoOriginal e ClasseNovoTipo podem ser uma classe, uma interface ou um array,
existem nove combinações possíveis de conversão e conseqüentemente nove regras de conversão. A
tabela a seguir traz um sumário dessas combinações.
Tabela 15.1.
Para ilustrar essas regras, considere a árvore de hierarquia apresentada na Figura 15.7:
A sintaxe do casting de objetos é idêntica à utilizada para tipos primitivos, com o objeto a ser
convertido precedido da classe destino entre parênteses.
Exemplo:
É sempre possível utilizar casting nas mesmas situações em que a conversão é possível. Apesar
de não ser necessária, essa prática é usada por alguns programadores para tornar o código mais
compreensível.
0 operador new é usado para criar um novo objeto, determinando a real classe de um objeto. Os
programadores Java não manipulam diretamente os objetos. Ao contrário disso, trabalham apenas
com referências a objetos. No código anterior, por exemplo, a variável g não é um objeto, e sim uma
referência a um objeto, a qual armazena, na verdade, o endereço do objeto na área de memória onde
está armazenado. Esse endereço é representado por um número de 32 bits e é chamado de Referência
ao Objeto. Veja a figura a seguir.
Figura 15.8: Representação das referências a objetos.
A classe de um objeto é imutável. Contudo, ele pode ser referenciado por objetos de vários tipos.
A seguir é mostrado um trecho de código e a representação gráfica equivalente na Figura 15.9.
Existem dois conjuntos de regras que regem o casting de objetos. 0 primeiro é formado por regras
aplicadas em tempo de compilação, mostradas na tabela a seguir:
Tabela 15.2.
São, ao todo, 16 regras. Uma quantidade não muito fácil de decorar. A maioria delas se refere a
casos pouco utilizados.
Exemplo:
Polimorfismo
Polimorfismo é o processo pelo qual uma mesma variável de referência pode responder de forma
diferente à mesma chamada de um método. Esse mecanismo é executável por meio das
implementações de classes abstratas e interfaces ou por simples extensão de classes, em conjunto
com o conceito de sobrescrita de métodos (overriding).
Exemplo:
Observação: se alguma referência para a ClassePai executar o método imprime(), este poderá
imprimir ClasseFilhaA ou ClasseFilhaB, uma vez que devera referenciar um objeto de uma das
duas classes filhas. Nesse caso, tem-se o que é chamado de polimorfismo, pois um mesmo tipo
de variável pode reagir de forma diferente para o mesmo método.
Programas em Java são normalmente organizados em conjunto de pacotes. Cada pacote tem seu
próprio conjunto de nomes de tipos (ou classes), ou seja, dois pacotes diferentes podem ter classes
com o mesmo nome, o que previne conflitos de nomes.
Membros de um pacote
Os membros de um pacote são seus subpacotes e todas as classes e interfaces declaradas nas
unidades de compilação do pacote. As unidades de compilação podem ser vistas como os arquivos
com os códigos das classes, as quais contêm a definição de um pacote, as importações de outras
classes e a declaração do tipo (classe ou interface).
• o pacote java tem os subpacotes awt, applet, io, lang, net, and util, mas não tem unidade de
compilação;
• o pacote java.awt tem um subpacote image, assim como um certo número de unidades de
compilação contendo declarações de classes e interfaces.
Os pacotes são organizados no sistema de arquivos por meio de pastas e subpastas. Dessa forma,
a Máquina Virtual Java faz a busca automática no momento da execução de classes de um
determinado pacote no diretório correspondente ao nome do pacote e no diretório corrente para
classes sem declaração de pacotes.
Por exemplo, uma classe Classel pertencente ao pacote pacote, deverá estar em uma pasta com o
mesmo nome (pacode/Classel.class) e, outra classe Classe2 pertencente ao pacote pacote. subpacote
deverá estar armazenada no diretório pacode/subpacote/Classe2.class.
Declarações de pacotes
Exemplo:
Para executar uma classe que fora declarada em um pacote é necessário chamar a Máquina Virtual
Java passando o nome completo da classe, ou seja, os nomes dos pacotes, subpacotes e classe
separados por pontos.
Exemplo:
1. Indicar o nome completo da classe ao criar uma referência, ao instanciar um objeto e executar
um método estático (Exemplo: java.util.Vector v = new java.util.Vector()).
Há quatro tipos possíveis de declarações de importação: simples, sob demanda, estática simples
e importação estática sob demanda.
A importação de tipo simples tem o objetivo de importar uma determinada classe de um pacote.
Exemplo:
A importação de tipo sob demanda permite importar, conforme a necessidade, todos os tipos
(classes) declarados em um determinado pacote.
Exemplo:
A importação estática simples tem o objetivo de importar todos os membros estáticos de uma
determinada classe de um pacote.
Exemplo:
A importação estática sob demanda permite importar, conforme a necessidade, todos os membros
estáticos declarados em uma determinada classe de algum pacote.
Exemplo:
• toda classe declarada sem o indicador de pacote package será uma classe membro do pacote
default que deverá estar em algum caminho do classpath fora de qualquer diretório;
• qualquer importação de pacotes que incorporem classes com o mesmo nome, gerando conflitos,
exige que as instruções conseqüentes indiquem o nome completo da classe (Ex: java.util. Point e
teste.Point);
• a relação de mapeamento de diretórios X nomes dos pacotes pode não ser respeitada para a
estrutura dos códigos-fonte .java, mas deve ser sempre respeitada para os arquivos bytecodes
class. Vale lembrar que caso as fontes não respeitem a estrutura de diretórios, o compilador não
criará a estrutura automaticamente, tendo que ser realizada manualmente depois da compilação;
• a indicação de qual pacote a classe é membro (package) é sempre realizada em primeiro lugar
em um arquivo java, seguido das importações (import) e só depois das declarações das classes.
Um sistema desenvolvido em Java é normalmente formado por um conjunto de classes, as quais
podem estar distribuídas em vários pacotes diferentes. Quando se deseja implantar o sistema em
algum lugar ou distribuí-lo a diferentes pessoas, a existência de inúmeros arquivos, sendo um para
cada classe, dificultaria o procedimento.
Uma forma de facilitar o agrupamento das classes é criar um arquivo compactado semelhante a
um zip que contém toda estrutura de diretórios e arquivos do sistema, o qual é denominado Java
ARchive e possui a extensão jar.
Criando um jar
Para se criar um arquivo jar simples utilizando a ferramenta jar oferecida com o JDK são
utilizados os parâmetros c (criar), f (nomear) e v (verbose-opcional). Veja os exemplos a seguir:
Quando se utiliza o Netbeans, este cria automaticamente um .jar para o projeto em questão. 0
arquivo .jar é armazenado dentro de uma pasta chamada dist dentro da pasta do projeto. Basta então
copiar esse arquivo e utilizá-lo para executar sua aplicação.
É possível executar, de duas formas, um arquivo jar, a primeira é igual à execução de um jar
simples, passando o arquivo como um classpath e identificando a classe a ser executada
normalmente, como mostrado a seguir:
A segunda opção, válida apenas para arquivos jar executáveis, utiliza o parâmetro -jar da
ferramenta java, não precisando passar o nome da classe principal, como mostrado a seguir:
Vale lembrar também que o instalador do J2SDK para MS Windows já associa arquivos .jar para
a ferramenta javaw, facilitando a execução com apenas um duplo clique no arquivo, porém não será
muito útil para aplicações de console, uma vez que o javaw executa a aplicação fora de um terminal
(caso do java).
Introdução
0 pacote java.io define um grande número de classes para leitura e escrita de dados em cadeia de
bytes (streaming ou dados seqüenciais). As classes InputStream e OutputStream são designadas à
leitura e escrita de seqüências de bytes, enquanto as classes Reader e Writer são usadas para a
leitura e escrita de seqüências de caracteres.
Stream binárias
As classes de manipulação de seqüência de bytes, denominadas stream binárias, são base para
qualquer operação de entrada e saída de dados. Essa característica inerente à linguagem de
programação Java se torna clara e óbvia se pensar que todas as informações em um computador,
sejam elas números inteiros, booleanos, caracteres ou qualquer outro tipo, estarão sendo
armazenadas em memória e em disco como conjuntos de bytes. Dessa forma, ao armazenar uma
variável do tipo int em um arquivo, na verdade estarão sendo armazenados os quatro bytes
correspondentes.
A plataforma Java oferece então várias classes de manipulação de entrada e saída de seqüências
de bytes, por exemplo, DatalnputStream, DataOuputStream, FileInputStream, FileOutputStream etc.,
sendo todas subclasses (ou classes estendidas) das classes bases InputStream e OutputStream.
InputStream e OutputStream
java.ia.InputStream
• int available () - retorna o número de bytes que podem ser lidos (ou saltados) da seqüência de
entrada sem bloquear até a próxima chamada por um método dessa stream;
• void Glose () - fecha a seqüência de entrada e libera qualquer recurso de sistema associado
com a stream;
• abstract int real () - faz a leitura do próximo byte de dados da seqüência de entrada;
• int read (byte [ ] b) - faz a leitura de um número de bytes da seqüência de entrada e armazena em
um buffer do tipo array de bytes;
• int read(byte[] b, int off, int len) - faz a leitura de um número de bytes da seqüência de entrada e
armazena em um buffer do tipo array de bytes, respeitando o offset e o tamanho máximo passado
como parâmetro;
• void reset () - reposiciona a seqüência de entrada para a posição no momento que o método
mark foi chamado por último;
Vale lembrar que toda classe que estende a classe InputStream deve obrigatoriamente
implementar o método abstrato read () e opcionalmente algum outro, tal como Glose () e available ().
java.io.OutputStream
• void close () -fecha a seqüência de saída e libera qualquer recurso do sistema associado com a
seqüência;
• void flush() - limpa a seqüência de saída e força quaisquer bytes de saída bufferizados a serem
escritos na saída;
• void write (byte [] b) - escreve a quantidade de bytes contidos no array de bytes b (b.length) na
seqüência de saída;
• void write (byte [] b, int off, int len) - escreve a quantidade de bytes definidos no parâmetro len,
lendo do array de bytes b, a partir do offset definido em off;
Vale lembrar que toda classe que estende a classe outputstream deve, obrigatoriamente,
implementar o método abstrato write (int b) e opcionalmente algum outro, tal como close ().
FilelnputStream e FileOutputStream
Vale lembrar que a existência dos arquivos, assim como a forma de referenciá-los dependerá do
sistema operacional utilizado.
java.io.FileInputStream
Essa classe é composta por três diferentes construtores, os quais permitem criar um objeto de
leitura de seqüência de dados a partir de um objeto da classe File, da classe FileDescriptor, ou uma
String com o caminho do arquivo a ser lido.
• FilelnputStream(File file)
• FilelnputStream(FileDescriptor fdObj)
• FilelnputStream(String name)
Além disso, a classe FilelnputStream sobrescreve alguns dos métodos herdados da classe
java.io.InputStream, levando em consideração alguns detalhes para a leitura em arquivos. Essa classe
ainda implementa mais dois métodos específicos à manipulação de arquivos:
java. io.FileOutputStream
A classe FileOutputStream é usada para a escrita de dados em uma seqüência de bytes associada
a um arquivo, indicado por um objeto das classes File ou FileDescriptor, ou pelo arquivo indicado
pelo nome (String). Quando o arquivo está ou não disponível, ou mesmo quando ele poderá ser
criado, dependerá da plataforma que está sendo executada. Algumas plataformas e sistemas
operacionais, em particular, permitem que um arquivo seja aberto para escrita por apenas um
FileOutputStream (ou outros objetos de escrita) por vez. Nessas situações, o construtor da classe
falhará ao tentar abrir o arquivo.
Essa classe é utilizada para a escrita de seqüências de bytes puros, tais como dados de imagens,
diferentemente da classe Filewriter utilizada para a leitura de seqüência de caracteres. Ela é
composta por cinco diferentes construtores, que permitem criar um objeto de escrita de seqüência de
dados a partir de um objeto da classe File, da classe FileDescriptor, ou uma String COM o ca minho
do arquivo a ser lido, indicando ou não se criará um novo arquivo ou aproveitará algum possível
existente:
• FileOutputStream(File file)
• FileOutputStream(FileDescriptor fdObj)
• FileOutputStream(String name)
Além disso, a classe FileOutputStream sobrescreve alguns dos métodos herdados da classe
java.io.outputstream, levando em consideração alguns detalhes para a escrita em arquivos. Essa
classe ainda implementa dois métodos específicos à manipulação de arquivos:
As classes DatalnputStream e DataOutputStream, membros do pacote j ava. io, são subclasses não
diretamente das classes InputStream e OutputStream e sim das classes FilterlnputStream e
FilterOutputStream, as quais representam classes para manipulação de seqüências de entrada e saída
que efetuam algum processamento nos dados antes de repassar ao componente interessado.
java.ia.DataInputStream
A classe DatalnputStream é composta por um construtor que recebe como parâmetro um objeto da
classe InputStream, o qual é utilizado para ler os dados antes da filtragem ser realizada. Além disso,
ela implementa alguns métodos para leitura direta dos tipos primitivos, garantindo a leitura correta
da quantidade de bytes de cada tipo, por exemplo, ao executar o método que lê um int,
automaticamente quatro bytes serão lidos da seqüência de entrada.
Os métodos para leitura dos tipos primitivos oferecidos pela classe DatalnputStreamSão:
java.ia.DataOutputStream
A classe DataOutputStream é composta por um construtor que recebe como parâmetro um objeto
da classe OutputStream, o qual é utilizado para escrever os dados após a filtragem ter sido realizada.
Além disso, ela implementa alguns métodos para escrita direta dos tipos primitivos, garantindo a
escrita correta da quantidade de bytes de cada tipo, por exemplo, ao executar o método que escreve
um int, automaticamente quatro bytes serão enviados à seqüência de saída.
Os métodos para escrita dos tipos primitivos oferecidos pela classe DataOutputStream São:
A classe DataOutputStream ainda possui os seguintes métodos para escrita de caracteres:
ObjectlnputStream e ObjectOutputStream
java.io.ObjectOutputStream
Para que se possa escrever uma objeto em um OutputStream, é necessário que a classe
correspondente implemente a interface java.io.Serializable, a qual indicará a possibilidade de
converter um objeto da mesma em um seqüência de bytes.
A classe de cada objeto serializada é codificada incluindo o nome da classe e sua assinatura, os
valores dos campos e arrays do objeto, e de qualquer outro objeto referenciado dentro da própria
classe.
java.io.ObjectInputStream
Um objeto de ObjectlnputStream deserializa dados primitivos e objetos previamente escritos
usando um ObjectOutputStream, em outras palavras, as classes ObjectOutputStream e
ObjectlnputStream em conjunto oferecem para uma aplicação a possibilidade de armazenamento
persistente de grafos de objetos quando utilizados em conjunto com Um FileOutputStream e
FilelnputStream respectivamente.
Esse mecanismo é factível com a execução do método readObject() que fará a leitura na stream,
recuperando os dados referentes a um determinado objeto de uma classe que deve implementar a
interface java.io.Serializable.
PrintStream
Diferentemente de outras streams de saída, a PrintStream nunca lança uma exceção IOException,
ao invés disso, em casos de situações excepcionais, ela simplesmente modifica uma propriedade (ou
flag) interna que pode então ser verificada por meio do método checkError. Opcionalmente, uma
PrintStream pode ser criada de forma a forçar a escrita (flush) dos dados na seqüência de saída após
cada chamada de um método que escreve um array de bytes, após a chamada de algum método
printin, ou depois da escrita de algum caractere de nova linha ou byte ('\n').
Todos os caracteres escritos por um objeto da classe PrintStream são convertidos em bytes
usando a codificação de caracteres padrão da plataforma. Para a escrita de caracteres ao invés de
bytes, é interessante utilizar a classe PrintWriter.
Prática
1. Crie a classe TesteSystemIn Com método main (...) que faz a leitura do teclado utilizando o
método read () do System. in.
2. Acrescente à classe do tópico anterior um loop para leitura até encontrar o "Enter" ('\n').
3. Crie a classe TesteDatalnputstream para a leitura de uma linha do teclado por meio do método
readLine ().
Streams de caracteres
Reader e Writer
A principal diferença da classe Reader para a InputStream é o método abstrato que deve ser
implementado por alguma subclasse, e que é específico para array de char:
Do outro lado, a principal diferença da classe writer para a OutputStream é o método abstrato que
deve ser implementado por alguma subclasse, o qual é específico para array de char:
FileReader e FileWriter
BufferedReader e BufferedWriter
java.io.BufferedReader
Faz a leitura de uma seqüência de entrada de caracteres, buferizando os caracteres com o objetivo
de melhorar a eficiência da leitura dos caracteres, arrays e linhas de texto.
0 tamanho do buffer pode ser especificado, ou então será utilizado um valor padrão, o qual é
grande o suficiente para manipular as principais leituras de textos.
Geralmente cada pedido por leitura realizado por um Reader causa um pedido direto à seqüência
de caracteres correspondente, o que prejudica o desempenho para dispositivos de entrada e saída
lentos. Para resolver o problema, são utilizados BufferedReaderers em conjunto com o Reader
padrão:
Programas que usam Data lnputstreams para leitura de textos devem ser localizados e substituídos
pelo apropriado BufferedReader. Substituir:
por
java.io.BufferedWriter
Faz a escrita para uma seqüência de saída de caracteres, buferizando os caracteres com o
objetivo de melhorar a eficiência da escrita dos caracteres, arrays e linhas de texto.
Assim como no BufferedReader, o tamanho do buffer pode ser especificado, ou então será
utilizado um valor padrão, o qual é grande o suficiente para manipular as principais leituras de
textos.
A classe BufferedWriter oferece ainda um método newLine () que usa o separador de linha da
própria plataforma em execução, cujo valor é definido em line.separator, uma vez que nem todas as
plataformas usam o caractere de nova linha ('\n') para a finalização das linhas.
Geralmente um Writer envia sua saída imediatamente para a seqüência de byte ou caractere a ele
associado. Com exceção de saídas para a tela, é aconselhável utilizar Um BufferedWriter em
conjunto com outros Writers para melhorar o desempenho do método write (), como é o caso dos
FileWriters e OutputStreamWriters.
PrintWriter
Os métodos dessa classe nunca lançam exceções de entrada e saída, então o programador pode
verificar a ocorrência de algum erro por meio do método checkError().
Manipulação de arquivos
Além das streams binárias e streams de caracteres específicas para manipulação de arquivos
mostrada anteriormente, a plataforma oferece uma classe File para outras operações que podem ser
realizadas no sistema de arquivos do sistema, incluindo verificação de permissões, criação de novos
arquivos, verificação de propriedades etc.
java.io.File
A classe File é utilizada para a manipulação abstrata de arquivos do sistema. Em outras palavras,
como cada sistema operacional possui algumas particularidades quanto ao acesso aos arquivos, essa
classe garante a compatibilidade a eles.
Essa classe possui dois campos essenciais para garantia da compatibilidade entre sistemas, os
quais contêm os caracteres para separação de diretórios e arquivos (separator) e separação de
caminho de busca - path (pathSeparator).
A classe File possui quatro construtores, podendo destacar um que recebe uma String como o
nome completo do arquivo. Ela possui ainda vários métodos, destacando:
Essa seção descreve o Java Collections Framework, que apresenta o que são as coleções e como
elas facilitam o trabalho, além de torná-lo melhor. Serão apresentados as principais interfaces,
implementações e algoritmos utilizados na manipulação de coleções e agrupamentos de objetos.
0 Java Collection Framework possui várias interfaces (mecanismos para manipulação de objetos)
e implementações (classes que implementam as interfaces) para o tratamento de coleções e mapas.
Uma coleção é um conjunto de objetos agrupados, e ela pode ser implementada de duas formas.
Como um conjunto, que não permite duplicatas, ou como uma lista que permite duplicatas.
Para se ter uma melhor idéia do que motiva a utilização das classes do Collections, é possível
destacar as seguintes limitações dos arrays:
O Framework Collections
• Interfaces: são representações de tipos abstratos de dados, que permitem coleções serem
manipuladas independentemente dos detalhes das representações. Em linguagem orientada a
objetos, essas interfaces são formas genéricas que representam contratos para manipulação de
coleções;
• Algoritmos: são métodos que realizam computações úteis, tal como busca e ordenação, em
objetos que implementam interfaces de coleções. Esses algoritmos são ditos como polimórficos,
pois o mesmo método pode ser usado por diferentes implementações da interface de coleção
apropriada.
• Set: é uma coleção que não permite elementos duplicados. Essa interface modela a abstração
matemática do conceito de conjunto e é usado para representar conjuntos, tais como cartas de
baralhos, turmas de um curso, ou processos em execução em uma máquina;
• List: é uma coleção encadeada, também chamada de seqüência, e pode conter elementos
duplicados. 0 usuário de uma List geralmente tem controle preciso sobre onde na lista cada
elemento está inserido, e pode acessar os elementos por meio de seus índices inteiros (posição).
Vale lembrar que o uso de listas é semelhante ao uso da classe Vector;
• Queue: é uma coleção usada para armazenar elementos antes de serem processados. Além das
operações básicas da Collection, as filas oferecem operações de inserção adicional, extração e
inspeção. Elas tipicamente, mas não necessariamente, ordenam os elementos como FIFO (First-
In-First-Out);
• Map: é um objeto que mapeia chaves para valores. Os mapas não podem conter chaves
duplicadas, sendo que cada chave pode mapear para um valor. A sintaxe de uso é semelhante à
classe Hashtable existente desde a versão 1.0.
As duas últimas interfaces de coleções são simplesmente versões ordenadas do Set e do Map:
• SortedSet: é um conjunto que mantém seus elementos em ordem ascendente, sendo que várias
operações adicionais são oferecidas para obter vantagens na ordenação. Os conjuntos ordenados
são usados por conjuntos naturalmente ordenados, tais como listas de palavras e lista de
convidados;
java.util.Collection
É a classe mais genérica para representar uma coleção de objetos e contém alguns métodos para
inserção, consulta e remoção de elementos, sendo esses herdados por todas demais subinterfaces e
suas respectivas implementações, podendo destacar:
java.util.List
A classe ArrayList implementa a interface List, e utiliza um array internamente para armazenar os
elementos da coleção. Essa é a classe pertencente ao Framework Collections mais próxima à classe
Vector.
java.util. Set
java.util.HashSet
A classe HashSet implementa a classe Set utilizando técnicas de função hash para organizar os
elementos internamente na estrutura de dados. Essa técnica aumenta um pouco o overhead na adição
de novos elementos, mas garante alto desempenho na busca de elementos quando já existem muitos
elementos inseridos.
java.util.TreeSet
A classe TreeSet implementa a classe Set utilizando a estrutura de dados em árvore, o que
permite buscas mais eficientes.
java.util.Map
A interface Map é utilizada para definir genericamente um mapeamento, ou seja, classes que
permitirão agrupar objetos associandoos a outros objetos como chaves. Os principais métodos
contratados pela interface são:
java.util.HashMap
A classe HashMap implementa a classe Map utilizando técnicas de função hash para organizar os
elementos internamente na estrutura de dados. Essa técnica aumenta um pouco o overhead na adição
de novos elementos, mas garante alto desempenho na busca de elementos quando já existem muitos
deles inseridos.
Java Database Connectivity ou JDBC é um conjunto de classes e interfaces (API) escritas em Java
que faz o envio de cláusulas SQL para qualquer banco de dados relacional. JDBC é uma interface
baseada em Java para acesso a banco de dados através de SQL. Usando JDBC, pode-se obter acesso
direto a banco de dados para utilização em applets, servlets, programas, desktop e quaisquer
aplicações que utilizem tecnologia Java.
Drivers e URLs
Para acessar um banco de dados via JDBC, ele deve ao menos possuir um driver JDBC. Caso não
exista um driver JDBC para o banco de dados que você deseja acessar, pode-se utilizar o driver
ODBC existente através do JDBC Bridge. Um driver pode ser dividido em quatro categorias, as
quais se diferenciam por métodos de acesso ao servidor de banco de dados.
1. Usam uma ponte para ter acesso a um banco de dados. Este tipo de solução requer a instalação
de software do lado do cliente. Também é conhecido como JDBC-ODBC Bridge.
2. Usam uma API nativa. Esses drivers contém métodos Java implementados em C/C++. Requer
software no cliente.
3. Oferecem uma API de rede via middleware que traduz requisições para API do driver
desejado. Não requer software do cliente;.
4. Drivers que se comunicam diretamente com o banco de dados usando soquetes de rede. É uma
solução Java pura. Não requer código adicional do lado do cliente.
É importante utilizar drivers do tipo 4 sempre que disponível. A principal vantagem de utilizar
drivers deste tipo é a portabilidade. Antes de aplicar drivers de outros tipos, procure nos sites dos
fabricantes de Banco de Dados, normalmente, os drivers são fornecidos gratuita mente. Para mais
informações sobre drivers JDBC e bancos de dados suportados visite o site JDBC da Sun
Microsystems na Web:
Toda conexão ao Banco de Dados é feita através de uma URL (Uniform Resource Locator),
indiferentemente do tipo de driver. Utilizando a URL, é possível carregar diferentes bancos em um
mesmo servidor dinamicamente. A sintaxe padrão é:
A JDBC provê um framework básico para acesso a dados. As principais classes e interfaces são:
• Driver;
• DriverManager;
• Connection;
• Statement;
• PreparedStatement;
• CallableStatement;
• ResultSet;
• ResultSetMetaData;
• Types.
Conhecendo esse breve resumo é necessário saber agora onde encontrar o devido driver para o
banco de dados a ser usado. Cada fabricante normalmente oferece o driver, ou é possível ainda
utilizar drivers de terceiros. Alguns drivers podem ser encontrados na página a seguir:
Graficamente é possível representar a utilização de Banco de dados como mostra a Figura 20.1.
A conexão com o banco de dados é representada por um objeto de alguma classe que implementa
a interface Connection. Cada driver específico para um determinado Banco de Dados irá
implementar essa classe, a qual só poderá ser instanciada com o auxílio do DriverManager, após a
carga do driver específico.
Exemplo:
Executando operações no Banco - j ava. sq1. Statement
Após a criação de uma conexão é necessário criar um objeto de uma classe que implementa a
interface Statement, a qual representa alguma operação em SQL a ser executada no Banco de Dados.
A interface Statement possui três principais métodos para execução de comandos em SQL:
Exemplos:
Exemplo:
Utilizando o java.sgl.PreparedStatement
A classe Prepare dstatement é utilizada para a preparação do SQL conforme os padrões adotados
por cada gerenciador de Banco de Dados. Esse mecanismo é realizado por meio de uma pré-
compilação realizada no SQL por essa classe, garantindo a formatação específica necessária.
Essa classe permite então uma maior portabilidade de códigos desenvolvidos em Java, para que
se possa executá-los utilizando também diferentes SGBDs. Isso se torna necessário, pois apesar de
SQL ser um padrão de linguagem, cada SGBD implementa com suas adaptações e características de
formatação dos tipos.
Exemplo: