Você está na página 1de 132

Érico Caselia Tavares de Mattos

Capítulo 1 - Sintaxe Java ............................................. 7

Apresentação .....................................................................................................8

Capítulo 2 - Introdução à programação ...................... 9

Paradigmas de programação ..........................................................................10

Paradigma Procedural .....................................................................................11

Paradigma orientado a objetos ......................................................................11

Linguagens x Ambientes de desenvolvimento ............................................. 12

Linguagens interpretadas x linguagens compiladas .................................... 13

Capítulo 3 - Plataforma Java ..................................... 15

Mitos da linguagem .........................................................................................16

Java 2 Standard Edition (J2SE) ......................................................................17

Capítulo 4 - Preparações para o desenvolvimento em Java


...................................... 23

Principais fontes de informações Java


.......................................................... 24

Lendo a API do Javadoc ..................................................................................25

Baixando e instalando o SDK .........................................................................25

Principais IDEs .................................................................................................26

Baixando e instalando o NetBeans ................................................................26

Executando alguns programas de teste ........................................................ 26

Capítulo 5 - Fundamentos da linguagem .................. 29

A Linguagem Java ...........................................................................................30

Palavras reservadas ........................................................................................30


Convenções de código ....................................................................................31

Comentários .....................................................................................................31

Capítulo 6 - Variáveis ................................................. 33

Tipos primitivos ...............................................................................................34

Referência (Reference) ....................................................................................35

Tipos de variáveis e seus escopos .................................................................35

Prática ...............................................................................................................36

Capítulo 7 - Operadores ............................................ 39

Operadores unários .........................................................................................40

Operadores aritméticos: +, -, *, / e % ...........................................................42

Operadores de deslocamento: «, » e »> ............................................43

Operadores de comparação: <, <=, >, >=, == e != .................................44

Operadores de comparação de tipos: instanceof ..................................45

Operadores bit a bit inteiros - lógicos booleanos ........................................47

Operadores condicionais ................................................................................48

Operadores de atribuição: *- /_ %=, +=, -_, «=, »=, »>=, &=, ^_ 1=

Operador condicional ternário (? :) ................................................................50

Capítulo 8 - Controle de fluxo - blocos

Finalização normal e finalização bruta de Statements

Blocos

Statements rotulados - label

Controle condicional - if else ....................................................................53

Controle switch .............................................................................................54


Controle while ...............................................................................................56

Controle do ... while

Controle for

Controle break

Controle continue

Controle return

Capítulo 9 - Arrays

Capítulo 10 - Classes utilitárias .................................. 65

Classe String

Classe StringBuffer

Classe Math e StrictMath

Classe Arrays

Classe System

Classe Runtime

Capítulo 11 - Programação orientada a objetos com Java

Introdução à criação de classes

Capítulo 12 - Encapsulamento ................................... 85

G etters e Setters

Checkers e Converters

Modificadores de acesso

Capítulo 13 - Construtores e método finalize ......... 91

Criando construtores

Sobrecarga de construtores
Método finalize

Capítulo 14 - Modificador static ............................. 95

Atributo static

Método static

Bloco de código static - iniciador static

Representação do modificador static em UML

Capítulo 15 - Relacionamento entre classes .............. 99

Associação (simples)

Agregação

Composição

Herança (Generalização)

Interfaces e implementação (realização) ..................................................... 109

Dependência .................................................................................................. 112

Outros fatores envolvidos nos relacionamentos de classes ..................... 113

Capítulo 16 - Pacotes

Membros de um pacote

Organização dos pacotes no sistema de arquivos ..................................... 124

Declarações de pacotes

Trabalhando com classes de outros pacotes .............................................. 125

Detalhes da utilização de pacotes

Capítulo 17 - Arquivamento de bytecodes - JAR (Java ARchive)

Criando um j ar

Executando um arquivo jar


Capítulo 18 - Principais APIs

Introdução

Entrada e saída de dados - Input & Output ............................................ 134

Capítulo 19 - Coleções

O Framework Collections

j ava.util .Collection

java.util.List

j ava.util Set .

j ava.util .Map

Capítulo 20 - Java Database Connectivity (JDBC)

Drivers e URLs

O pacote j ava. sql

Conexão com o Banco - j ava. sgl.Connection .................................. 156

Executando operações no Banco - java.sql.Statement ................... 157

Listando uma Consulta - j ava. sgl.ResultSet .................................... 157

Utilizando o java.sql.PreparedStatement ...................................... 158


0 aprendizado de uma linguagem de programação envolve na verdade a assimilação de diversos
conceitos, ferramentas e técnicas. Para que se dê início aos estudos e aos primeiros passos na
programação é importante se basear em uma fonte de informação capaz de guiar de maneira fácil e
eficiente.

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:

• plataforma extensa com vários segmentos e subcategorias;

• não está associada a nenhuma ferramenta integrada de desenvolvimento (IDE);

• não possui arquivos de ajuda (help) que acompanham os kits de desenvolvimento;

• a documentação existente não está totalmente centralizada em um lugar;

• a mudança de paradigma de programação dificulta ainda mais o aprendizado.

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;

• ambiente de desenvolvimento - IDE;

• servidores de aplicação;

• sistemas gerenciadores de banco de dados - SGBDs;

• 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

Dentre os paradigmas de programação existentes, podem-se destacar os paradigmas procedural e


orientado a objetos, os quais compõem a grande maioria dos projetos de softwares comerciais
voltados a diferentes sistemas computacionais, desde celulares à supercomputadores. Outros
paradigmas tais como funcional e lógico são utilizados em um número reduzido de projetos em
aplicações científicas e de uso restrito como regras para sistemas de inteligência artificial.

As principais diferenças entre os paradigmas procedural e orientado a objetos são observadas na


organização do código e nas técnicas de modelagem do problema, características que estão dire
tamente associadas aos custos dos projetos e ao ciclo de vida do desenvolvimento de software.
LISP é um exemplo de linguagem que utiliza paradigma funcional.
PROLOG é um exemplo de linguagem que utiliza paradigma lógico.

Paradigma Procedural

Algumas características comuns deste paradigma são:

• conjunto de instruções organizado em blocos para executar determinada tarefa (funções);

• conjuntos de funções agrupadas por funcionalidade em bibliotecas;

• modelagem através de fluxograma e grande utilização de algoritmos;

• extremamente técnica.

Como exemplo desse paradigma, têm-se as linguagens Pascal, C, Perl e Basic.

Paradigma orientado a objetos

A orientação a objetos é o paradigma de programação predominante atualmente, e vem aos


poucos substituindo a programação procedural, que foi criada no início da década de 1960.

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.

Exemplos de linguagens orientadas a objetos são o SmallTalk, C++ e Java.

Linguagens x Ambientes de desenvolvimento

A partir do momento que as linguagens de programação passaram a despertar interesses


comerciais, as empresas responsáveis pela distribuição associaram a linguagem a um ambiente de
desenvolvimento, característica que à primeira impressão parece ser boa, mas que acaba
prejudicando em alguns pontos.

Existem diversas linguagens que são associadas a algum ambiente de desenvolvimento, como é o
caso da:

• Linguagem Basic que possui o MS Visual Basic, e


• Object Pascal que possui o Delphi. Por outro lado, há outras linguagens que estão desvinculadas
a ambientes de desenvolvimento, como por exemplo:

• Java

• PHP

• C/C++

As principais vantagens de uma linguagem ser desvinculada de ambiente de desenvolvimento são:

• você não depende de um único fornecedor de ambiente de desenvolvimento;

• não são utilizados formatos proprietários para representação intermediária;

• maior controle sobre o código produzido;

• a concorrência entre fornecedores gera melhorias nas ferramentas.

A desvinculação da linguagem ao ambiente de desenvolvimento socializa a linguagem de


programação, permitindo que o desenvolvedor possa escolher se quer ou não comprar um ambiente
de desenvolvimento, bem como qual comprar.

Apesar das vantagens de uma linguagem desvinculada de IDEs, um ambiente de desenvolvimento


proporciona maior facilidade para a escrita do código, depuração e construção de interfaces
gráficas.

Linguagens interpretadas x linguagens compiladas

Linguagens de programação são comumente divididas em linguagens interpretadas e compiladas.


Já existem algumas de nova geração com conceito híbrido.

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;

• nomes de variáveis, métodos e classes inválidas;

• seqüência de comandos inválidos "{" sem "}" correspondente.

Além de erros semânticos, incluindo:

• tipos e quantidade de parâmetros, retorno de funções etc;

• atribuição de um valor alfanumérico para uma variável inteira.

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:

• divisão por zero, operadores logicamente errados;

• operações com objetos não construídos (nulos).

Como exemplos dessas categorias de linguagens têm-se o Pascal, C/C++ e VB.

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.

0 processo adotado para implementação do modelo híbrido baseia-se na utilização de uma


representação intermediária denominada bytecode, que é gerada pelo compilador e interpretada no
momento da execução.

A plataforma Java utiliza essa abordagem, contendo os seguintes passos em seu processo de
desenvolvimento.

• arquivos com os códigos-fonte são armazenados como texto simples (.java);

• após a compilação dos fontes (.fava), são gerados os bytecodes (.class);

• 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 é uma linguagem direcionada para Web?

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.

Java é igual à JavaScript?

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 plataforma Java 2 Standard Edition (J2SE) oferece um ambiente completo para


desenvolvimento de aplicações em desktops e servidores e para a implantação em ambientes
embarcados. A plataforma J2SE também serve de fundação para a plataforma Java 2 Enterprise
Edition (J2EE) e Java Web Services.

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.

• Core Java: oferece funcionalidades essenciais para a escrita de programas poderosos


preparados para ambientes corporati vos em áreas-chave como acesso a banco de dados,
segurança, chamada remota de procedimento (RMI) e comunicações;

• 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).

Exemplos de aplicações Java

A seguir, imagens de alguns programas em Java:

Figura 3.2: Aplicação de console rodando em Linux.


Figura 3.3: Aplicação visual Swing.

Figura 3.4: Aplicação console rodando em Windows.

J2SE Runtime Environment e a JVM - Java Virtual Machine

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 é o ponto-chave da plataforma Java. Ela é o componente da tecnologia


responsável pela independência de hardware e sistema operacional, bem como do reduzido tamanho
dos códigos compilados e a habilidade de proteger os usuários de programas maliciosos.

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.

Java 2 Standard Development Kit (J2SDK)

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;

• Javadoc: gerador de documentação;

• Jar: ferramenta para criação de arquivos contendo grupos de classes;

• Javap: ferramenta para descompilar uma classe (disassembles);

• Javah: ferramenta para gerar cabeçalho em C de uma classe. Usado com JNI;

• Idlj: gera um correspondente Java de um arquivo IDL - Corba;

• Idb: fava debugger;

• Orbd: servidor de registro de objetos - Object Request Broker Daemon;

• Apt: ferramenta para processamento de anotações;

• 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.

Por que coleta de lixo?

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.

Com o Java, as ferramentas de desenvolvimento, o ambiente de desenvolvimento integrado, a


documentação e demais informações normalmente são encontrados separados e em diferentes locais.
Por exemplo, é possível utilizar uma implementação de uma JVM da BlackBox, um SDK da IBM,
referenciar a documentação da SUN e utilizar implementações de terceiros sem maiores problemas,
devido à padronização das especificações pela JCP (Java Community Process).

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.

Principais fontes de informações Java

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:

Lendo a API do Javadoc

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:

Esse conteúdo segue o padrão de apresentação do gerador de documentação que acompanha a


J2SDK - Javadoc, como é mostrado na Figura 4.1:

Figura 4.1: Documentação gerada pelo JavaDoc da API do Java.

Baixando e instalando o SDK

Para baixar o SDK 1.5 e instruções para instalação, acesse respectivamente os sites a seguir:

Agora teste a instalação e confira a versão, seguindo este passoa-passo:

1. Abra uma janela de console e execute o seguinte comando

fava -version
2. Algo semelhante com o texto a seguir deverá ser mostrado.

Principais IDEs

A seguir, uma lista com as principais IDEs, e onde encontrá-las:

• Netbeans, no site www.netbeans.org

• Eclipse, no site www.eclipse.org

• Jbuilder, no site www.borland.com

• Forte, no site www.sun.com

• Jedit, no site www.jedit.org

• Jdeveloper, no site www.oracle.com

Baixando e instalando o NetBeans

Para baixar o NetBeans 4.1 e também as instruções para instalação, acesse, respectivamente, os
sites a seguir:

Executando alguns programas de teste

Veja como executar um programa de terminal texto como teste:

1. Crie uma pasta para colocar o código.

2. Abra algum editor de texto simples e copie o seguinte código:

3. Salve o arquivo com o nome OiMundoApp.java.


4. Compile a classe em um console com o comando javac OiMundoApp.java. 0 comando javac
deverá estar no diretório de instalação do Java. Exemplo: C:IArquivos de Programas \Javalj2sdk
1.5.01 bin.

5. Verifique a criação do arquivo OiMundoApp.class.

6. Execute o teste com o comando java OiMundoApp.

Para executar um applet de teste, siga este passo-a-passo:

1. Abra algum editor de texto simples e crie o arquivo OiMundoApplet.html com o seguinte
código:

2. Crie o arquivo OiMundoApplet.java com o seguinte código:

3. Compile a classe em um console com o comando javac OiMundoApplet.java.

4. Verifique a criação do arquivo OiMundoApplet.class.

5. Execute o teste abrindo o arquivo OiMundo.html no navegador.

É possível encontrar diversos exemplos nos seguintes sites:


Para iniciar o estudo dos fundamentos da linguagem Java, é necessário primeiramente falar um
pouco a respeito da linguagem propriamente dita, de sua gramática, palavras reservadas e algumas
convenções do código.

A Linguagem Java

A linguagem de programação Java é concorrente, baseada em classes, orientada a objetos e de


propósito geral. Ela foi desenvolvida para ser simples o suficiente de maneira que vários
programadores possam adquirir fluência no uso. A linguagem Java, quanto à sintaxe, é semelhante ao
C e C++, contudo, é organizada de maneira diferente, com um número de aspectos do C e C++
omitidos e algumas idéias de outras linguagens incluídas. A linguagem Java tem o objetivo de ser
uma linguagem para produção, assim como C, e não uma linguagem de pesquisa.

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

A programação em Java não exige padronização de nomes de classes e variáveis, porém, há


convenções para forma de nomeá-los de maneira que outros programadores tenham maior facilidade
em entender o sistema. Dentre as convenções que serão apresentadas e utilizadas no decorrer do
curso, destacamos as seguintes:

• o nome de pacotes contendo domínio da empresa desenvolvedora e identificadores deve estar


em letras minúsculas (Exemplo: br. com.tdmultimidia.teste). Uma observação importante é que,
não se pode utilizar pacotes iniciados com java, cujo uso é reservado para as bibliotecas
padrões;

• 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:

1. /* texto */ Um comentário tradicional: todo o texto entre os caracteres ASCII /* até os


caracteres ASCII */ é ignorado (assim como no C e C++).

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).

Tipo byte e números inteiros

• 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 int (4 bytes), o valor padrão é zero, ou seja, 0;

• para o tipo long (8 bytes), o valor padrão é zero, ou seja, OL.

Números com ponto flutuante

• 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

• para o tipo char, o valor padrão é o caracter nulo, ou seja, 1\ u0000'.

Booleanos

• para o tipo boolean, o valor padrão é false.

Referência (Reference)
A variável do tipo referência pode armazenar um dos seguintes valores:

• uma referência nula - nu11;

• uma referência para qualquer objeto cuja classe seja compatí-

vel de atribuição com o tipo da variável.

Para todas variáveis do tipo referência o valor padrão é nu11.

A referência é semelhante ao conceito de ponteiro da linguagem C++, mas, diferentemente da


linguagem C++, toda e qualquer variável que contenha objetos é, na realidade, uma referência para
uma instância da classe ou subclasse de seu tipo, não havendo, então, a diferenciação de variável de
classe e ponteiro para classe como no C++.

Veja o exemplo a seguir:

Tipos de variáveis e seus escopos

Há um total de sete categorias de variáveis quanto ao escopo e utilização:

1. Variável de classe: é um campo declarado usando a palavrachave static dentro de uma


declaração de classe, ou com ou sem a palavra-chave static dentro de uma declaração de interface.
Uma variável de classe é criada quando sua classe ou interface é preparada e é iniciada com um
valor-padrão. A variável de classe termina sua existência quando sua classe ou interface é
descarregada.

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.

5. Parâmetros construtores: nomeiam os valores dos argumentos passados para um construtor.


Para todo parâmetro declarado em um construtor, uma nova variável de parâmetro é criada toda vez
que uma expressão de criação de instância de classe ou chamada explícita do construtor chama o
construtor. A nova variável é iniciada com o valor do argumento correspondente passado na
expressão de criação ou invocação do construtor. 0 parâmetro construtor deixa de existir quando a
execução do corpo do construtor é finalizada.

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

Para a prática inicial do uso de variáveis prepare os seguintes testes:

1. Crie e teste o seguinte programa de teste de variáveis primitivas (TesteTiposPrimitivos.java):


2. Crie e execute a seguinte classe para teste de referência (TesteReferencia.java):

3. 0 resultado deve ser:


4. Agora, crie a seguinte classe para testar o escopo das variáveis a seguir:

5. 0 resultado deve ser:


A linguagem Java possui 37 operadores, os quais são representados por um ou mais caracteres
ASCII formando um token e que representam algum tipo de operação em um, dois ou três argumentos.

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:

Operadores de incremento e decremento: ++, --

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.

Se o operador de incremento ou decremento é prefixado, a variável tem seu valor modificado


antes do uso para atribuição ou com paração, caso o operador seja pós-fixado, o valor antigo é que
será utilizado para a atribuição.

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:

Operadores de representação de sinal: + e -

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

Operador de conversão: Cast

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 /

0 operador binário * realiza uma multiplicação, produzindo um produto dos operandos. A


multiplicação é uma operação comutativa se os operandos não tiverem efeitos de posição.
Enquanto a multiplicação é associativa para operandos inteiros do mesmo tipo, em pontos
flutuantes eles não são associativos.

0 operador binário / produz o quociente dos operandos, sendo o da esquerda o dividendo e o da


direita o divisor.

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:

Operadores de deslocamento: «,»e»>

Os operadores de deslocamento incluem o deslocamento para esquerda «, deslocamento para a


direita sensível ao sinal », e deslocamento à direita insensível ao sinal »>.

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.

No momento da execução, operações de deslocamento são realizadas na representação inteira de


complemento de dois do valor do operando à esquerda, ou seja, considera-se a representação de
sinal negativo do mesmo.

0 valor de n«s é o n com deslocamento à esquerda de s posições de bits. Isso equivale a


multiplicar por 2 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 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:

Operadores de comparação: <,<=,>,>=,==e !=

Os operadores de comparação se utilizam de dois operandos do tipo numérico primitivo, gerando


um erro de compilação em caso contrário. Toda expressão de comparação retorna um valor booleano
constante, a qual poderá ser utilizada em controles de fluxos ou atribuições em variáveis do mesmo
tipo.

Os operadores seguem os seguintes comportamentos:

• 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:

Operadores de comparação de tipos: instanceof

0 operador de comparação de tipos de referência instanceof tem a seguinte sintaxe:

ExpressãoRelacional instanceof TipoReferência

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.

No momento da execução, o resultado de um operador instanceof será true se o valor da


ExpressãoRelacional for não nula (null) e seu tipo puder sofrer uma conversão (cast) para o
TipoReferência sem lançar uma exceção do tipo ClassCastException, caso contrário retorna false.

Se uma conversão da ExpressãoRelacional para TipoReferência não puder ser executada, a


expressão produzirá um erro de compilação, uma vez que essa situação jamais resultará em um valor
verdadeiro true.

Considere o seguinte programa de exemplo:


Esse exemplo resulta em dois erros de compilação. A conversão (Point) e é incorreta, pois
nenhuma instância de Element ou de uma subclasse sua poderá ser uma instância da classe Point ou
de alguma subclasse de Point. A expressão instanceof também é incorreta, exatamente pelo mesmo
motivo.

Por outro lado, se a classe Point for uma subclasse de Element, como no código a seguir:

Então a conversão será factível, compilando e realizando em tempo de execução a expressão


instanceof. A conversão (Point) e jamais lançará uma exceção, pois nunca será executada, pois a
condição do laço if não será satisfeita.

Operadores bit a bit inteiros - lógicos booleanos

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.

Exemplo: a 1 b & c= a 1 (b & c)

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.

Os operadores inteiros bit a bit realizam as seguintes operações:

• 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:

Operadores AND, OR e XOR booleanos lógicos (&, I, ^)

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

Operador AND (&&)

0 operador && é semelhante ao £r, diferenciando apenas em duas questões:

1. 0 operador && só é aplicável a operandos lógicos.

2. Com o operador &&, o operando da direita só será avaliado se o operando da esquerda for
verdadeiro (troe).

0 operador é totalmente associativo, respeitando os efeitos de ambos os lados e seus valores


resultantes, por exemplo:

Operador OR (11)

0 operador 11 é semelhante ao I, diferenciando-se apenas em duas questões:

1. 0 operador 1 1 só é aplicável a operandos lógicos.

2. Com o operador 11 o operando da direita só será avaliado se o operando da esquerda for falso
(false).

0 operador é totalmente associativo, respeitando os efeitos de ambos os lados e os seus valores


resultantes, por exemplo:

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.

Atribuição composta (op=)

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:

Operador condicional ternário (? :)

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.

0 operador de atribuição condicional é sintaticamente associativo à direita, sendo avaliado do


extremo à direita para a esquerda, por exemplo: a?b:c?d:e?f:g significa o mesmo que a?b: (c?d: (e?f:
g) ).

0 operador de atribuição condicional possui três operandos, o operador ? aparece entre a


primeira e a segunda expressão e o aparece entre a segunda e terceira expressão.

No momento da execução, o primeiro operando da expressão condicional é avaliado inicialmente,


efetuando uma conversão para booleano se necessário, utilizando o valor resultante boolean para
escolher um dos demais operandos como segue:

• se o valor do primeiro operando for true, então o segundo operando é escolhido;

• se o valor do primeiro operando for false, então o terceiro operando é escolhido.

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.

Finalização normal e finalização bruta de Statements

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 é uma seqüência de comandos ou statements, declarações de classes locais e


declaração de variáveis locais sem chaves.

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.

Statements rotulados - label

Os comandos e statements podem ter um rótulo prefixado. 0 identificador é declarado como um


rótulo de qualquer comando contido imediatamente após ele. Apesar de se poder rotular qualquer
comando, é mais comum rotular controles de repetição e blocos entre chaves.
Exemplo:

Esse exemplo imprime abd na tela.

Controle condicional - if else

0 controle if permite a execução condicional de um statement ou a escolha condicional de dois


statements, executando um ou outro, mas nunca os dois.

0 comando tem a seguinte sintaxe:

if ( Expression ) StatementNoShortlf else StatementNoShortlf

A Expression deve ser do tipo boolean OU Boolean, ou será gerado um erro de compilação.

0 comando if-then é executado primeiro avaliando a Expression e realizando a seguinte seleção:

• se o valor for true, então o conteúdo do primeiro statement é executado;

• se o valor for false, então nada mais é executado e o comando termina normalmente.

Já o comando if-then-else é executado primeiro avaliando a Expression e realizando a seguinte


seleção:

• se o valor for true, então o conteúdo do primeiro statement é executado;

• se o valor for false, então o conteúdo do segundo statement é executado.


Exemplo:

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 rótulo poderá ser null;

• 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:

Resultado sem 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.

Sintaxe: while ( Expression ) Statement

Exemplo:
0 resultado será:

Controle do ... while

0 controle do while executa o statement e a expressão condicional repetidamente até que o valor
da expressão seja false.

Sintaxe: do Statement while ( Expression ) Exemplo 1:

0 resultado será:

Exemplo 2:

Controle for

0 controle for tem duas formas de uso:

1. 0 controle for básico.


2. 0 controle for diferenciado.

Controle for básico

0 controle for básico possui três expressões de controle:

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.

Dessa forma, o laço se repetirá até que a Expression seja false.

Sintaxe:

Exemplo:

Controle for diferenciado

0 controle de laço de repetição for diferenciado é uma funcionalidade acrescida no Java 1.5 e é
apresentado como a seguir:

for ( VariableModifiersopt Type Identifier: Expression)

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.

0 resultado de um controle de laço for diferenciado é traduzido em um controle for comum da


seguinte forma:
Exemplo:

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:

Arrays unidimensionais - vetores

São aqueles que possuem uma dimensão:

Exemplo:
0 resultado será:

Arrays bidimensionais - matrizes

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

Os membros de uma variável do tipo array são as seguintes:

• 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.

Podem-se destacar as classes String, StringBuffer, Math, Arrays, System e Runtime.

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

A classe string já incorpora inúmeros métodos que implementam funcionalidades comumente


utilizadas para manipulação de strings. Dentre os existentes, destacamos os seguintes:
Os métodos oferecidos não se limitam nos apresentados aqui, podendo também existir algumas
variações dos mesmos, além de outros métodos com diferentes funcionalidades. Vale lembrar que a
API da classe String deve ser consultada sempre que necessitar de alguma operação sobre strings,
para que não se desenvolva métodos que já são oferecidos pela própria classe String.

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

A classe StringBuffer implementa uma seqüência de caracteres mutáveis. Um string buffer é


semelhante a uma String, mas pode ser modificada. Em qualquer momento é sabido que um objeto da
classe StringBuffer possui alguma seqüência particular de caracteres, mas o comprimento e o
conteúdo da seqüência podem ser modificados a qualquer momento por meio da chamada de
determinados métodos.

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.

0 compilador usa os buffers de strings para implementar o operador binário de concatenação de


string +. Como no exemplo a seguir:

Que é compilado como no equivalente:

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.

Classe Math e StrictMath

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.

Diferentemente de alguns métodos numéricos da classe StrictMath, todas implementações das


funções equivalentes da classe Math não são definidas para retornar resultados exatamente bit por
bit. Esse relaxamento permite melhor desempenho nas implementações enquanto a StrictMath está
direcionada para uma qualidade de resultado mais preciso.

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

A linguagem de modelagem unificada UML (Unified Modeling Language) é uma linguagem


padronizada industrial para descrever, visualizar e documentar sistemas orientados a objetos (oo). A
UML utiliza uma coleção de variados diagramas para diferentes finalidades. Cada tipo de diagrama
modela um aspecto particular de desenvolvimento 0o de uma maneira visual fácil de entender.

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;

• a identidade de um objeto é a forma como se distinguem dois objetos com mesmo


comportamento e mesmo estado.

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:

Exemplo: Core Java, R$ 150,00; Camiseta, R$ 15,00 etc.

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).

Portanto, podemos especificar a classe produto da seguinte maneira:

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 possuem estado, comportamento e identidade. 0 estado é mantido em um ou mais


atributos, o comportamento é implementado através de métodos e a identidade é a referência
exclusiva que cada instância tem. Enquanto os atributos são variáveis contendo alguma informação
pertinente ao objeto, um método é uma função associada a ele.

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

Outro exemplo de classe normalmente utilizada é a classe de manipulação de números complexos,


os quais são compostos por uma parte real e outra imaginária.
Representação de classes em UML

UML é a Modeling UnifiedLanguage, padrão para a representação de modelagens utilizadas no


desenvolvimento orientado a objetos. Um dos componentes mais difundidos de UML é o seu
diagrama de classes.

A representação de classes em diagramas UML contempla três tipos básicos de informação: o


nome da classe, os seus atributos e os seus métodos. Graficamente, um retângulo com três
compartimentos internos representa esses grupos de informação, como ilustrado a seguir para uma
classe Aluno:

A especificação de uma classe é composta por três regiões:

• Nome da classe: um identificador para a classe, que permite referenciá-la posteriormente -- por
exemplo, no momento da criação de um objeto;

• Atributos: conjunto de propriedades da classe;

• 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.

A declaração segue a sintaxe abaixo:

• 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.

0 indicar de tipo tipovariavel pode conter um dos seguintes itens:

• palavra-chave de um tipo primitivo (Exemplo: byte, int, long etc.);

• nome de uma classe que esteja no mesmo pacote ou cujo pacote já tenha sido importado;

• nome completo de uma classe, contendo seu pacote (Exemplo: java.util.Hashtable).

Exemplo:

Acessando atributos a partir de outras classes

Para acessar os atributos de uma classe a partir de outra classe alguns dos seguintes pré-
requisitos são necessários:

• o atributo tem o modificador public;

• 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:

• para variável de classe: Nomeclasse.atributo;


• para variável de instância: nomeObjeto.atributo.

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:

Representando atributos em UML

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:

• nome: um identificador para o atributo;

• tipo: o tipo do atributo (inteiro, real, caráter, outra classe etc.);


• valor_default: opcionalmente, pode-se especificar um valor inicial para o atributo;

• visibilidade: opcionalmente, pode-se especificar o quão acessível é um atributo de um objeto a


partir de outros objetos.

Os valores possíveis são:

• - (privativo), nenhuma visibilidade externa;

• + (público), visibilidade externa total;

• # (protegido), visibilidade externa limitada.

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).

A declaração segue a sintaxe a seguir:

• 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.

Observação: outros detalhes serão vistos em seção a seguir.

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:

• se o método não retorna valor, seu tipoRetorno deve ser void;

• 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:

Acessando métodos a partir de outras classes

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 public;

• 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 estático: NomeClasse.metodo ( [varl] [,var2] *;

• para método não estático (de instância): nomeObjeto. metodo ([varl] [,var2] *).

Exemplo:

Representando métodos em UML

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:

• nome: um identificador para o método;

• tipo: quando o método tem um valor de retorno, o tipo desse valor;

• 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:

Passagem de parâmetro de tipos primitivos

A passagem de parâmetros de tipos primitivos para a execução de um método é realizada por


meio da cópia do valor para uma variável local do método, cuja existência se mantém durante a
execução do mesmo.

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)

A passagem de parâmetros de tipos referência para a execução de um método é realizada por


meio da passagem da cópia da referência ao objeto, ou seja, não é realizada a cópia do objeto
propriamente dito. Sendo assim, qualquer método acessado ou atributo modificado pela variável
local do método efetuará alterações no mesmo objeto utilizado para a chamada.

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.

0 encapsulamento é realizado utilizando técnicas para criação de métodos que retornam e


modificam cada atributo conforme a necessidade. São os chamados Getters e Setters, além de
métodos específicos para confirmar características e métodos de conversão.

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.

De uma forma geral, temos:

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.

De uma forma geral, temos:


Checkers e Converters

Outra técnica de encapsulamento é a criação de métodos para verificar alguma característica do


objeto, ou para convertê-lo em outro tipo conhecido.

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.

Observação: é reconhecido na verdade como um modificador protected modificado, o qual


permite que as classes do mesmo pacote tenham acesso ao recurso, mas suas classes filhas
(subclasses ou extensões) não.

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.

Quando utilizar private e quando utilizar public

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

A declaração de um construtor se assemelha a de um método comum com exatamente o mesmo


nome da classe, exceto que não há a especificação de um retorno, uma vez que ele só apresenta os
comandos a serem executados na criação de um objeto da classe. A sintaxe é a seguinte:

De uma forma geral, todas as classes que não declararam nenhum construtor são vistas como
tendo um construtor sem parâmetros:

Podendo ser instanciadas da seguinte forma:

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.

Quando se faz o uso de mais de um construtor, ou seja, utiliza a sobrecarga de construtores, é


possível chamar um construtor por meio de outro, utilizando o comando this ([param] *). Isso evita a
repetição de código.

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

Um atributo declarado com static tem duas principais características:

1. Pode ser acessado sem a criação de instâncias do objeto (Exemplo:


NomeClasse.atributoStatic).

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.

Um método declarado com static tem duas principais características:

1. Pode ser acessado sem a criação de instâncias do objeto (Exemplo: NomeClasse.metodoStatic


() ).

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.

Bloco de Código static - iniciador static

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:

Representação do modificador static em UML

A representação em UML de um atributo ou método estático é feita simplesmente utilizando a


assinatura sublinhada. Assim sendo, para a classe Pessoa, a representação UML correspondente
seria:
Para a engenharia de software, assim como para a UML, há diferentes tipos e conceitos de
relacionamentos entre classes, incluindo associação, agregação, composição, dependência, herança e
realização (implementação). Em outras palavras, em um diagrama de classes UML, tendo uma linha
entre duas ou mais classes, pode-se considerar a existência de um relacionamento.

Dentre os relacionamentos citados, é possível dividi-los em subcategorias como mostrado a


seguir:

• Associação;

• associação (simples);

• agregação;

• composição;

• Generalização;

• herança;

• implementação (realização);

• Dependência.

Um dos grandes problemas de conflitos conceituais na representação em UML é saber exatamente


qual tipo de relacionamento que se trata, uma vez que existe uma grande semelhança entre eles. Esses
relacionamentos serão apresentados individualmente, com exemplos de códigos e modelos, com o
objetivo de auxiliar a documentação da programação orientada a objetos com Java.

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.

Representação de associação na UML

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.

Figura 15.1: Associação simples.

Representação de associação em Java

Em Java, assim como em qualquer linguagem de programação orientada a objetos, a associação é


implementada por meio de um atributo tipo referência na classe que contém a outra classe.

Exemplo:

Encapsulamento de associações

0 encapsulamento de atributos utilizados na implementação de associações é realizado de duas


formas:

• 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

Agregação é um relacionamento do tipo todo/parte. Esse relacionamento é uma especialização da


associação e define normalmente uma associação cuja classe contida normalmente é a parte de um
todo, ou seja, normalmente a classe que contém é um grupo de objetos da outra. Vale lembrar também
que um objeto da classe parte integrante pode existir sem o todo, sendo que este último apenas agrega
as partes já existentes.

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.

Representação de agregação na UML

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.

Figura 15.2: Agregação.

Representação de agregação em Java

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.

Objetivando manter as técnicas de encapsulamento e permitir a adição, remoção e verificações


das partes agregadas ao todo, alguns métodos são comumente encontrados, permitindo adicionar
novos objetos ao atributo de coleção. Esses métodos são equivalentes aos encontrados em classes de
coleções e listas, como os seguintes:

• 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

A composição, assim como a agregação, é um relacionamento do tipo todo/parte. A principal


diferença é que, na composição, a existência da parte é controlada pelo todo. Ou seja, o todo, pode
ter a responsabilidade de criar ou destruir a parte diretamente, ou pode aceitar uma parte já existente,
e futuramente passá-la para outro todo assumir o controle por ela.

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.

Representação de composição na UML

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.

Figura 15.3: Composição.

Representação de composição em Java

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

Devido às semelhanças das representações das composições e agregações em Java, as técnicas de


encapsulamento são praticamente as mesmas, sendo realizadas como apresentadas na seção
Encapsulamento de agregações, por meio dos métodos add(Tipo obj), para adição de novos objetos,
remove (Tipo obj) ou remove (int indice) para remover objetos já existentes e get(int indice) para
retornar um objeto existente.

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.

Generalização é o relacionamento entre uma classe e uma ou mais versões refinadas


(especializadas) desta classe. A classe sendo refinada é chamada de superclasse ou classe base,
enquanto que a versão refinada da classe é chamada uma subclasse ou classe derivada. Atributos e
operações comuns a um grupo de classes derivadas são colocados como atributos e operações da
classe base, sendo compartilhados por cada classe derivada. Diz-se que cada classe derivada herda
as características de sua classe base. Algumas vezes, generalização é chamada de relacionamento is-
a (é-um), porque cada instância de uma classe derivada é também uma instância da classe base.

Representação de herança na UML

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.

Figura 15.4: Herança.

Representação de herança em Java

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.

A extensão em Java é realizada utilizando a palavra reservada extends na declaração da classe


filha, indicando de qual classe é feita a extensão. Vale ressaltar que uma classe só pode estender
apenas uma outra classe, ou seja, não há o conceito de herança múltipla em Java, como em outras
linguagens orientadas a objetos.

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 public, a subclasse tem acesso ao recurso;

• modificador protected, a subclasse tem acesso ao recurso mesmo que esteja em um pacote
diferente da superciasse;

• modificador private, a subclasse não tem acesso ao recurso.

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.

Sobreposição de métodos de instância - overriding

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:

Sobreposição de métodos de classe - hiding

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:

Interfaces e implementação (realização)

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.

Representando interfaces e realizações na UML

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).

Definindo uma interface em Java

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:

• ao invés da palavra reservada class, utiliza-se a palavra interface;

• só pode conter atributos ou assinaturas de métodos, sem as respectivas implementações.

Exemplo:

Implementando uma interface em Java

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

São relacionamentos de utilização no qual uma mudança na especificação de um elemento pode


alterar a especificação do elemento dependente. A dependência entre classes indica que os objetos
de uma classe usam serviços dos objetos de outra classe.

Representação de dependência na UML

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.

Figura 15.6: Dependência.

Representação de dependência em Java

Normalmente, o uso de dependência está relacionado com a instanciação de uma classe A em um


método de outra classe B, ou quando uma classe A tem como parâmetro de um de seus métodos uma
referência para a classe B.

Exemplo:

Outros fatores envolvidos nos relacionamentos de classes

Os relacionamentos entre classes e, principalmente, a herança e extensão envolvem uma série de


fatores e detalhes que devem ter atenção especial, para que os recursos oferecidos sejam melhor
aproveitados.

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.

Modificador final na declaração de métodos

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.

Modificador final na declaração de atributos

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.

Classes abstratas X Interfaces

Um dos pontos-chave da programação orientada a objetos e, conseqüentemente, da programação


em Java é a questão de quando se utilizar classes abstratas e quando utilizar interfaces.
Como já dito anteriormente, as classes abstratas devem ser utilizadas quando não há a
possibilidade de implementar determinada funcionalidade em um momento do processo de
desenvolvimento do sistema, deixando para que a funcionalidade seja implementada por uma
subclasse que estenda a classe abstrata.

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 contém métodos abstratos;

• 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:

Conversão e Casting de objetos

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: atribuição

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.

0 código a seguir ilustra o formato geral de uma conversão de referência a objetos.

A atribuição anterior armazena em uma referência do tipo classeNovoTipo um objeto do tipo


ClasseTipooriginal. Contudo, esse código só compilará se a conversão seguir algumas regras.

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.

Regras de conversão de referências de objetos.

Para ilustrar essas regras, considere a árvore de hierarquia apresentada na Figura 15.7:

Figura 15.7: Hierarquia das Classes Cachorro, Gato e Papagaio.

0 código a seguir exemplifica alguns dos possíveis usos:


Casting de referências ao jetos

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.

Figura 15.9: Exemplo da área de memória durante a execução de um programa.

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.

Regras de casting de objetos aplicadas em tempo de compilação

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).

Por exemplo, na API do Java:

• 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.

Se o nome completo de um pacote é P, e Q e um subpacote de P, então P.Q é o nome completo do


subpacote Q.

Organização dos pacotes no sistema de arquivos

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

Além da organização de pastas/diretórios dos arquivos, é necessário declarar explicitamente que


uma classe pertence a um pacote. A declaração do pacote de uma unidade de compilação, ou arquivo
contendo classes Java, é realizada no início do arquivo utilizando a palavra reservada package
seguida do nome qualificado completo da classe.

Exemplo:

Executando classes que estão em pacotes

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:

Trabalhando com classes de outros pacotes

É possível utilizar de duas formas uma classe contida em outro pacote:

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()).

2. Importar no início do arquivo .fava as classes que serão utilizadas.

Há quatro tipos possíveis de declarações de importação: simples, sob demanda, estática simples
e importação estática sob demanda.

Importação de tipo simples

A importação de tipo simples tem o objetivo de importar uma determinada classe de um pacote.

Exemplo:

Importação de tipo sob demanda

A importação de tipo sob demanda permite importar, conforme a necessidade, todos os tipos
(classes) declarados em um determinado pacote.
Exemplo:

Importação estática simples

A importação estática simples tem o objetivo de importar todos os membros estáticos de uma
determinada classe de um pacote.

Exemplo:

Importação estática sob demanda

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:

Detalhes da utilização de pacotes

A utilização de pacotes exige a atenção do programador em alguns aspectos:

• 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:

Armazena duas classes em um arquivo chamado classes.jar:

Usa um arquivo mymanifest e arquiva todos os arquivos do diretório classes/ em classes.jar:

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.

Executando um arquivo jar

É 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

A programação em Java não envolve o aprendizado só da sintaxe da linguagem e dos conceitos de


programação a objetos. Além disso, é muito importante conhecer os conjuntos de classes e pacotes
distribuídos com a plataforma (API Java), para que se aproveite ao máximo o desempenho e o
reaproveitamento das implementações já existentes.

Dentre as principais APIs distribuídas com o ambiente de execução Java (JRE) e,


conseqüentemente com o kit de desenvolvimento J2SDK, podem-se destacar as apresentadas nesta
seção, incluindo as classes de manipulação de entrada e saída (input/output), framework Collections,
acesso a banco de dados (JDBC), interfaces gráficas (AWT e Swing).

Entrada e saída de dados - Input & Output

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.

A principal característica da manipulação de entrada e saída de informações da plataforma Java é


que, independente da origem e do destino dos dados, toda interação será realizada por objetos do
mesmo tipo. Essa interação é basicamente realizada através das classes java.io.OutputStream e
java.io.InputStream, ou por alguma outra classe que as utiliza, implementando algum método mais
específico para determinados tipos de dados como é o caso das classes DatalnputStream e
ObjectOutputStream.

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

As classes InputStream e OutputStream, membros do pacote java.io, são classes abstratas de


manipulação de entrada e saída que genericamente apresentam métodos para qualquer interação com
dispositivos externos. Por serem abstratas jamais podem ser instanciadas diretamente, mas sim por
meio de gerenciadores ou utilizando algumas de suas subclasses.

java.ia.InputStream

Os métodos contidos na classe InputStream e, conseqüentemente, em todas suas subclasses, são


apresentados a seguir:

• 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;

• void mark(int readlimit) - marca a posição de leitura corrente na seqüência de entrada;

• boolean markSupported () - testa se a seqüência de entrada suporta os métodos mark e reset;

• 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;

• long skip (long n) - salta e descarta n bytes de dados da seqüência de entrada.

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

Os métodos contidos na classe Outputstream e, conseqüentemente, em todas suas subclasses, são


apresentados a seguir:

• 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;

• abstract void write (int b) - escreve o byte especificado na seqüência de saída.

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

As classes FilelnputStream e FileOutputStream, membros do pacote java.io, são subclasses


diretas das classes InputStream e OutputStream e são utilizadas para fazer a leitura e escrita de bytes
em algum arquivo no sistema de arquivo local.

Vale lembrar que a existência dos arquivos, assim como a forma de referenciá-los dependerá do
sistema operacional utilizado.

java.io.FileInputStream

A classe FilelnputStream permite a obtenção dos bytes de entrada de um arquivo já existente no


sistema de arquivos local, sendo utilizada para a leitura de seqüência de bytes puros, tal como dados
de imagens, diferentemente da classe FileReader utilizada para a leitura de seqüência de caracteres.

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:

• FileChannel getChannel () - retorna o objeto único da classe FileChannel associado à seqüência


de leitura de arquivos;

• FileDescriptor getFD () - retorna o objeto da classe FileDescriptor que representa a conexão


com o atual arquivo no sistema de arquivo.

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(File file, boolean append)

• FileOutputStream(FileDescriptor fdObj)

• FileOutputStream(String name)

• FileOutputStream(String name, boolean append)

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:

• FileChannel getChannel () - retorna o objeto único da classe FileChannel associado à seqüência


de leitura de arquivos;

• FileDescriptor getFD () - retorna o objeto da classe FileDescriptor que representa a conexão


com o atual arquivo no sistema de arquivo.
DatalnputStreameDataOutputStream

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:

A classe Datalnputstream ainda possui métodos para leitura de caracteres:

E métodos para leitura completa de arrays de bytes:

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

As classes ObjectlnputStream e ObjectOutputStream, membros do pacote java.io, são subclasses


diretas das classes Inputstream e OutputStream e são utilizadas para fazer a leitura e escrita de
objetos de qualquer classe derivada de Object em alguma seqüência de bytes.

java.io.ObjectOutputStream

Um ObjectOutputStream permite a escrita de qualquer tipo primitivo e grafos de objetos Java


para um OutputStream básico. Os objetos podem ser então lidos (reconstituídos) usando um
ObjectlnputStream. Objetos de armazenamento persistente podem ser criados, por exemplo,
utilizando arquivos como stream de saída. Para streams de rede, os objetos podem ser reconstituídos
na outra máquina ou em outro processo após a transferência.

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.

De uma forma geral, como a classe java.io.ObjectOutputStream implementa a interface Datalnput,


ela também possui os métodos para escrita de tipos primitivos existentes na classe
DataOutputStream, além do método de escrita de objetos:

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

A classe PrintStream acrescenta algumas funcionalidades a alguma seqüência de saída (output


stream), criando a habilidade de imprimir representações de texto de diferentes dados de forma
conveniente.

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 ().

4. Crie a classe TesteLeituraTeclado, que lê do teclado um número que representa a quantidade


de nomes que deverão ser lidos, lendo-os e imprimindo-os em seguida:
S. Crie a classe TesteFilelnputstream para leitura de um arquivo, contendo um dos segmentos de
código a seguir:

6. Crie a classe TesteFileOutputStream para escrita de um arquivo, contendo um dos segmentos


de código a seguir:

Streams de caracteres

As classes de manipulação de seqüência de caracteres, denominadas stream de caracteres, são


utilizadas para operações de entrada e saída de informações do tipo texto. Essas classes são
extensões das classes de manipulação de stream binárias, levando em consideração alguns
tratamentos específicos de caracteres para cada plataforma.

Dentre as classes de tratamento de seqüências de caracteres, podem-se destacar as duas mais


básicas chamadas de Reader e Writer, utilizadas para a leitura e escrita respectivamente. Há ainda os
leitores e escritores específicos para arquivos (FileReader e FileWriter), os que utilizam técnicas de
armazenamento antes da operação de entrada e saída (BufferedReader e BufferedWriter) e a utilizada
para equivalência ao PrintStream para caracteres (PrintWriter).

Reader e Writer

São classes abstratas semelhantes às classes InputStream e OutputStream, com o objetivo


específico de leitura e escrita de dados tipo texto.

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

Semelhantemente às streams binárias, a plataforma Java oferece classes para manipulação de


streams de caracteres específicos para arquivos. Essas classes possuem os mesmos métodos de
leitura e escrita herdados de suas superclasses Reader e Writer, acrescentando-as apenas os
construtores que recebem um objeto do tipo File, OU FileDescriptor, Ou uma String Com o nome do
arquivo a ser aberto.

BufferedReader e BufferedWriter

As classes BufferedReader e BufferedWriter são utilizadas para melhorar o desempenho da


leitura e da escrita de informações em algum dispositivo de entrada e saída. A técnica utilizada de
bufferização permite a leitura direta de certa quantidade de dados antes de efetuar a conversão de
bytes para caracteres e vice-versa.

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

A classe java.io.PrintWriter imprime representações formatadas de objetos para uma seqüência


de saída de texto. Essa classe implementa todos os métodos print encontrados na PrintStream, não
contendo métodos para escrita de bytes puros, pelo qual um programa poderia usar streams de bytes
não codificados.
Diferentemente da classe PrintStream, se o flushing automático estiver habilitado, o mesmo só
será realizado quando um método printin () for chamado, ao invés de ser sempre realizado ao
encontrar um caractere de nova linha na saída. Isso ocorre, pois o método printin () usa a noção de
nova linha da própria plataforma e não apenas utiliza o caractere de nova linha.

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.

A linguagem de programação Java já oferecia recursos de agrupamento de objetos desde antes da


versão 1.2, a qual foi realmente incorporado o Framework Collections, porém, essas classes, vector,
Hashtable, e Array não obedecem os mesmos padrões e, por isso, é aconselhado não utilizarem mais
em função das pertencentes ao referido Framework.

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.

Já um mapa é um conjunto de chaves com os valores correspondentes, como uma listagem de


alunos em uma turma em que a chave é o número na lista de chamada e o valor é o nome do aluno.

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:

• um array não pode ter o tamanho modificado depois de criado;

• somente pode conter elementos de um tipo;

• para inserir ou retirar um elemento é necessário modificar a posição de outros elementos.

O Framework Collections

0 Framework Collections é uma arquitetura unificada para representação e manipulação de


coleções, sendo composto por:

• 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;

• Implementações: são implementações concretas de uma interface de coleção, em essência, são


estruturas de dados reusáveis;

• 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.

A hierarquia de interfaces existente é como a apresentada na Figura 19.1:

Figura 19.1: Hierarquia das interfaces do Framework Collections.

A seguir, suas características:

• collection: é a raiz da hierarquia das coleções e representa um grupo de objetos, conhecidos


como elementos da coleção. A interface collection é o mais comum denominador para todas
implementações de coleções, e é usada para passar como parâmetros e manipular de maneira
mais genérica desejada. Alguns tipos de coleções permitem elementos duplicados, e outros não.
Alguns são ordenados e outros desordenados. Não há no Java nenhuma implementação direta
dessa interface, apenas implementações de suas subinterfaces, como é o caso de Set e List;

• 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;

• SortedMap: É um Map que mantém seu mapeamento em ordem ascendente de chave. É o


elemento Map análogo ao SortedSet, sendo usado para coleções naturalmente ordenadas por
pares, de chave/valor, por exemplo, dicionários, diretórios de telefones entre outros.

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

Essa interface estende a interface java.util.Collection, oferecendo os mesmos métodos herdados,


acrescentando alguns métodos específicos de uma lista para a manipulação da posição dos elementos
na mesma:
java.util.ArrayList

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

Essa interface estende a interface java.util.Collection, oferecendo os mesmos métodos herdados,


levando em conta apenas que conterá elementos não duplicados.

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.

0 maior diferencial do JDBC é a possibilidade de programar sem a necessidade de implementar


nenhum pacote de bibliotecas adicional, indiferentemente do SGBD (Sistema Gerenciador de Banco
de Dados), versão e/ou plataforma.

0 JDBC é a implementação de classes e interfaces da especificação da Sun, pelos fabricantes de


servidores de banco de dados ou por empresas de desenvolvimento de software. Cada
implementação é chamada de Driver. Cada driver pode ser implementado de maneira diferente,
desde que siga o que a especificação exige.

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 é:

Ou ainda mais detalhadamente para tipo quatro:

O pacote j ava . sq1

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;

• Data base Meta Data;

• ResultSetMetaData;

• Types.

Basicamente, o relacionamento entre as classes e interfaces funciona da seguinte forma:

No código, a classe do driver Database é carregada dinamicamente. Ao carregar a classe do


driver, a classe DriverManager registra uma instância do objeto driver, implementado pelo
fabricante do JDBC Driver para obter uma conexão através da classe Connection. Utilizando as
Interfaces Statement, PreparedStatement ou CallableStatement, pode-se atualizar dados ou executar
uma query de consulta de dados. No caso de executar uma query, uma instância do objeto ResultSet é
retornada contendo os dados desejados. Utili zando a interface Type pode-se determinar qual o tipo
de dado. Para informações sobre o database ou o resultado da pesquisa, utiliza-se as classes Data
base Meta Data e ResultSetMetaData.

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.

Figura 20.1: Diagrama das principais classes do JDBC.

Conexão com o Banco - java. sgl.Connection

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:

1. boolean execute(String sql) - executa algum SQL genérico, retornando um boolean.

2. int executeUpdate(String sql) - executa uma SQL de atualização (INSERT, UPDATE e


DELETE), retornando um int com o número de registros atualizados.

3. ResultSet executeQuery(String sql) - executa uma SQL de consulta (SELECT), retornando um


objeto de uma classe que implementa ResultSet.

Exemplos:

Listando uma Consulta - java.sgl.ResultSet

Ao executar um comando em SQL de consulta por meio do método executeQuery() de um


Statement, é obtido um objeto de uma classe que implementa a interface ResultSet, a qual permite
navegar nos resultados com métodos do tipo first () e next () e ler os valores dos campos dos
registros por meio de métodos no padrão getTipo () (Ex: getString (), getInt (), getDate () etc).

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:

Você também pode gostar