You are on page 1of 149

Apostila de Java

Introdução
Baseada nos 7 Primeiros Capítulos do
Livro Core Java – Fundamentals
de Cay S. HorstMann e Gray Cornel
Prof. Carlos Ribeiro
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 2
Índice
INTRODUÇÃO.............................................................................................................................................5
O Que é Java................................................................................................................................................5
Como Compilar e Executar um Programa Java...........................................................................................8
Applications.............................................................................................................................................8
Java Bemvindo ....................................................................................................................................8
Applets.....................................................................................................................................................9
IDENTIFICADORES, PALAVRAS-CHAVE, TIPOS E OPERADORES............................................13
Comentários...............................................................................................................................................15
Identificadores ...........................................................................................................................................15
Palavras-Chave..........................................................................................................................................15
Tipos de Dados..........................................................................................................................................16
Lógico – boolean...............................................................................................................................16
Texto – char e String......................................................................................................................16
Char ...................................................................................................................................................16
Strings................................................................................................................................................17
Inteiros – byte, short, int e long.................................................................................................20
Pontos Flutuantes – float e double .............................................................................................21
Float...................................................................................................................................................21
Double ...............................................................................................................................................22
Variáveis....................................................................................................................................................22
Designações e Inicializações .....................................................................................................................23
Conversões entre Tipos Numéricos ...........................................................................................................23
Constantes .................................................................................................................................................24
Variáveis de Classe....................................................................................................................................25
Operadores.................................................................................................................................................26
Exponenciação...........................................................................................................................................27
Operadores de Incremento e Decremento..................................................................................................27
Operadores Relacionais e Boleanos...........................................................................................................27
Parênteses e a Hierarquia dos Operadores.................................................................................................28
ENTRADA DE DADOS E FORMATAÇÃO DA SAÍDA........................................................................29
Lendo Dados..............................................................................................................................................29
Formatando a Saída ...................................................................................................................................30
PRINCIPAIS ESTRUTURAS DE PROGRAMAÇÃO EM JAVA.........................................................33
Controle de Fluxo......................................................................................................................................33
Comandos Condicionais ........................................................................................................................33
Loops Indeterminados ...........................................................................................................................35
Loops Determinados..............................................................................................................................36
O Comando Switch................................................................................................................................37
O Comando Break .................................................................................................................................38
Métodos de Classes (Funções Definidas pelo Usuário).............................................................................40
Recursividade ............................................................................................................................................43
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 3
ARRAYS ......................................................................................................................................................44
Copiando Arrays........................................................................................................................................45
Arrays como Argumentos..........................................................................................................................46
O Método Lenght ......................................................................................................................................46
Retornando um Array................................................................................................................................48
Arrays Multidimensionais .........................................................................................................................49
Determinando o número de linhas e de colunas ........................................................................................51
OBJETOS E CLASSES ..............................................................................................................................52
Terminologia .............................................................................................................................................53
Como Definir uma Classe..........................................................................................................................54
Relacionamentos entre Classes..................................................................................................................60
Diferenças entre a OOP e as Técnicas Tradicionais de Desenvolvimento de Software ............................62
Variáveis do Tipo Objeto ..........................................................................................................................62
A Classe GregorianCalendar .....................................................................................................................64
Alguns Métodos Definidos para a Classe GregorianCalendar...............................................................65
Métodos para Acesso e Mutação...............................................................................................................67
Objetos como Argumentos de Funções .....................................................................................................68
A Classe Date ............................................................................................................................................70
Construindo suas Próprias Classes ............................................................................................................72
Construtores...............................................................................................................................................75
Métodos Privados ......................................................................................................................................78
Métodos Construtores................................................................................................................................78
Construtores Default..............................................................................................................................80
Overloading...............................................................................................................................................80
O Objeto this .............................................................................................................................................83
O Método toString.....................................................................................................................................84
Blocos de Inicialização..............................................................................................................................85
Métodos e Campos Estáticos.....................................................................................................................86
Como Inicializar um Campo Estático........................................................................................................86
Como Compilar Programas .......................................................................................................................87
Packages ....................................................................................................................................................88
Utilizando Packages ..............................................................................................................................89
Como o Compilador Localiza os Packages ...........................................................................................89
O Escopo de um Package ......................................................................................................................89
Polimorfismo.............................................................................................................................................97
Como Impedir a Herança...........................................................................................................................99
Instanceof ................................................................................................................................................100
Casting.....................................................................................................................................................100
Classes do tipo “Abstract”.......................................................................................................................105
Métodos do Tipo Protected......................................................................................................................114
Object: A Superclasse Cósmica...............................................................................................................114
Vectors.....................................................................................................................................................120
Trabalhando com um Vector Existente ...................................................................................................122
Acessando um Elemento de um Vector...................................................................................................123
Inserindo e Removendo Elementos de um Vector...................................................................................126
Objetos Empacotadores ...........................................................................................................................131
Convertendo Strings em Números...........................................................................................................133
A Classe Class (Identificação de Tipo em Tempo de Execução) ............................................................134
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 4
INTERFACES E INNER CLASSES .......................................................................................................137
Utilizando uma Superclasse do tipo Abstract ..........................................................................................137
Utilizando uma Interface .........................................................................................................................140
Propriedades das Interfaces .....................................................................................................................143
A Interface Cloneable..............................................................................................................................145
BIBLIOGRAFIA.......................................................................................................................................149
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 5
INTRODUÇÃO
O Que é Java
Uma linguagem de programação desenvolvida com os seguintes objetivos:
• Criar uma linguagem orientada a objetos.
• Prover um ambiente interpretado por duas razões:
• Aumentar a velocidade do desenvolvimento – eliminando a necessidade do ciclo
compilação-ligação-carga-teste.
• Tornar o código portável.
• Eliminar práticas de programação que afetam a robustez do código.
• Aritmética de ponteiro.
• Alocação e desalocação de memória.
• Permitir que programas executem mais de uma thread.
A arquitetura de Java foi desenvolvida para alcançar estes objetivos. As seguintes
características da linguagem foram elaboradas para atingir estes objetivos:
• A Máquina Virtual Java
• Garbage collection
• Code security
A Máquina Virtual Java
A Especificação da Máquina Virtual Java define o termo Máquina Virtual Java (JVM)
assim:
“Uma máquina imaginária que é implementada emulando-a através de software
em uma máquina real. O código a ser executado por uma Máquina Virtual Java é
armazenado em um arquivo .class. Cada arquivo .class contém o código para no máximo
uma classe pública.”
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 6
O código a ser executado por uma JVM consiste de um código pré-compilado
denominado byte code. A maior parte da checagem de tipos em Java ocorre em tempo de
compilação, isto é, no momento em que o byte code é gerado.
Qualquer interpretador Java compatível deve ser capaz de executar qualquer programa
com arquivos .class que estejam de acordo com o formato especificado para estes
arquivos na Especificação da Máquina Virtual Java.
Garbage Collection (Coleta de Lixo)
Em linguagens como C / C++ o programador fica responsável por liberar toda a memória
alocada ao longo da execução do programa. Programas que não desalocam memória
podem eventualmente travar quando não há mais memória no sistema para alocar.
Java remove do programador a responsabilidade de liberar a memória alocada ao longo
da execução de um programa. A JVM provê uma thread (executando em background)
que registra toda a alocação de memória e mantém um contador do número de referências
para cada ponteiro de memória. Quando a JVM encontra-se ociosa, a thread de coleta de
lixo checa para ver se existem ponteiros de memória para os quais o número de
referências caiu a zero. Neste caso a memória é liberada pelo coletor de lixo. A coleta de
lixo ocorre automaticamente durante o tempo de vida de um programa Java e elimina a
necessidade de desalocação de memória por parte do programador.
Code Security (Segurança de Código)
Arquivos Java são “compilados” no sentido de que são convertidos de um formato texto
(como são escritos) em um conjunto de códigos (byte code) independente de máquina.
Em tempo de execução, os byte codes que formam um programa Java são carregados,
verificados, e então executados pelo interpretador. O interpretador possui duas tarefas:
executar o byte code Java e efetuar as chamadas apropriadas ao sistema para executar o
byte code no hardware disponível.
Em alguns ambientes Java, uma porção do código java verificado é compilado para
código de máquina nativo e executado diretamente na plataforma do hardware. (Nestes
ambientes, costumam ser compiladas as porções de código executadas com maior
freqüência.)
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 7
O Interpretador Java
O interpretador Java possui três tarefas principais:
• Carregar o código – Esta tarefa é executada pelo Class Loader.
• Verificar o código – Tarefa executada pelo Byte Code Verifier.
• Executar o código – Tarefa executada pelo Runtime Interpreter.
Class Loader
O Class Loader carrega todas as classes necessárias para executar o programa. O Class
Loader adiciona segurança separando os name spaces das classes do sistema de arquivos
local e daquelas importadas através da rede. Isto limita qualquer aplicação “cavalo de
tróia”.
Byte Code Verifier
Um código Java passa por vários testes antes de ser efetivamente executado. O Byte Code
Verifier testa o formato de fragmentos de código procurando por código ilegal.
O Byte Code Verifier verifica se o código (byte code) respeita a especificação da JVM e
não viola a integridade do sistema. Se o verificador completa a verificação sem retornar
uma mensagem de erro, então você pode estar certo de que:
• As classes seguem fielmente as especificações da JVM.
• Não há violações de restrições de acesso.
• Os tipos dos parâmetros estão corretos.
• Nenhuma conversão ilegal será feita, como converter inteiros em ponteiros.
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 8
Como Compilar e Executar um Programa Java
Applications
1. No Ambiente DOS
Javac Bemvindo.java
Java Bemvindo
O programa javac compila o arquivo Bemvindo.java na classe Bemvindo.class e o
programa java interpreta o bytecode que o compilador gerou, isto é, o interpretador
executa o arquivo Bemvindo.class.
Exemplo 1
Este programa simplesmente exibe uma mensagem na console (janela DOS).
public class Bemvindo
{ public static void main(String[] args)
{ String[] vetor = new String[3];
vetor[0] = "Meu Primeiro Programa";
vetor[1] = "Que Emoção!!!";
vetor[2] = "Carlos";
int i;
for (i = 0; i < vetor.length; i++)
System.out.println(vetor[i]);
}
}
2. Utilizando o Kawa
Iniciar o Kawa
Abrir o arquivo Bemvindo.java
Clicar no botão Compile para executar o Compilador Java
Clicar no botão Run Java para executar o programa
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 9
Applets
O programa apresentado na página anterior é uma “Java Application”, isto é, um
programa stand alone. No entanto, é possível executar Applets dentro de um Web
browser. Veremos, a seguir como executar uma applet a partir da linha de comando e
através do applet viewer que vem com o JDK.
1. No Ambiente DOS
Javac BemvindoApplet.java
appletviewer BemvindoApplet.html
O primeiro comando chama o compilador java que gera o arquivo
BemvindoApplet.class.
Já o segundo comando executa uma ferramenta que vem com o JDK que nos permite
testar a Applet sem a necessidade de um browser (Web).
O conteúdo do arquivo .html vem a seguir:
(Este código necessita ser convertido, para chama-lo através do browser. Para ver a
applet com o appletviewer utiliza-se o arquivo html sem conversão.)
<HTML>
<TITLE>Bemvindo</TITLE>
<BODY>
Applet Bemvindo.
<APPLET CODE="BemvindoApplet.class" WIDTH=180 HEIGHT=180>
</APPLET>
</BODY>
</HTML>
Observe que o tag APPLET indica que o applet viewer deverá carregar a applet cujo
código se encontra no arquivo Bemvindo.class. O applet viewer ignora todos os
demais tags existentes neste arquivo. Há uma série de tags que permitem que o
browser carregue o java plug-in e exiba o texto HTML juntamente com a applet,
como um objeto embutido. Caso você possua o java plug-in instalado, você poderá
ver esta página com o Netscape ou com o Internet Explorer. O java plug-in localiza o
ambiente de run-time Java corrente e o utiliza para carregar e exibir a applet.
Infelizmente, objetos embutidos são tratados diferentemente pelo Netscape e pelo
Internet Explorer. Para exibir a applet em ambos os browsers, a página acima
necessita ser convertida. Após o processo de conversão um código javascript será
acrescentado com o objetivo de detectar qual browser está sendo utilizado para que
sejam executados os tags apropriados para exibir a applet.
Note que os botões não funcionam no applet viewer. O applet viewer serve apenas
para executar um teste rápido da applet.
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 10
2. Utilizando o Kawa
Também é possível compilar applets com o Kawa e compilar e executar applets com
o TextPad. Para executar uma applet com o TextPad selecione “Compile” e após
“Run”.
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 11
Documentação da API Java
Até aqui vimos todos os termos básicos que java utiliza para descrever seus métodos,
classes e interfaces. Uma vez sentindo-se confortável com estas informações, com
freqüência, você consultará a documentação das APIs que vem com o JDK.
Com o seu web browser aponte para c:\jdk1.2\docs\api\index.html
A figura abaixo apresenta 3 janelas: Uma pequena janela no topo a esquerda mostra todos
os packages disponíveis. Clique em um package e todas as classes existentes neste
package aparecerão na janela inferior à esquerda. Clique no nome de uma classe e a
documentação referente a esta API será exibida na janela maior à direita.
No topo da documentação de cada classe aparece o nome da classe e a cadeia de herança
desta classe, seguido de uma discussão (mais ou menos útil) a respeito da classe. A seguir
vem um resumo de todos os campos públicos e protected, e de todos os construtores e
métodos.
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 12
No topo de cada página há um conjunto de links interessantes: clique em “Tree” para
obter uma visão de todas as classes neste package. Clique em “all packages” para obter
uma lista de todos os packages.
E para obter uma descrição detalhada a respeito de campos e métodos clique em seus
nomes.
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 13
IDENTIFICADORES, PALAVRAS-CHAVE, TIPOS E OPERADORES
Infelizmente, em Java, não é simples escrever um programa que utiliza uma interface
gráfica. Como nosso objetivo, no momento, é escrever programas simples, em vez de
utilizar uma interface gráfica (que seria complicado) vamos escrever programas que
simplesmente lêem dados do teclado e enviam dados para a console (uma janela DOS, no
windows).
Como efetuar a leitura de dados diretamente do teclado não é muito fácil, vamos utilizar
uma classe para este fim sem nos preocuparmos como ela funciona.
Um Simples Programa Java
public class PrimeiroExemplo
{ public static void main (String[] args)
{ System.out.println ("Alô Mundo!");
}
}
ou
public class SegundoExemplo
{ public static void main(String[] args)
{ int i;
for (i = 0; i < args.length; i++)
System.out.println(args[i]);
}
}
Observações sobre o programa PrimeiroExemplo:
• Java é “case sensitive”. Se você digitar Main em vez de main o programa não
compilará.
• A palavra-chave public é denominada um “access modifier”. Um “modifier” controla
que partes de um programa podem utilizar este código.
• A palavra-chave class é para nos lembrar que tudo em java ocorre dentro de uma
classe.
• Após a palavra-chave class vem o nome da classe. O nome de uma classe deve
começar com uma letra. Os demais caracteres devem ser letras ou dígitos. O tamanho
é ilimitado. Palavras reservadas não podem ser utilizadas como nome de classe.
• É preciso criar um arquivo com o mesmo nome da classe pública com a extensão
.java. Logo, o código acima deve ser armazenado em um arquivo denominado
PrimeiroExemplo.java.
• Para compilar este programa digite: javac PrimeiroExemplo.java
• E para executá-lo digite: java PrimeiroExemplo (não digite a extensão .class)
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 14
• O interpretador java sempre começa a execução de um programa pelo método
denominado main. Naturalmente você pode adicionar seus próprios métodos a uma
classe e chamá-los a partir do método main.
• Chaves {um bloco} são utilizadas para delimitar as partes (blocos) do seu programa.
• A palavra-chave static indica que o método não opera sobre objetos. O método main
em Java é sempre static. E a palavra-chave void indica que o método não retorna um
valor.
• Com relação ao fragmento:
{ System.out.println ("Alô Mundo!");
}
• As chaves indicam o início e o fim do corpo de um método.
• Em Java todo comando deve terminar com um ponto e vírgula. O ponto e vírgula
não é um separador de comandos como em pascal, mas um terminador de
comandos.
• No comando acima estamos utilizando o objeto System.out e pedindo para ele
utilizar o método println. Java sempre utiliza a sintaxe:
objeto.método(parâmetros).
• O método println trabalha com um String e o exibe na console.
• Observe que Java utiliza aspas duplas para strings.
• Métodos em Java, assim como funções em qualquer linguagem de programação,
podem utilizar zero, um ou mais argumentos. Até mesmo o método main aceita
que o usuário digite na linha de comando seus argumentos.
• Mesmo que um método possua zero argumentos, deverão ser utilizados parênteses
vazios.
• Existe um método denominado print em System.out que não adiciona um caracter
de nova linha ao string.
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 15
Comentários
Há três maneiras de se colocar um comentário em um programa java:
// Primeira forma. Utilizada quando o comentário não excede uma
linha.
/* Segunda forma: deve ser utilizada quando o comentário é longo e
ocupa várias linhas */
/** Terceira forma: Deve ser utilizada quando se deseja gerar
documentação automaticamente. Há um utilitário denominado javadoc
para este fim. */
Este último tipo de comentário, quando colocado imediatamente antes de uma
declaração (de uma variável ou método), indica que o comentário deve ser incluído em
qualquer documentação gerada automaticamente. (Arquivos HTML gerados pelo
comando JAVADOC). Estes comentários aparecem como uma descrição do item
declarado.
Identificadores
Na linguagem Java um identificador começa com uma letra, um underscore (_), ou com o
símbolo $. Caracteres subsequentes também podem conter dígitos. Identificadores são
case sensitive e não possuem tamanho máximo.
Palavras-Chave
abstract do implements private throw
boolean double import protected throws
break else instanceof public transient
byte extends int return true
case false interface short try
catch final long static void
char finally native super volatile
class float new switch while
continue for null synchronized
default if package this
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 16
Observações:
1. Em Java os literais true, false e null são em letras minúsculas e não em letras
maiúsculas como em C++. Estritamente falando eles não são palavras-chave e sim
literais.
2. goto e const são palavras chave mas não são utilizadas em java.
3. Não existe o operador sizeof. O tamanho e a representação de todos os tipos são fixos
e não são dependentes de implementação.
Tipos de Dados
Java possui oito tipos de dados primitivos e um tipo especial. Estes tipos podem ser
utilizados como valores de literais ou em variáveis. Estes tipos podem ser considerados
em quatro categorias: lógico, texto, inteiro e ponto flutuante.
Lógico – boolean
Booleanos em java possuem dois valores literais: true e false. Não há como
converter booleanos em inteiros e vice-versa. Em C / C++ valores numéricos podem ser
interpretados como valores lógicos (0 – false e não 0 – true). Isto não é permitido em
Java.
Texto – char e String
Char
Caracteres isolados são representados utilizando-se o tipo char. Um char representa um
caracter Unicode utilizando um número sem sinal de 16 bits na faixa de 0 a 2
16
-1. Um
literal do tipo char deve aparecer entre plics.
‘a’
‘\t’ um tab
‘\n’ linefeed
‘\u????’ um caracter Unicode específico. ???? deve ser substituído por
exatamente 4 dígitos hexadecimais. Ex. ‘\u0041’ é o caracter A.
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 17
Strings
O tipo String, que não é primitivo, encontra-se definido na biblioteca standard da
linguagem. É utilizado para representar seqüências de caracteres. Os caracteres em si são
Unicode. Um String literal deve aparecer entre aspas e são seqüências de caracteres. Cada
conjunto de caracteres entre aspas é uma instância da classe String.
Exemplos:
String e = “”; // Observe que como String é uma classe,
// começa com letra maiúscula.
String valor = “abcd”;
Concatenação
Utiliza-se o operador + para se concatenar strings.
Exemplo:
String nome = “Silvio”;
String sobrenome = “ Cesar”;
String nomeCompleto = nome + sobrenome;
Quando se concatena um string com um valor que não é um string o valor é convertido
para string.
String valor = “PG” + 13; // valor = “PG13”
Este tipo de concatenação é muito utilizada em comandos de saída de dados. Por
exemplo:
int resposta = 10;
System.out.println (“A resposta é “ + resposta);
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 18
Substrings
Para extrair um substring de um string maior deve-se utilizar o método substring da
classe String. Por exemplo:
String nome = “Luciana”;
String outroNome = nome.substring (0, 5); // Extrai “Lucia”
O substring s.substring (a, b) possui sempre b–a caracteres.
Editando Strings
Para descobrir o tamanho de um String:
String nome = “Juliana da Rocha”;
int n = nome.length(); // retorna 16
Assim como char representa caracteres Unicode, String representa seqüências de
caracteres Unicode. É possível recuperar um caracter específico de um string.
Exemplo:
nome.charAt (n); // retorna o caracter na n-ésima posição.
// A primeira posição é a zero.
A classe String não fornece nenhum método que nos permita alterar um caracter em um
string. Logo, para modificar um string é preciso mudar o valor da variável que o contém.
Exemplo:
String nome = “Juliana da Rocha”;
nome = nome.substring (0, 6) + “o” + nome.substring (7, 9);
// Resulta “Juliano da Rocha”
Se você pretende escrever uma aplicação que, por exemplo, lê do teclado caracteres a
medida que eles são digitados e os concatena a um string, será mais eficiente utilizar a
classe StringBuffer, que será examinada mais adiante.
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 19
Comparando Strings
Para verificar se dois strings são iguais:
s.equals (t); // Retorna true se os strings s e t são iguais.
“Juliana”.equals (nome) // Note que constantes tb
// podem ser utilizadas.
Não utilize == para verificar se dois strings são iguais. Isto apenas vai determinar se eles
se encontram armazenados no mesmo local de memória. Se strings estão armazenados no
mesmo local, são iguais, mas é perfeitamente possível armazenar strings iguais em locais
diferentes.
Se o compilador sempre armazenasse strings iguais no mesmo local seria possível utilizar
o operador == para testar igualdade. No entanto, apenas strings constantes são
compartilhados. Strings resultantes de operações como + ou substring não são
compartilhados, logo, nunca utilize == para comparar strings ou você terá o pior tipo de
bug para resolver: o intermitente.
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 20
Inteiros – byte, short, int e long
Existem 4 tipos inteiros na linguagem Java. Cada tipo é declarado utilizando-se uma das
seguintes palavras-chave: byte, short, int e long. Literais do tipo inteiro podem
ser representados utilizando-se uma das seguintes formas: decimal, octal ou hexadecimal.
2 O valor decimal 2.
077 O zero a esquerda indica que se trata de um valor octal.
0xBASE Os caracteres 0x à esquerda indicam que se trata de um número
hexadecimal.
Observação: Todos os tipos inteiros em Java são números com sinais.
Literais do tipo inteiro possuem o tipo int a menos que explicitamente seguido da letra
“L”. O “L” indica que se trata de um valor do tipo long. Note que em Java é válido
utilizar a letra L em maiúscula ou minúscula, no entanto, não é uma boa opção utilizar a
letra L minúscula pois pode ser confundida com o dígito 1 (um).
Versões long dos literais exibidos acima:
2L O valor decimal 2, como um long.
077L O zero a esquerda indica que se trata de um valor octal.
0xBASEL Os caracteres 0x à esquerda indicam que se trata de um número
hexadecimal.
A seguir vem o tamanho e a faixa dos quatro tipos inteiros. (Independente de plataforma)
Comprimento Tipo Faixa
8 bits (1 byte) byte -2
7
. . . 2
7
–1 -128 a 127
16 bits (2 bytes) short -2
15
. . . 2
15
–1 - 32.768 a 32.767
32 bits (4 bytes) int -2
31
. . . 2
31
–1 - 2.147.483.648 a 2.147.483.648
64 bits (8 bytes) long -2
63
. . . 2
63
–1
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 21
Pontos Flutuantes – float e double
Uma variável ponto flutuante pode ser declarada utilizando-se as palavras-chave float
ou double.
Float
A razão para se utilizar float em vez de double não é uma questão de desempenho,
embora operações com variáveis do tipo double sejam ligeiramente mais lentas do que
com variáveis do tipo float, mas uma questão de consumo de memória, especialmente
quando se está trabalhando com uma grande quantidade de dados deste tipo e para manter
a compatibilidade com dados armazenados em arquivos externos.
Faixa de valores: -3.4E38 a 3.4E38
Veja os exemplos abaixo para compreender como representar literais válidos do tipo
float:
1e1f 2.f .3f 3.14f 6.02e+23f
O sufixo F ou f é sempre requerido em literais do tipo float. Um erro comum é deixar de
fora o sufixo de um literal do tipo float, conforme vem abaixo:
float variavel = 6.5;
Error: cast explícito necessário para converter um double em
float.
O correto seria:
float variavel = 6.5f;
Um literal double não pode ser designado a uma variável float sem um cast mesmo que o
valor esteja na faixa dos valores permitidos para um float. A razão disto é que alguma
precisão nos dígitos decimais pode ser perdida.
Literais ponto flutuante são do tipo double a menos que explicitamente declarados
como float.
Comprimento Tipo
32 bits float
64 bits double
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 22
Double
Variáveis do tipo double referenciam números ponto flutuante armazenados em 64 bits.
Faixa de valores: -1.7E308 a 1.7E308
Exemplos de literais válidos do tipo double:
1e1 2. .3 3.14 6.02e+23d
Neste formato basta fornecer ao compilador um ponto decimal ou um expoente. O sufixo
“D” ou “d” ou sem sufixo, indica que se trata de um double. Na prática se costuma omitir
o “D”.
O maior número do tipo double é um pouco maior do que um 17 seguido de 307 zeros.
Observações:
• Em java a faixa dos números inteiros não depende da máquina onde você vai executar
o programa, isto é, ao mudar um programa de uma plataforma para outra não é
preciso mexer no programa. Naturalmente isto causa uma certa perda de desempenho,
mas este não é o maior gargalo.
• No caso de um overflow ou no caso de uma divisão por zero, ocorrerão erros.
• Aspas simples são utilizadas para representar constantes do tipo char. Por exemplo,
‘H’ é um caracter. Já “H” representa um string contendo um único caracter.
Variáveis
Exemplos de declaração de variáveis:
byte b;
int umaVariavelInteira, outraVariavelInteira; // Ambas inteiras
long umaVariavelLong;
char ch;
O nome de uma variável deve começar com uma letra e pode possuir os seguintes
caracteres: ‘A’-‘Z’, ‘a’-‘z’, ‘0’-‘9’, ’_’ ou qualquer caracter unicode que represente uma
letra. Todo nome de variável é “case sensitive”.
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 23
Designações e Inicializações
int valor; // Isto é uma declaração
valor = 37; // Isto é uma designação
char yesChar;
yesChar = ‘Y’;
int i = 10; // E isto é uma declaração seguida de uma designação
Observação: Em java é possível declarar uma variável em qualquer lugar do código, mas
esta declaração só pode aparecer uma vez em qualquer bloco de um método.
Conversões entre Tipos Numéricos
• Se um dos operandos é do tipo double, o outro será convertido para double.
• Senão, se um dos operandos é do tipo float, o outro será convertido para float.
• Senão, se um dos operandos é do tipo long, o outro será convertido para long.
O mesmo ocorre com as variáveis dos tipos inteiros: int, short e byte (nesta ordem).
Como converter um double em int:
double x = 9.997;
int nx = (int) x; // A variável nx irá receber o valor 9
Como arredondar um número ponto flutuante para o valor inteiro mais próximo:
double x = 9.997
int nx = (int) Math.round(x); // nx receberá 10
Observações:
• É preciso utilizar (int) porque o resultado retornado pelo método round é do tipo
long e uma variável do tipo long só pode ser atribuída a outra do tipo int
efetuando-se a conversão explicitamente uma vez que existe a possibilidade de se
perder informação.
• É possível efetuar conversão através de uma simples designação se estas designações
ocorrerem na ordem indicada a seguir: byte short int long float
double.
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 24
Constantes
Em java utiliza-se a palavra-chave final para definir constantes. Por exemplo:
public class Constantes1
{ public static void main (String[] args)
{ final double CM_POR_POLEGADA = 2.54;
double larguraPapel = 8.5;
double alturaPapel = 11;
System.out.println ("Tamanho do papel em centímetros: "
+ larguraPapel * CM_POR_POLEGADA + " por "
+ alturaPapel * CM_POR_POLEGADA);
}
}
A palavra-chave final indica que é possível atribuir um valor a CM_POR_POLEGADA uma
única vez. Qualquer tentativa de modificá-la causa um erro de compilação.
Como definir uma constante de forma que ela possa ser utilizada por todos os métodos de
uma classe:
public class Constantes2
{ private static final double G = 9.81; // força da gravidade
public static void main (String[] args)
{ System.out.println (G + " metros por segundo ao quadrado");
}
}
Observações:
• Para se obter o efeito desejado a definição da constante aparece fora do método main.
• Se em vez de “private” tivéssemos utilizado o access modifier “public”, outros
métodos Java fora desta classe também poderiam acessar livremente esta constante.
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 25
Variáveis de Classe
Uma variável “static” é similar em alguns aspectos a uma variável global em outras
linguagens. Java não possui variáveis globais como outras linguagens, mas uma variável
estática é uma variável que pode ser acessada por qualquer instância da classe.
Este tipo de variável costuma ser chamada de variável de classe (Class Variable) para
distingui-la de uma variável de instância (instance variable ou member variable).
Exemplo:
public class Contar
{ private int numeroDeSerie;
private static int contador = 0;
public Contar()
{ contador++;
numeroDeSerie = contador;
}
}
No exemplo acima, todo objeto da classe Contar é criado com um numeroDeSerie
diferente. A variável contador é compartilhada por todas as instâncias, logo quando o
construtor de um objeto a incrementa, está incrementando uma variável global a todas as
instâncias desta classe.
Se a variável estática for marcada como pública, ela poderá ser acessada por outras
classes.
public class StaticVar
{ public static int numero;
}
public class OtherClass
{ public void metodo()
{ int x = StaticVar.numero; // Não é necessário
// instanciar a classe
}
}
Observações:
Variáveis globais a uma classe são inicializadas automaticamente, isto é, possuem valores
default caso não sejam explicitamente inicializadas. Objetos são inicializados como null,
variáveis boleanas recebem false, e variáveis numéricas recebem zero.
Cuidado! Variáveis globais a uma classe podem ser sobrepostas por variáveis com o
mesmo nome definidas dentro de um método da classe. Isto é, dentro do método fica
valendo o valor da variável local, e fora dele, o valor da variável global.
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 26
E finalmente, embora vá contra tudo que a orientação a objetos prega, trocando-se a
palavra-chave private por public é possível definir uma verdadeira variável global que
poderá ser acessada por todos os métodos de uma aplicação, isto é, por métodos de outras
classes. Logo, toda variável global deve ser private.
Operadores
• Operadores aritméticos: +, -, *, /
• O operador / efetua divisão inteira se ambos os argumentos são inteiros.
• Para se obter o resto da divisão de um número por outro utiliza-se o %. Por
exemplo: 15 % 2 é 1.
• É possível efetuar operações aritméticas na declaração e inicialização de uma
variável. Por exemplo:
int n = 5;
int a= 2 * n; // a vale 10
• Assim como em C e em C++:
x += 4;
é equivalente a
x = x + 4;
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 27
Exponenciação
Java não possui um operador para exponenciação. Utilize o método pow que faz parte da
classe Math de java.lang. Exemplo:
double y = Math.pow(x, a); // y = x
a
Ambos os argumentos deste método são do tipo double, assim como o resultado
retornado.
Operadores de Incremento e Decremento
int n = 12;
n++; // equivale a: n = n + 1
int m = 7;
int n = 7;
int a = 2 * ++m; // agora a vale 16 e m vale 8
int b = 2 * n++; // agora b vale 14 e n vale 8
Como estes operadores (++ e --) modificam o valor de uma variável, não podem ser
aplicados a números. Por exemplo, 4++ não é um comando válido. Não é recomendável
utilizar o operador ++ dentro de outras expressões. Costumam causar bugs difíceis de
resolver.
Operadores Relacionais e Boleanos
• Para testar se 3 é igual a 7:
(3 == 7)
• Para testar se 3 é diferente de 7:
(3 != 7)
• Existem ainda: <, <=, > e >=
• Assim como em C++, java utiliza && para “and” e || para “or”.
No exemplo abaixo, se o valor da expressão A é falso, a expressão B não é calculada:
(A && B)
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 28
Parênteses e a Hierarquia dos Operadores
Se parênteses não são utilizados, as operações são realizadas na ordem indicada abaixo:
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 29
ENTRADA DE DADOS E FORMATAÇÃO DA SAÍDA
Lendo Dados
Ler dados do teclado é bastante difícil em java. Naturalmente isto não é um problema se
você pretende escrever programas gráficos que coletam dados dos usuários a partir de
caixas de texto. Mas é um problema para quem pretende escrever programas simples com
o objetivo de aprender a linguagem.
Para facilitar a entrada de dados vamos utilizar uma classe denominada Console que
possui métodos para este fim.
Exemplo de leitura de um double:
import corejava.*; // importa o corejava package
public class ConsoleDouble
{ public static void main (String[] args)
{ double x = Console.readDouble
("Digite um numero. Vou somar 2 a ele.");
System.out.println (x + 2);
}
}
Exemplo de leitura de um String:
import corejava.*; // importa o corejava package
public class ConsoleString
{ public static void main (String[] args)
{ String nome;
nome = Console.readLine ("Digite seu nome: ");
System.out.println ("Oi " + nome);
}
}
O método readLine exibe uma mensagem e captura o string digitado pelo usuário após
ser apertada a tecla <Enter>. Há ainda um terceiro método: readInt que pode ser utilizado
da mesma forma como o readDouble. Caso o usuário tecle <Enter> sem digitar um
número, a mensagem que solicita a digitação de um número será novamente exibida.
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 30
Formatando a Saída
Saída sem formatação:
x = 10000.0 / 3.0
System.out.print (x);
Imprime
3333.3333333333335
Para formatar a saída utilize a classe NumberFormat definida no pacote java.text. Esta
classe possui três métodos para a formatação de:
• Números
• Valores monetários
• Percentuais
Suponha que o seu default locale – que depende da configuração da máquina – seja
Brasil. (Um locale é um conjunto de especificações, de um determinado país, aplicadas a
propriedades de strings e números, tais como ordem dos caracteres, símbolo monetário,
separador decimal, etc.)
Para obter um formatador para o default locale, utilize os três métodos:
NumberFormat.getNumberInstance()
NumberFormat.getCurrencyInstance()
NumberFormat.getPercentInstance()
Cada um destes métodos retorna um objeto do tipo NumberFormat. Você pode utilizar
este objeto para formatar um ou mais números.
Exemplo:
import java.text.*;
public class FormataNumero1
{ public static void main (String[] args)
{ double x = 10000.0 / 3.0;
NumberFormat nf = NumberFormat.getNumberInstance();
String fx = nf.format (x);
System.out.println (fx);
}
}
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 31
Também é possível determinar o número mínimo e máximo de dígitos inteiros e
fracionários que devem ser exibidos. Para fazer isto devemos utilizar os métodos da
classe NumberFormat, listados abaixo:
• setMinimumIntegerDigits
• setMinimumFractionDigits
• setMaximumIntegerDigits
• setMaximumFractionDigits
Exemplo:
import java.text.*;
public class FormataNumero2
{ public static void main (String[] args)
{ double x = 10000.0 / 3.0;
NumberFormat nf = NumberFormat.getNumberInstance();
nf.setMaximumFractionDigits (2);
nf.setMinimumFractionDigits (2);
// nf.setMaximumIntegerDigits (5);
// Cuidado, isto pode truncar o resultado.
nf.setMinimumIntegerDigits (1);
String fx = nf.format (x);
System.out.println (fx);
}
}
É possível formatar números de forma apropriada para diversos países. No caso da
Alemanha, por exemplo, deveríamos utilizar um objeto predefinido – do tipo Locale -
denominado Locale.GERMAN que conhece as regras Alemãs de formatação de números.
Exemplo:
double x = 10000.0 / 3.0;
NumberFormat cf =
NumberFormat.getCurrencyInstance (Locale.GERMAN);
System.out.println (cf.format (x));
Este código deveria imprimir:
3.333,333 DM
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 32
É possível ainda criar seu próprio formato. Por exemplo, você pode querer exibir um
número com seis dígitos após o ponto decimal, mas sem separador de milhar.
Exemplo:
DecimalFormat df = new DecimalFormat (“0.######”);
System.out.println (df.format(x));
Este código deveria imprimir:
3333,333333
Observação: Quando utilizamos métodos para a formatação de números o default Locale
é utilizado.
Você pode utilizar os caracteres da tabela abaixo para formatar Strings:
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 33
PRINCIPAIS ESTRUTURAS DE PROGRAMAÇÃO EM JAVA
Controle de Fluxo
Antes de começarmos com comandos de controle de fluxo é preciso conhecer mais sobre
blocos. Um bloco é formado por um conjunto de comandos Java entre chaves. Blocos
definem o escopo de suas variáveis e podem ser aninhados.
Exemplo:
public static void main (String[] args)
{ int n;
...
{ int k; // Variável local a este bloco.
int n: // Erro! Não é permitida a redefinição de n em
// um bloco mais interno.
...
}
}
Comandos Condicionais
Exemplo 1:
if (nome.equals("Silvio"))
bonus = 100;
Exemplo 2:
if (nome.equals("Silvio"))
{ desempenho = "satisfatório";
bonus = 100;
}
Exemplo 3:
if (numero == 10)
{ valor = valor + 100;
bonus = 500;
}
else
{ valor = valor + 200;
bonus = 1200;
}
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 34
Exemplo 4:
if (numero == 10)
{ valor = valor + 100;
bonus = 500;
}
else if (numero == 20)
{ valor = valor + 300;
bonus = 1200;
}
else if (numero == 30)
{ valor = valor + 700;
bonus = 1900;
}
else
{ valor = valor + 1100;
bonus = 3000;
}
Exemplo 5:
if (x != 0 && 1 / x > 0) // Não divide por zero.
x = x + 1;
Exemplo 6:
public class ManipulaStrings
{ public static void main (String[] args)
{ String r = "bcd";
String s = "abcd".substring(1,3) + "abcd".substring(3,4);
System.out.println("r = " + r);
System.out.println("s = " + s);
if (r == s)
System.out.println
("'r' e 's' ocupam a mesma posição de memória");
else
System.out.println
("'r' e 's' ocupam posições de memória diferentes");
}
}
Working Directory - C:\Java\Packages\Introd\Exemplo11b\
r = bcd
s = bcd
'r' e 's' ocupam posições de memória diferentes
Process Exit...
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 35
Loops Indeterminados
Utilizados quando não se sabe quantas vezes o loop deve ser executado.
Exemplo 1: Calcula a soma dos números de 1 a limite.
import corejava.*;
public class SomaNumeros
{ public static void main (String[] args)
{ int soma = 0;
int i = 1;
int limite = Console.readInt ("Digite um numero maior " +
"do que zero: ");
while (i <= limite)
{ soma = soma + i;
i++;
}
System.out.println ("A soma dos numeros de 1 a " + limite +
" e' " + soma);
}
}
Exemplo 2: Calcula o fatorial de um número.
import corejava.*;
public class Fatorial
{ public static void main (String[] args)
{ int numero = Console.readInt ("Digite um numero maior " +
"do que zero: ");
if (numero == 0)
System.out.println ("O fatorial de 0 é 1");
else
{ int num = numero;
int fat = 1;
do
{ fat = fat * numero;
numero--;
}
while (numero != 1);
System.out.println ("O fatorial de " + num + " e' " +
fat);
}
}
}
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 36
Loops Determinados
Exemplo 1: Calcula o fatorial de um número.
import corejava.*;
public class Fatorial2
{ public static void main (String[] args)
{ int numero = Console.readInt ("Digite um numero maior " +
"do que zero: ");
if (numero == 0)
System.out.println ("O fatorial de 0 é 1");
else
{ int fat = 1;
for (int i = 1; i <= numero; i++)
fat = fat * i;
System.out.println ("O fatorial de " + numero +
" e' " + fat);
}
}
}
Observação: No comando for (valor inicial; valor limite; incremento) é permitido mudar
os valores de limite e incremento a cada novo iteração. Para o cálculo do valor inicial, do
limite e do incremento é permitida a utilização de expressões aritméticas.
Exemplo 2: Um loop sem fim devido a erros de arredondamento. Cuidado ao testar
igualdade com números ponto flutuante.
import corejava.*;
public class ForClass
{ public static void main (String[] args)
{ for (double i = 0; i != 10.0; i += 0.01)
System.out.println (i);
System.out.println ("Fora do for");
}
}
Observação: for (double i=0; i=10.0; i+=0.01) provoca erro uma vez que a
segunda condição significa: enquanto i igual a 10 faça.
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 37
O Comando Switch
Exemplo:
import corejava.*;
public class SwitchCmd
{ public static void main (String[] args)
{ int opcao = Console.readInt ("Selecione uma opção (de 1 a 4)");
switch (opcao)
{ case 1:
System.out.println ("Você digitou o número um");
// break; Sem este break, o próximo System.out será
// executado.
case 2:
System.out.println ("Você digitou o número dois");
break;
case 3:
System.out.println ("Você digitou o número tres");
break;
case 4:
System.out.println ("Você digitou o número quatro");
break;
default:
System.out.println ("Opção inválida");
break; // Este break é desnecessário.
}
System.out.println ("Fora do Switch");
}
}
A variável utilizada no switch deve ser do tipo int, short ou do tipo char. Não é
possível selecionar uma opção por faixa de valores.
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 38
O Comando Break
Exemplo 1: Sem a utilização de um label.
import corejava.*;
public class Break1
{ public static void main (String[] args)
{ for (double i = 0; i != 10; i += 0.01)
{ if (i > 10) break; // Evita que este loop não tenha fim
System.out.println (i);
}
System.out.println ( "Fora do For");
}
}
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 39
Exemplo 2: Com a utilização de um label.
Em algumas situações pouco comuns é preciso fazer com que o fluxo da execução saia de
um loop aninhado profundo. Nestes casos, em vez de programar condições extras nos
vários loops aninhados, pode-se utilizar o comando break - com um label - para onde o
fluxo da execução deve ser desviado.
import corejava.*;
public class BreakLabel
{ public static void main (String[] args)
{ int n;
read_data:
{ while ( . . . )
{ . . .
for ( . . . )
{ n = Console.readInt ( . . . );
if ( n < 0 ) // Não deveria acontecer. Não pode
// continuar.
Break read_data:
/* O fluxo da execução é desviado para o label e o
próximo comando a ser executado será o primeiro após o bloco read_data,
onde deverá ser testado em que condições a execução chegou lá. */
. . .
}
}
}
// Testando como a execução chegou aqui.
if ( n < 0 )
// Situação que não deveria ter ocorrido
. . .
else
{ // Chegou aqui normalmente
. . .
}
}
}
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 40
Métodos de Classes (Funções Definidas pelo Usuário)
A definição de um método deve ocorrer dentro de uma classe. Ela pode ocorrer em
qualquer lugar dentro da classe, embora seja comum colocar-se os métodos antes do
método main. Existem muitos tipos de métodos em java. No momento, vamos utilizar
apenas métodos public e static, assim como o método main.
Java não possui métodos globais. Todos os métodos devem ser definidas dentro de uma
classe. Os métodos que vamos estudar a seguir ainda não operam sobre objetos e são, por
esta razão, definidos como static.
A seguir vem a descrição de um método que nos indica qual a chance que temos de
acertar numa loteria com n números retirados de um total de k números. Por exemplo,
para acertarmos na sena é preciso acertar 6 números em 50, logo a chance que temos de
ganhar é: (50 * 49 * 48 * 47 * 46 * 45) / (1 * 2 * 3 * 4 * 5 * 6), isto é, 1 chance em
15.890.700.
E qual seria a nossa chance se tivéssemos que acertar 7 números em 38?
public static long calculaChanceLoteria (int maiorNumero,
int numero)
{ long r = 1;
long j = 1;
int i;
for (i = 1; i <= numero; i++)
{ r = r * maiorNumero;
maiorNumero--;
j = j * i;
}
return r/j;
}
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 41
Observações:
• Como um método estático pode ser chamado sem qualquer instância da classe à qual
pertence, não cabe fazer referência a this. A conseqüência disto é que um método
estático não pode acessar variáveis além dos seus argumentos e variáveis estáticas. A
tentativa de acessar variáveis não estáticas provocará um erro de compilação.
Exemplo:
public class Erro
{ int x;
public static void main (String args[])
{ x = 9; // Erro de compilação
}
}
• Variáveis declaradas dentro de um método são locais a ele. Não podem se acessadas
nem modificadas por nenhuma outra variável com mesmo nome na classe. Quando
java chama um método, todas as variáveis locais são inicializadas como indicado no
corpo do método. Dentro de um método o escopo de uma variável local é
determinado pelo bloco no qual ela foi declarada. Note que você deve inicializar
todas as variáveis. Variáveis locais em um método não recebem valores default. Já as
variáveis de instância são criadas quando o objeto é construído e continua a existir
enquanto o objeto é necessário.
• Quando java executa um comando return é retornado para o método chamador o valor
constante da expressão que segue este comando. Caso você não deseje retornar
nenhum valor, indique este fato no header através da palavra-chave void.
• Ao sair de um método a memória alocada a variáveis locais é automaticamente
reclamada. (garbage colection)
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 42
A seguir apresentamos o método main com a chamada ao método calculaChanceLoteria.
import corejava.*;
public class CalculaChanceLoteria
{ public static long calculaChanceLoteria (int maiorNumero,
int numero)
{ long r = 1;
long j = 1;
int i;
for (i = 1; i <= numero; i++)
{ r = r * maiorNumero;
maiorNumero--;
j = j * i;
}
return r/j;
}
public static void main (String[] args)
{ int numero = Console.readInt ("Quantos números você " +
"quer apostar? ");
int maiorNumero = Console.readInt ("Qual o maior número? ");
long suaChance = calculaChanceLoteria (maiorNumero, numero);
System.out.println ("Quantidade de números = " + numero);
System.out.println ("Maior número = " + maiorNumero);
System.out.println ("Sua chance de acertar é de 1 em " +
suaChance);
}
}
Observações:
• Na chamada ao método calculaChanceLoteria não é preciso especificar o nome da
classe uma vez que tanto o método main quanto o método calculaChanceLoteria
pertencem à mesma classe CalculaChanceLoteria.
• Note que nas chamadas ao método getNumberInstance() nos exemplos apresentados
anteriormente, sempre especificamos o nome da classe (NumberFormat) uma vez que
este método pertence a outra classe.
• NumberFormat.getNumberInstance();
• Quando o comando long suaChance = calculaChanceLoteria
(maiorNumero, numero) é executado, os valores correntes das variáveis
maiorNumero e numero são passados para o método
calculaChanceLoteria. É importante saber que em java todos os argumentos
passados para métodos são passados por valor e não por referência. Logo, não é
possível mudar o valor de variáveis através de chamadas a métodos.
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 43
Recursividade
Tipos de recursividade:
• Recursividade direta: quando um sub-programa chama apenas a si próprio.
• Recursividade indireta: quando um método chama outro que, por sua vez, chama o
primeiro.
Ambos os tipos de recursividade são possíveis em java. A seguir será apresentado o
algoritmo recursivo para se calcular a nossa chance de ganhar em uma loteria em que são
sorteados k números de um total m números.
public static long calculaChanceLoteria (int maiorNumero, int numero)
{ if (numero <= 0) return 0; // Não deveria ocorrer.
else if (numero == 1) return maiorNumero;
else return maiorNumero *
calculaChanceLoteria (maiorNumero - 1, numero - 1) / numero;
}
Observação: Deve-se evitar soluções recursivas quando existe uma solução não recursiva
óbvia.
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 44
ARRAYS
Em Java arrays são objetos de primeira classe. Um objeto de primeira classe é um tipo
objeto definido pela linguagem e não pelo usuário. Como são objetos, é possível, por
exemplo, atribuir um array de inteiros a outro, assim como você faz com uma variável
inteira. Se você atribuir um array de inteiros a outro, ambos os arrays irão referenciar o
mesmo conjunto de dados, isto é, a modificação efetuada em um deles afetará o outro.
Uma vez criado um array não se pode facilmente trocar o seu tamanho. Se você
necessitar aumentar o tamanho de um array ao longo da execução de um programa será
necessário utilizar um objeto diferente, denominado vector. Mais adiante estudaremos
os vectors.
Você já viu alguns exemplos de arrays em java: o argumento String[] args do
método main indica que este método recebe um array de strings.
Arrays são o primeiro exemplo de objetos, cuja criação um programa deve manipular
explicitamente. Por exemplo:
int[] arrayDeInteiros;
arrayDeInteiros = new int[100];
O primeiro comando, a declaração, aloca espaço suficiente para armazenar a referência.
O segundo comando aloca o espaço necessário para se armazenar 100 números inteiros.
Esta região de memória alocada através do comando new int[100] é dita um objeto.
A variável arrayDeInteiros recebe uma referência (um ponteiro) para este objeto,
isto é, para a região de memória onde os números inteiros poderão ser armazenados.
O comando abaixo cria um array que poderá conter 100 números inteiros. As entradas
neste array são numeradas de 0 a 99. Uma vez criado o array podemos atribuir valores a
ele, por exemplo, com um loop:
int[] arrayDeInteiros = new int[100];
for (int i = 0; i < 100; i++)
arrayDeInteiros [i] = i; // Preenche o array com os números 0
// a 99.
Em Java há uma forma abreviada de se criar um objeto array e inicializá-lo ao mesmo
tempo. Exemplo:
int[] algunsPrimos = {2, 3, 5, 7, 11, 13);
Note que não foi preciso chamar o new com esta sintaxe.
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 45
Pode-se inclusive inicializar um array anônimo com esta sintaxe:
new int[] {2, 3, 5, 7, 11, 13};
O número de elementos do array é contado e o tamanho do array especificado com este
valor. Esta sintaxe pode ser utilizada quando se deseja passar um array para um método e
não se deseja criar uma variável local para este fim. Exemplo:
printLabels (new String[] {"Compras", "Vendas"};
A forma acima é uma abreviação de:
String[] departamentos = {"Compras", "Vendas"};
printLabels (departamentos);
Para se descobrir o número de elementos de um array utilize o método length,
conforme vem a seguir:
for (int i = 0; i < departamentos.lenght; i++)
System.out.println (departamentos [i]);
Copiando Arrays
É possível copiar um array para outro, mas neste caso, ambos os arrays farão referência
aos mesmos dados.
int[] primeirosPrimos = {2, 3, 5, 7, 11, 13};
int[] maisPrimos = primeirosPrimos;
maisPrimos [5] = 12; // Agora primeirosPrimos [5] também vale 12.
Se você realmente deseja copiar os valores de um array para outro, utilize o método
arraycopy definido na classe System.
System.arraycopy (from, fromIndex, to, toIndex, count);
Exemplo:
public class CopiaArray
{ public static void main (String args[])
{ int[] primeirosPrimos = {2, 3, 5, 7, 11, 13};
int [] outrosNumeros =
{1001, 1002, 1003, 1004, 1005, 1006, 1007};
System.arraycopy (primeirosPrimos, 2, outrosNumeros, 3, 4);
for (int i = 0; i < outrosNumeros.length; i++)
{ System.out.println
("Posição "+ i + " após a cópia = " +
outrosNumeros [i]);
}
}
}
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 46
Saída:
Posição 0 após a cópia = 1001
Posição 1 após a cópia = 1002
Posição 2 após a cópia = 1003
Posição 3 após a cópia = 5
Posição 4 após a cópia = 7
Posição 5 após a cópia = 11
Posição 6 após a cópia = 13
Process Exit...
Arrays como Argumentos
Como arrays em java são, na verdade, objetos, um método pode alterar um elemento em
um array. Lembre-se que a passagem de parâmetros em java é por valor o que faz com
que um método não possa alterar o valor de uma variável comum.
Exemplo:
public class OrdenaArray
{ public static void sort(int[] a)
{ int n = a.length;
boolean troca = true;
while (troca)
{ troca = false;
for (int i = 0; i < n - 1; i++)
{ int temp = a[i];
if (a[i] > a[i+1])
{ a[i] = a[i + 1];
a[i + 1] = temp;
troca = true;
}
}
}
}
public static void print (int[] a)
{ for (int i = 0; i < a.length; i++)
System.out.print (a[i] + " ");
System.out.println();
}
public static void main (String[] args)
{ // Cria um array com 10 números inteiros
int [] a = new int [10];
int i;
// Preenche o array com números gerados randomicamente
for (i = 0; i < a.length; i++)
a [i] = (int) (Math.random() * 100);
print (a);
sort (a);
print (a);
}
}
O Método Lenght
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 47
Observe que o método lenght retorna um número que representa a quantidade de posições
alocadas para o vetor.
public class ImprimeArray
{ public static void print (int[] a)
{ for (int i = 0; i < a.length; i++)
System.out.print (a[i] + " ");
System.out.println();
}
public static void main (String[] args)
{ // Cria um array com 10 números inteiros
int [] a = new int [10];
int i;
// Preenche o array com números gerados randomicamente
a [0] = (int) (Math.random() * 100);
a [1] = (int) (Math.random() * 100);
a [2] = (int) (Math.random() * 100);
a [3] = (int) (Math.random() * 100);
print (a);
}
}
Saída:
93 18 35 73 0 0 0 0 0 0
Process Exit...
Observações:
• Como o algoritmo de ordenação apresentado acima não é muito eficiente (é apenas
fácil de programar), se você desejar ordenar um grande array de números, utilize um
dos métodos de sort da classe Arrays.
int [] a = new int[10000];
. . .
Arrays.sort(a);
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 48
Retornando um Array
O tipo retornado por um método também pode ser um array. Isto é útil quando um
método gera uma seqüência de valores. Por exemplo, vamos escrever um método que
gere uma seqüência de números que possam ser utilizados em uma aposta na sena.
import java.util.*;
import corejava.*;
public class Loteria
{ public static int[] geraNumeros (int maiorNumero, int qtdNumeros)
{ int i;
int numeros[] = new int[maiorNumero];
int resultado[] = new int[qtdNumeros];
// Preenche um array com os números 1, 2, 3, ..., maiorNumero.
for (i = 0; i < maiorNumero; i++) numeros [i] = i + 1;
for (i = 0; i < qtdNumeros; i++)
{ int j = (int) (Math.random() * (maiorNumero - i));
resultado [i] = numeros [j];
numeros [j] = numeros [maiorNumero - 1 - i];
}
return resultado;
}
public static void main (String[] args)
{ int qtdNumeros = Console.readInt ("Quantos numeros voce " +
"deseja apostar?");
int maiorNumero = Console.readInt ("Qual e' o maior numero?");
int [] a = geraNumeros (maiorNumero, qtdNumeros);
Arrays.sort (a);
System.out.println ("Quer ficar rico? Aposte a seguinte " +
"combinacao: ");
for (int i = 0; i < a.length; i++)
System.out.println (a [i]);
}
}
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 49
Arrays Multidimensionais
Como declarar uma matriz:
double [] [] umaMatriz;
Só se pode utilizar um objeto (um array, neste caso) após inicializá-lo com uma chamada
a new.
umaMatriz = new double [5] [6];
Uma vez inicializado, é possível acessar elementos individuais.
umaMatriz [i] [j] = 15;
Na realidade java não possui arrays multidimensionais, mas apenas arrays de arrays. No
exemplo anterior o array umaMatriz é, na realidade, um array que contém cinco
elementos, e cada um deles é um array de 6 números ponto flutuantes, conforme vem na
figura abaixo.
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 50
A expressão umaMatriz [i] referencia o i-ésimo subarray, isto é, a i-ésima linha da tabela.
Já umaMatriz [i] [j] referencia a j-ésima posição daquele array. Como as linhas de um
array são acessadas individualmente é possível trocá-las:
double [] temp = umaMatriz [i];
umaMatriz [i] = umaMatriz [i + 1];
umaMatriz [i + 1] = temp;
Outra conseqüência do fato de que uma matriz é um array de arrays é que você pode
utilizar a notação abreviada para inicializá-la, conforme vem a seguir:
int [] [] vendasDosUltimosDoisAnos = { {1997, 1998},
{100000, 200000} };
Também é possível definir “ragged arrays” (array dentado), isto é, um array no qual
linhas diferentes possuem diferentes comprimentos. Abaixo vem um exemplo padrão.
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
1 5 10 10 5 1
1 6 15 20 15 6 1
Como j nunca pode ser maior do que i, a matriz é triangular. A i-ésima linha possui i + 1
elementos. Para construir este array, primeiramente alocamos o array que armazena as
linhas.
int [] [] matrizDentada = new int [n + 1] [];
A seguir alocamos as linhas:
for (i = 0; i <= n; i++)
matrizDentada [i] = new int [i + 1];
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 51
Agora que o array já se encontra alocado, podemos acessar os elementos normalmente,
tomando o cuidado de não ultrapassar os limites.
import corejava.*;
public class CriaMatrizDentada
{ public static void main (String args[])
{ int i, j;
int n = Console.readInt ("Digite o numero de linhas da " +
"matriz:");
// Aloca matriz dentada
int [][] matrizDentada = new int [n][];
for (i = 0; i < n; i++)
matrizDentada [i] = new int [i + 1];
// Cria matriz dentada
for (i = 0; i < n; i++)
{ matrizDentada [i][0] = 1;
matrizDentada [i][i] = 1;
}
for (i = 2; i < n; i++)
for (j = 1; j < matrizDentada[i].length - 1; j++)
matrizDentada [i][j] = matrizDentada [i-1][j-1] +
matrizDentada [i-1][j];
// Imprime matriz dentada
for (i = 0; i < n; i++)
{ for (j = 0; j < matrizDentada[i].length; j++)
System.out.print (matrizDentada [i] [j] + " ");
System.out.println ("");
}
}
}
Determinando o número de linhas e de colunas
import corejava.*;
public class Principal
{ public static void main(String[] args)
{ int md[][] = new int [10][5];
System.out.println (md.length + " " + md[0].length);
// md.length retorna o número de linhas alocadas 10
// md[0].length retorna o número de colunas alocadas 5
}
}
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 52
OBJETOS E CLASSES
A programação estruturada tradicional consiste em projetar uma série de funções para
resolver um problema. O próximo passo é encontrar formas apropriadas de armazenar os
dados. Esta é a razão pela qual o projetista da linguagem Pascal original, Niklaus Wirth,
chamou seu famoso livro de programação de Algorithms + Data Structures = Programs
(Prentice Hall, 1995). Note que neste título Algorithms vêm em primeiro lugar e
estruturas de dados em segundo. Isto mostra como os programadores pensavam naquela
época. Primeiro você decide como manipular os dados, e depois decide qual estrutura de
dados deve ser utilizada de forma que a manipulação seja mais fácil. OOP inverte esta
ordem: põe as estruturas de dados em primeiro lugar, para só então verificar quais
algoritmos devem operar sobre estas estruturas.
O segredo de como ser produtivo com a OOP é fazer com que cada objeto seja
responsável pelas tarefas a ele relacionadas. Se um objeto precisa executar uma tarefa que
não é da sua responsabilidade, é preciso que ele tenha acesso ao objeto responsável pela
tarefa. O primeiro objeto, então solicita ao segundo para executar a tarefa. No jargão da
OOP objetos clientes enviam mensagens para objetos servidores.
Em particular, um objeto nunca deve manipular diretamente os dados internos de outro
objeto. Todas as comunicações devem ser através de mensagens, isto é, através de
chamadas a métodos. Projetando os seus objetos para manipular seus dados internos,
maximiza-se a reusabilidade, reduz-se a dependência entre os dados e reduz-se o tempo
de debug.
Assim como no desenvolvimento de módulos em uma linguagem procedural, você não
irá querer que um único objeto faça muitas coisas. Tanto o projeto, quanto a programação
(debug) são simplificados quando pequenos objetos são construídos, em vez de objetos
gigantescos com complexas estruturas de dados e com centenas de métodos para
manipular os dados.
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 53
Terminologia
Tipos de Dados Agregados
Representa um modelo de alguma coisa em um programa. O modelo é construído
acrescentando a ele variáveis que descrevem os vários aspectos que especificam o estado
da entidade. Uma vez definido, variáveis podem ser criadas utilizando o nome do tipo.
Algumas linguagens utilizam os termos Record ou tipos estruturados. Java
utiliza o termo Class.
Class
Uma versão de um tipo de dado agregado para uma linguagem orientada a objetos.
Estritamente falando, a idéia de classe é um superconjunto da idéia de tipo agregado de
dado, mas as muitas características que diferenciam uma classe de um tipo agregado de
dados ainda não foram discutidas.
Uma classe é geralmente descrita como um modelo a partir do qual os objetos são
instanciados. Uma classe funciona como uma forma de doces na culinária. Os objetos são
os próprios doces. Assim como para montar um doce você necessita de massa, para
alocar um objeto você necessita de memória. Java esconde muito bem de você a
preparação desta massa (ou a alocação da memória). Você simplesmente utiliza a
palavra-chave new para obter a massa (ou memória), e o coletor de lixo automático
comerá os doces (ou liberará a memória) quando ninguém mais estiver interessado neles.
Object
Uma instância de uma classe. A classe pode ser considerada um modelo. Um objeto é o
que você obtém cada vez que você cria uma instância da classe.
O comportamento de um objeto é definido pelas mensagens que ele aceita. Um objeto
armazena a informação de como ele se parece no momento. Isto é o que geralmente se
chama de estado do objeto. Um objeto pode mudar de estado ao longo do tempo, mas não
espontaneamente. Uma mudança no estado de um objeto deve ser conseqüência de
mensagens enviadas a ele (caso contrário o encapsulamento estará quebrado).
O estado de um objeto, no entanto, não o descreve completamente, uma vez que cada
objeto possui uma identidade distinta. Por exemplo, em um sistema de processamento de
pedidos, dois pedidos são distintos mesmo que requisitem itens idênticos. O estado de um
objeto pode influenciar o seu comportamento. Se um pedido “já foi enviado” ou “já foi
pago” deverá rejeitar uma mensagem que solicite a adição ou a remoção de itens. Por
outro lado, se um pedido encontra-se “vazio”, isto é, nenhum item ainda foi pedido, ele
não deveria permitir que fosse “enviado”.
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 54
Member
Um membro é um dos elementos que formam um objeto. O termo também é utilizado
para os elementos que definem uma classe. Os termos variável membro, variável de
instância ou campo também são utilizados, significando a mesma coisa.
Reference
Uma variável que possui um tipo classe. Através da variável pode-se acessar um objeto
em particular. Este tipo de variável é denominada referência.
Como Definir uma Classe
Para definir uma classe utilizamos objetos. Por exemplo, embora a linguagem Java
possua mais de uma classe para representar datas, suponha que desejamos criar um novo
tipo de dado para este fim. Poderíamos, por exemplo, criar a classe Data, composta de
dia, mês e ano, assim:
public class Data
{ public int dia;
public int mes;
public int ano;
}
A palavra class deve ser escrita com letras minúsculas e o nome da classe “Date”, por
convenção, deve ser escrito com a primeira letra maiúscula.
Agora uma variável pode ser declarada como sendo do tipo Data, e dia, mês e ano
poderão ser acessados assim, caso tenham sido declaradas como public.
Date umaData; // Data do fim do mundo.
umaData.dia = 11;
umaData.mes = 08;
umaData.dia = 99;
Quando declaramos variáveis de qualquer tipo primitivo (boolean, byte, short, char, int,
long, float e double), o espaço de memória para a variável é alocado como parte da
operação. A declaração de uma variável utilizando um tipo classe como String ou Array
ou qualquer tipo definido pelo usuário não aloca espaço de memória.
Na realidade, a variável que é declarada com o tipo classe não contém o dado em si, mas
uma referência para o dado. Você pode pensar nesta referência como um ponteiro para a
região de memória onde o dado se encontra.
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 55
Por exemplo:
No corpo de um método a declaração:
Date hoje; // Declarando uma variável com o tipo Date – uma
// classe da linguagem Java para a criação de objetos
// que possam armazenar datas.
hoje = new Date();
Aloca o espaço de memória necessário para armazenar apenas a referência:
hoje (inicialmente com null)
A construção do objeto aloca e inicializa a área de um objeto do tipo Date:
Date hoje;
hoje = new Date();
hoje (inicialmente com null – indica que a referência
não está apontando para nada)
day
month
year
E finalmente, a operação de atribuição faz com que faz com que a variável hoje recebe
uma referência para um objeto do tipo Date.
Date hoje;
hoje = new Date();
hoje
day
month
year
????
????
0
0
0
0x01abcd
0
0
0
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 56
O fato de Java tratar variáveis que são declaradas como do tipo classe como referências,
trás conseqüências relativas ao significado das designações:
int x = 7;
int y = x;
String s = “Oi”
String t = s;
x
y
s
t
Programadores C / C++ devem tomar cuidado. O identificador null em Java é grafado
com letras minúsculas e não em maiúsculas como nestas linguagens.
Uma classe, além das variáveis de instância, possuem métodos que permitem o acesso a
estas variáveis. Algumas linguagens de programação, incluindo Java, permitem uma forte
associação entre a declaração de um tipo de dado e a declaração do código que se
pretende utilizar para manipular as variáveis deste tipo.
Em Java, você pode criar uma associação entre o tipo Data e a operação amanha,
assim:
public class Data
{ public void amanha()
{ // código para incrementar um dia
}
private int dia, mes, ano;
}
Em Java você pode referenciar a parte do código denominada amanha como um
método, no entanto você também irá escutar os termos função membro e função.
Em linguagens que não suportam esta associação entre dados e código, a declaração do
método amanha especifica uma data em particular que deve ser incrementada em um
dia. Por exemplo:
void amanha(Data d);
0x01234567
0x01234567
“Oi”
7
7
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 57
A combinação de tipo de dado com seus operadores é chamada de tipo abstrato de dados.
Linguagens como Java que suportam tipo abstrato de dados criam uma forte associação
entre dado e código. Requisitamos que determinado método atue sobre um objeto assim:
Data d = new Data(); // É criado um objeto do tipo data
// (denominado d) contendo a data de hoje.
d.amanha(); // Esta notação reflete a idéia de que o
// comportamento é determinado pelo tipo
// do dado.
Em Java métodos são definidos utilizando uma abordagem que é muito similar à
abordagem utilizada em outras linguagens como C e C++. A declaração dos métodos
possui a seguinte forma:
<modificadores> <tipo do dado retornado> <nome do método> (<lista de argumentos>)
<bloco>
Por exemplo:
public void adicionaDias(int dias)
instrui o corpo do método adicionaDias para receber um argumento que indica o número
de dias que deve ser somado a uma data.
Exemplo de chamada a este método:
Data d = new Data(); // O operador new é utilizado para se
// criar uma instância da classe Data
d.adicionaDias(10);
No exemplo acima são somados 10 dias à data de hoje.
Os modificadores são:
public static
private final
protected
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 58
Utilizando a palavra-chave private na declaração dos campos dia, mês e ano da classe
Data possui o efeito de tornar impossível o acesso a estes campos a partir de qualquer
código, exceto através dos métodos da própria classe Data.
public class Data
{ public void amanha()
{ // código para incrementar um dia
}
private int dia, mes, ano;
}
Logo, considerando a definição da classe Data acima, o seguinte código é ilegal:
public class Principal
{ public static void main(String args[])
{ Data umaData = new Data();
umaData.dia = 21; // Código ilegal
}
}
Impedir que uma classe acesse diretamente os dados de outra como em (umaData.dia
= 21;) trás grandes vantagens para a qualidade de um programa. Como itens de dados
individuais de uma classe (campos, variáveis de instância) são inacessíveis, a única forma
de lê-los e modificá-los é através de métodos. Logo, se um programa requer consistência
interna dos membros de uma classe, isto pode ser gerenciado pelos próprios métodos da
classe.
Considere a classe Data que permite livre acesso a seus membros. Seria fácil um
programa cometer os seguintes erros:
Data d = new Data();
d.dia = 32;
d.mes = 2;
d.dia=30;
d.mes = d.mes + 1; // Sem passar de 12 para 1
// e somar 1 ao ano.
Se os dados membros de uma classe não estão expostos o usuário é forçado a modificar
variáveis membro através de métodos. Estes métodos podem efetuar, então, validações e
não permitir os erros acima mencionados.
public void setDia (int umDia)
{ if (umDia > this.diasNoMes())
{ System.err.println (“Dia inválido = “ + umDia);
// Na verdade, deveria gerar uma exceção.
}
else
{ this.dia = umDia;
}
}
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 59
O reuso de uma classe é viabilizado quando o usuário é forçado a acessar os dados da
classe através de seus métodos, uma vez que garante que os efeitos colaterais sejam
propriamente tratados. No caso da classe Data, por exemplo, considere como o método
amanha deveria ser construído.
Se Data fosse inteiramente acessível, cada usuário da classe teria que incrementar dia e
testar todas as situações de erro possíveis. Forçando o usuário da classe a utilizar o
método amanha() fornecido, todos podem estar certos de que os possíveis efeitos
colaterais serão sempre tratados corretamente.
Este aspecto de ocultação de dados é geralmente referenciado como encapsulamento.
A biblioteca standard da linguagem java fornece milhares de classes para diversos
propósitos tais como: interface com o usuário, network programming, etc. No entanto,
você ainda tem que criar as suas próprias classes em java, para descrever os objetos
existentes no domínio do problema tratado pela sua aplicação e para adaptar as classes
fornecidas na biblioteca standard para os seus próprios objetivos.
Uma classe, em java, é sempre construída com base em outra. Java vem com uma classe
básica, denominada object, a partir da qual todas as outras são construídas.
Quando você estende uma classe, a nova classe inicialmente possui todas as propriedades
e métodos da classe pai. Você pode escolher se deseja modificar ou simplesmente manter
os métodos da classe pai. Você pode, ainda, fornecer novos métodos que se aplicam à
classe filho apenas. O conceito geral de expansão de uma classe base se chama herança.
Encapsulamento é outro conceito básico da orientação a objetos. Formalmente,
encapsulamento não é nada mais do que a combinação de dados e comportamento
(métodos) em um pacote, e a ocultação da implementação deste comportamento, de
forma que o usuário do objeto não saiba como acessar estes dados a não ser através de
seus métodos. Os dados em um objeto são geralmente denominados variáveis de instância
ou campos e as funções e procedimentos pertencentes a uma classe java são seus
métodos. Um objeto específico (que é uma instância de uma classe) terá valores
específicos para seus campos, que definem seu estado corrente. Encapsulamento é a
maneira de dar ao objeto o seu comportamento de “caixa preta”. Isto significa que um
objeto pode mudar totalmente a maneira como armazena os seus dados mas, enquanto
continuar a utilizar os mesmos métodos para manipular os dados, nenhum outro objeto
saberá da mudança, nem tampouco se importará com ela.
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 60
Relacionamentos entre Classes
Em um sistema de processamento de pedidos os seguintes substantivos nos levam às
classes:
• Item
• Pedidos
• Pagamento
• Cliente
A seguir procuramos pelos verbos. Itens são adicionados a pedidos. Pedidos são enviados
ou cancelados. Pagamentos são aplicados a pedidos. Para cada verbo, adicionar, enviar,
cancelar, aplicar, você deve identificar o objeto responsável por executar esta tarefa. Por
exemplo, quando um novo item é adicionado a um pedido, o objeto pedido deveria ser o
responsável pela realização desta tarefa uma vez que ele sabe como armazenar e ordenar
itens. Isto é, adicionarItem deveria ser um método da classe Pedido que recebe um objeto
item como parâmetro.
Os mais comuns relacionamentos entre classes são:
• Utiliza
• Contém (“has-a”)
• Herda (“is-a”)
O relacionamento utiliza é o mais óbvio e também o mais geral. Por exemplo, a classe
Pedido utiliza a classe Cliente, uma vez que objetos Pedidos necessitam acessar objetos
Cliente para verificar a situação de crédito (do cliente). A classe Item, por sua vez não
utiliza a classe Cliente, uma vez que objetos item nunca necessitam se preocupar com
contas de clientes. Logo, uma classe utiliza outra se manipula objetos dela.
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 61
Em geral, uma classe A utiliza uma classe B se:
• Um método de A envia uma mensagem para um objeto da classe B, ou
• Um método de A cria, recebe, ou retorna objetos da classe B.
Tente minimizar o número de classes que utilizam outras. Se uma classe A não está
ciente da existência de uma classe B, uma modificação em B não afetará A. E isto
significa que uma modificação em B não vai introduzir bugs em A.
O relacionamento contém é fácil de entender porque é concreto. Por exemplo, um
objeto Pedido contém objetos Itens. Naturalmente, contém é um caso especial de
utilização. Se um objeto A contém um objeto B, então pelo menos um método da classe
A fará uso daquele objeto da classe B.
O relacionamento herda significa especialização. Por exemplo, um “pedido de entrega
rápida” possui métodos especiais para tratar a prioridade na entrega e um método
diferente para calcular o custo do transporte. No entanto seus demais métodos, tais como
adicionar um item ao pedido, remover um item do pedido, etc, são herdados da classe
pedido.
Estes três relacionamentos entre as classes formam os fundamentos do projeto orientado a
objetos. O diagrama de classe mostra as classes e seus relacionamentos. O diagrama da
figura abaixo (que segue a notação da UML) foi gerado através da ferramenta Together/J,
uma aplicação Java que tem por objetivo manter sincronizados o diagrama de classes do
seu sistema e seu respectivo código.
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 62
Diferenças entre a OOP e as Técnicas Tradicionais de Desenvolvimento de Software
Na programação orientada ao procedimento é comum se identificar as tarefas que devem
ser executadas e então:
• Através de refinamentos sucessivos, quebram-se as tarefas a serem executadas em
subtarefas menores, e estas em subtarefas ainda mais simples até que estas subtarefas
estejam simples o suficiente para que possam ser implementadas.
• Após a implementação destas tarefas, elas costumam ser combinadas para formar
procedimentos mais complexos.
A principal característica da OOP é que primeiramente isolamos as classes do projeto
para só então procuramos pelos métodos das classes. Cada método na OOP é associado a
uma classe responsável por executar a operação.
Outro aspecto importante é a questão da herança que não existente na programação
convencional.
Variáveis do Tipo Objeto
Na maioria das classes em Java, criamos objetos, instanciamos seus valores iniciais e,
então, trabalhamos com eles. A classe console é uma exceção uma vez que encapsula
apenas funcionalidade, pois não possui dados próprios.
Para acessar objetos, definimos variáveis do tipo objeto.
Empregado umEmpregado; // umEmpregado ainda não faz
// referência a um objeto.
Por exemplo, o comando acima define uma variável do tipo objeto, que poderá
referenciar objetos do tipo Empregado. É importante entender que a variável
umEmpregado não é um objeto e, de fato, ainda não faz sequer referência a um objeto.
Neste ponto do programa o comando
umEmpregado.aumentaSalario(5);
não vai funcionar, uma vez que é preciso instanciar o objeto apontado por
umEmpregado.
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 63
Para instanciar este objeto utilize o operador new:
umEmpregado = new Empregado();
A partir deste momento será possível aplicar os métodos definidos para Empregado
sobre umEmpregado.
Em muitas situações será preciso criar múltiplas instâncias de uma única classe.
Empregado outroEmpregado = new Empregado();
Agora existem dois objetos do tipo Empregado, um vinculado à variável objeto
umEmpregado e outro vinculado à variável objeto outroEmpregado.
Se você designar uma variável a outra conforme vem abaixo, então ambas as variáveis
farão referência ao mesmo objeto.
Empregado outroEmpregado = umEmpregado;
Isto pode provocar um comportamento estranho do programa, pois se você executar
outroEmpregado.aumentaSalario(5);
o salário de umEmpregado sofrerá o aumento.
Mas e se você quiser que as variáveis umEmpregado e outroEmpregado façam
referência a objetos diferentes de forma que você possa modificar um sem alterar o
outro?
Muitas classes possuem um método denominado clone que efetuam uma cópia do objeto
referenciado, de forma que ao se modificar um, o outro não é alterado.
É possível designar null a uma variável objeto para indicar que ela atualmente não faz
referência a nenhum objeto.
umEmpregado = null;
if (umEmpregado != null) umEmpregado.aumentaSalario(5);
Variáveis objeto locais NÃO são automaticamente inicializadas com null. É preciso
inicializá-las com o operador new ou atribuindo-lhes null.
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 64
A Classe GregorianCalendar
O comando abaixo faz o seguinte:
1. Cria uma nova instância da classe GregorianCalendar denominada hoje.
2. Ao mesmo tempo inicializa o estado do objeto hoje para a data corrente (data do
sistema).
GregorianCalendar hoje = new GregorianCalendar();
Também é possível criar uma instância da classe GregorianCalendar com uma data
específica:
GregorianCalendar umaData = new GregorianCalendar(1999, 11, 31);
Isto cria uma instância da classe GregorianCalendar denominada umaData com um valor
inicial de 31 de dezembro de 1999. Observe que os meses variam de zero (Janeiro) a 11
(dezembro).
A classe GregorianCalendar é, na realidade, uma classe para Data e Hora. Veja o
programa abaixo:
import java.util.*;
public class Datas
{ public static void main(String[ ] args)
{ GregorianCalendar todaysDate = new GregorianCalendar();
int dia = todaysDate.get(Calendar.DAY_OF_MONTH);
int mes = todaysDate.get(Calendar.MONTH);
int ano = todaysDate.get(Calendar.YEAR);
System.out.println(dia + "/" + (mes + 1) + "/" + ano);
GregorianCalendar viradaDoAno2000 =
new GregorianCalendar(1999, Calendar.DECEMBER, 31,
23, 59, 59);
dia = viradaDoAno2000.get(Calendar.DAY_OF_MONTH);
mes = viradaDoAno2000.get(Calendar.MONTH);
ano = viradaDoAno2000.get(Calendar.YEAR);
int hora = viradaDoAno2000.get(Calendar.HOUR);
int minuto = viradaDoAno2000.get(Calendar.MINUTE);
int segundo = viradaDoAno2000.get(Calendar.SECOND);
System.out.println
(dia + "/" + (mes + 1) + "/" + ano + " " +
hora + ":" + minuto + ":" + segundo);
}
}
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 65
Observações:
Calendar.DECEMBER é uma constante que pode ser utilizada em vez do número 11.
A razão pela qual a data em java é representada por uma classe e não por um tipo da
linguagem é que existem aspectos relativos a datas que variam de país para país.
Assim, cada programador pode, determinar o formato de data que deseja, sem que a
linguagem tenha que prever todos os aspectos de internacionalização possíveis.
Alguns Métodos Definidos para a Classe GregorianCalendar
GregorianCalendar ()
Constrói um objeto que representa a data e hora corrente do sistema.
GregorianCalendar (int year, int month, int date)
Constrói um objeto que representa a data fornecida.
GregorianCalendar (int year, int month, int date,
int hour, int minutes, int seconds)
Constrói um objeto que representa a data e a hora fornecidas.
boolean equals (Object when)
Compara este objeto calendário com when e retorna true se o objeto representa o
mesmo ponto no tempo.
boolean before (Object when)
Compara este objeto calendário com when e retorna true se ele vem antes de when.
boolean after (Object when)
Compara este objeto calendário com when e retorna true se ele vem depois de when.
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 66
int get (int field)
Parâmetro: field – um dos seguintes
Calendar.YEAR
Calendar.MONTH
Calendar.DAY_OF_MONTH // Dia do Mês
Calendar.DATE // Dia do Mês
Calendar.HOUR
Calendar.MINUTE
Calendar.SECOND
Calendar.AM_PM
Etc
void set (int field, int value)
Designa um valor para um campo específico.
void set (int year, int month, int date)
Altera os valores para ano, mês e dia.
void set (int year, int month, int date,
int hour, int minutes, int seconds)
Altera os valores de data e hora.
void add (int field, int amount)
Adiciona a determinado campo uma certo tempo.
Observação:
O calendário juliano termina em 15 de outubro de 1582 às 00:00:00. Neste instante
começa o calendário gregoriano.
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 67
Métodos para Acesso e Mutação
A classe GregorianCalendar possui um forma atípica de acessar a informação armazenada
em um objeto calendário. Normalmente uma classe com este objetivo teria métodos como
getYear, getMonth, etc. No entanto a classe GregorianCalendar possui um único método
denominado get que pode ser utilizado para recuperar uma grande quantidade de
informação e não apenas o ano ou o mês.
Para selecionar um item que você deseja recuperar é preciso passar uma constante
definida na classe Calendar, tal como: Calendar.MONTH ou
Calendar.DAY_OF_MONTH.
Exemplo:
GregorianCalendar umaData =
new GregorianCalendar(1999, Calendar.DECEMBER, 31, 23, 59, 59);
System.out.println (umaData.get(Calendar.DATE) + "/" +
(umaData.get(Calendar.MONTH)+ 1) + "/" +
umaData.get(Calendar.YEAR));
E para modificar uma data é preciso utilizar um dos métodos set ou o método add.
Exemplo:
GregorianCalendar umaData =
new GregorianCalendar(1999, Calendar.DECEMBER, 31,
23, 59, 59);
umaData.set(Calendar.YEAR, 2000);
Observe que existe uma diferença conceitual entre o método get e os métodos set e add.
O método get apenas verifica o estado de um objeto e o informa. Já os métodos set e add
modificam o estado de um objeto.
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 68
Objetos como Argumentos de Funções
Objetos, em chamadas de funções são, passados por valor e não por referência. Vejamos
os exemplos abaixo:
Exemplo 1
static void trocaDias (GregorianCalendar a, GregorianCalendar b)
{ GregorianCalendar temp = b;
b = a;
a = b;
}
A chamada da função abaixo,
GregorianCalendar data1 = new GregorianCalendar (1999, 10, 10);
GregorianCalendar data2 = new GregorianCalendar (1999, 12, 20);
trocaDias(data1, data2);
produzirá o seguinte efeito:
Data1 Data2 A B
Logo antes do return 10/10/99 20/12/99 10/10/99 20/12/99
Após o retorno 10/10/99 20/12/99 20/12/99 10/10/99
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 69
Exemplo 2
import java.util.*;
public class ManipulaDatas
{ static void mudaData(GregorianCalendar d, int somarAnos)
{ int dia = d.get(Calendar.DATE);
int mes = d.get(Calendar.MONTH);
int ano = d.get(Calendar.YEAR);
System.out.println (dia + "/" + (mes + 1) + "/" + ano);
d = new GregorianCalendar(ano + somarAnos, mes, dia);
dia = d.get(Calendar.DATE);
mes = d.get(Calendar.MONTH);
ano = d.get(Calendar.YEAR);
System.out.println (dia + "/" + (mes + 1) + "/" + ano);
}
public static void main (String[] args)
{ GregorianCalendar data1 = new GregorianCalendar (1999, 11, 31);
System.out.println (data1. get(Calendar.DATE) + "/" +
(data1.get(Calendar.MONTH) + 1) + "/" +
data1.get(Calendar.YEAR));
mudaData (data1, 2);
System.out.println (data1.get(Calendar.DATE) + "/" +
(data1.get(Calendar.MONTH) + 1) + "/" +
data1.get(Calendar.YEAR));
}
}
No exemplo acima aparentemente o objeto está sendo modificado dentro do método, no
entanto, como Java nunca passa parâmetros de métodos por referência, este exemplo não
vai funcionar. A variável d recebe uma cópia do ponteiro para o objeto referenciado por
data1.
O comando
d = new GregorianCalendar(ano + somarAnos, mes, dia);
instancia um novo objeto e faz d apontar para ele. O objeto apontado por d é modificado,
apenas dentro do método, uma vez que d está apontando para um novo objeto
GregorianCalendar. A variável data1, no método main, continua apontando para o objeto
original. Quando Java termina a execução do método mudaData a variável d é
abandonada e a memória alocada para ela será eventualmente recuperada pela função de
coleta de lixo do Java.
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 70
A Classe Date
java.lang.Object
|
+--java.util.Date
|
+--java.sql.Date
Esta classe permite que JDBC identifique a data como uma SQL DATE.
valueOf
public static Date valueOf(String s)
Converte um string no formato java.sql.Date em java.util.Date.
Parâmetros: s – uma string data no formato "yyyy-mm-dd"
Retorna: um objeto java.sql.Date.
toString()
public String toString()
Formata uma data no formato JDBC.
Returns: um String no formato yyyy-mm-dd
Overrides: efetua o override do método toString da classe java.util.Date
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 71
Exemplo:
String umaData = "1999-05-12";
Date dataAdmissao = Date.valueOf(umaData);
// dataAdmissao pode ser utilizada em um comando SQL Insert
// E para converter um objeto data no formato JDBC para String há
// duas possibilidades:
1.
String dataAdmString = dataAdmissao.toString();
System.out.println ("Data = " + dataString);
2.
df = DateFormat.getDateInstance();
String umStringData = df.format(dataAdmissao);
System.out.println ("Data = " + dataString);
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 72
Construindo suas Próprias Classes
Exemplo 1: Uma classe Empregado.
class Empregado
{ public Empregado (int num, String n, double s)
{ numero = num;
nome = n;
salario = s;
}
public void print()
{ System.out.println (numero + " " + nome + " " + salario);
}
public void aumentaSalario (double percentualDeAumento)
{ salario = salario * (1 + (percentualDeAumento/100));
}
private int numero;
private String nome;
private double salario;
}
import java.util.*;
public class TestaEmpregado
{ public static void main (String[] args)
{ Empregado[] vetEmpregados = new Empregado[3];
vetEmpregados [0] =
new Empregado (10, "Luis Claudio", 3500);
vetEmpregados [1] =
new Empregado (20, "Vinicius Aguiar", 4500);
int i;
for (i = 0; i < 2; i++) vetEmpregados[i].aumentaSalario(5);
for (i = 0; i < 2; i++) vetEmpregados[i].print();
}
}
Exercício: Acrescente à classe abaixo uma data de contratação do empregado utilizando
a classe GregorianCalendar. Acrescente ao construtor desta classe uma data de
contratação e construa métodos para:
a) permitir que a data de contratação seja alterada.
b) retornar o ano de contratação do empregado.
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 73
Solução: Uma Classe Empregado com uma data de contratação.
import java.util.*;
class Empregado
{ public Empregado (int num, String n, double s,
GregorianCalendar d)
{ numero = num;
nome = n;
salario = s;
dataContratacao = d;
}
public void print()
{ System.out.println (numero + " " + nome + " " + salario + " " +
anoContratacao());
}
public void aumentaSalario (double percentualDeAumento)
{ salario = salario * (1 + (percentualDeAumento/100));
}
public void alteraDataContratacao (int ano, int mes, int dia)
{ dataContratacao.set(Calendar.YEAR, ano);
dataContratacao.set(Calendar.MONTH, mes);
dataContratacao.set(Calendar.DATE, dia);
}
public int anoContratacao()
{ return dataContratacao.get(Calendar.YEAR);
}
private int numero;
private String nome;
private double salario;
private Calendar dataContratacao;
}
import java.util.*;
public class TestaEmpregado
{ public static void main (String[] args)
{ Empregado[] vetEmpregados = new Empregado[3];
vetEmpregados [0] =
new Empregado (10, "Luis Claudio", 3500,
new GregorianCalendar (1989, 10, 1));
vetEmpregados [1] =
new Empregado (20, "Vinicius Aguiar", 4500,
new GregorianCalendar (1992, 11, 6));
int i;
for (i = 0; i < 2; i++) vetEmpregados[i].aumentaSalario(5);
for (i = 0; i < 2; i++) vetEmpregados[i].print();
}
}
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 74
Analisando a Classe Empregado
Esta classe possui 4 métodos:
public Empregado (String n, double s, Day d)
public void print()
public void aumentaSalario (double percentualDeAumento)
public int anoContratacao()
A palavra-chave public indica que qualquer método de qualquer classe que possui acesso
a uma instância da classe empregado pode chamar o método. Existem 4 níveis de acesso
possíveis:
• private
• protected
• public
• package (o default, quando não se especifica nada).
A seguir note que existem 4 campos que armazenarão dados que iremos manipular dentro
de uma instância da classe Empregado:
private String nome;
private double salario;
private Day dataContratacao;
A palavra-chave private indica nenhuma outra classe terá acesso a estes dados a não ser
através dos métodos (public) da classe Empregados. Campos da instância de uma classe
são geralmente private.
Observe ainda que um dos campos é uma instância da classe GregorianCalendar. É
comum classes possuírem campos de instância (instance fields) que são instâncias de
classes (class instances).
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 75
Construtores
A seguir vem um exemplo de método construtor. O construtor é utilizado para inicializar
os objetos de uma classe. Sua função principal é atribuir valores iniciais às variáveis da
instância.
Por exemplo, quando você cria uma instância da classe empregado com o código abaixo:
dataContratacao = new GregorianCalendar(1950, 0, 1);
Empregado numero007 =
new Empregado (“James Bond”, 100000, dataContratacao);
Você cria uma instância com os seguintes dados:
nome = “James Bond”;
salario = 100000;
dataContratacao = 01/01/1950 // Na realidade um objeto do tipo
// GregorianCalendar com este dado
// encapsulado.
O método new é sempre utilizado com o construtor para criar um objeto da classe. Isto o
força a determinar os valores iniciais dos seus objetos. Em java é preciso inicializar as
variáveis de instância de um objeto implicitamente ou explicitamente.
Os Métodos da Classe Empregado
Observe que os métodos definidos para a classe Empregado podem acessar, pelo nome,
os campos da instância definidos como private.
Por exemplo,
public void aumentaSalario (double percentualDeAumento)
{ salario = salario * (1 + (percentualDeAumento/100));
}
estabelece um novo valor para o campo salario, do objeto que executa este método.
Já o comando
numero007.aumentaSalario(5);
aumenta em 5% o valor do campo salario do objeto apontado por numero007. Este
método é uma função com dois argumentos: O primeiro argumento, denominado
argumento implícito, é o objeto do tipo Empregado que aparece antes do nome do
método. E o segundo argumento (argumento explícito) é o número entre parêntesis que
aparece após o nome do método.
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 76
O método anoContratacao – abaixo – retorna um valor inteiro, através da aplicação de um
método sobre a variável de instância dataContratacao. Isto faz sentido uma vez que
dataContratacao é uma variável de instância da classe GregorianCalendar que, de fato,
possui um método denominado get.
public int anoContratacao()
{ return dataContratacao.get(Calendar.YEAR);
}
Finalmente, vamos examinar um método mais simples denominado getNome:
public String getNome()
{ return nome;
}
Este é um exemplo óbvio de um método para acesso a uma variável de instância, uma vez
que ele trabalha diretamente com um campo da classe. Ele simplesmente retorna o valor
corrente do campo nome. Para quem está implementando a classe é muito mais
trabalhoso criar um campo private e um método public do que criar um campo public.
No entanto, isto não gera trabalho para os usuários da classe, já que os programadores
que a utilizam terão de escrever numero007.getNome() em vez de
numero007.nome. O fato é que para o mundo exterior à classe Empregados nome é
read-only, isto é, apenas as operações da classe podem modificá-lo. Quando o usuário de
uma classe necessita ler e alterar um campo da classe, é preciso, ao criar a classe,
fornecer 3 itens:
• Um campo do tipo private.
• Um método público de acesso à informação.
• Um método público de atualização da informação.
As vantagens desta estratégia são:
• A implementação interna pode ser alterada sem afetar nenhum código além dos
métodos da classe.
• Os métodos responsáveis por alterar campos ficam responsáveis pela validação dos
mesmos.
Cuidado para não escrever um método de acesso que retorne uma referência para um
objeto mutável. Por exemplo:
class Empregado
{ . . .
public String getNome();
public Day getDataContratacao();
private String nome;
private double salario;
private GregorianCalendar dataContratacao;
}
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 77
O código abaixo quebra o encapsulamento:
dataContratacao = new GregorianCalendar(1950, 1, 1);
Empregado numero007 =
new Empregado (“James Bond”, 100000, dataContratacao);
Empregado lucia = new Empregado (“Lucia”, 10000,
new GregorianCalendar(1980, 1, 1));
GregorianCalendar d = lucia.getDataContratacao();
d.set(Calendar.YEAR, 1970) // Isto dá a lucia mais 10 anos de
// trabalho na empresa. (Diminui a
// data de admissão em 10 anos).
A razão é que d e lucia.getDataContratacao() fazem referência ao mesmo objeto. Ao
modificar d estaremos modificando um estado privado do objeto lucia.
O método getNome não sofre do mesmo mal uma vez que nome é um string e strings são
imutáveis. Não há métodos para alterar strings. Objetos da classe GregorianCalendar, no
entanto, podem ser modificados através do método set.
A solução para este problema é clonar o campo dataContratacao antes de retorná-lo para
o método que faz o acesso. Um clone é uma cópia fiel de um objeto que é armazenada em
um novo local. Esta técnica será discutida mais adiante. O código com a correção seria:
class Empregado
{ . . .
public GregorianCalendar getDataContratacao()
{ return (GregorianCalendar)dataContratacao.Clone(); }
}
Observação:
• Sempre utilize um clone quando for necessário retornar um campo mutável.
• E para atribuir esta nova data ao objeto lucia faça:
lucia.mudaDataContratacao(novaData);
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 78
Métodos Privados
Métodos privados só podem ser chamados por outros métodos da mesma classe. Estes
métodos não são utilizados diretamente pelos usuários da classe e, caso seja necessário,
poderão ser apagados sem que isto afete as aplicações.
Métodos Construtores
Regras para a criação de construtores:
Observações sobre Construtores:
• Sempre possui o mesmo nome da classe.
• Pode possuir zero ou mais parâmetros.
• Sempre chamado com a palavra-chave new. Isto o diferencia dos demais métodos.
Não é possível alterar os valores de campos da instância através de um construtor. Por
exemplo, chamar o método GregorianCalendar como vem a seguir resulta em um
erro: d.GregorianCalendar(1950,1,1).
• Não tem um valor de retorno. (Não se escreve void)
public Empregado (String n, double s, GregorianCalendar d)
• É possível ter mais de um construtor por classe. Você já viu isto nas classes
GregorianCalendar e GregorianCalendar.
• Construtores não são herdados.
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 79
Exemplos:
GregorianCalendar hoje = new GregorianCalendar();
ou
GregorianCalendar viradaDoMilenio =
new GregorianCalendar(1999, 12, 31);
A possibilidade de vários métodos possuírem o mesmo nome, mas argumentos diferentes
denomina-se overloading. O interpretador Java fica responsável por descobrir qual
método deve chamar. Ele (interpretador) seleciona o método que deve ser executado
casando os tipos dos argumentos nos cabeçalhos dos vários métodos com os tipos dos
valores utilizados em uma chamada de método específica.
Java determina um valor default (zero para números, null para objetos e false para
boleanos) se você não os inicializa explicitamente. No entanto, esta é considerada uma
prática de programação pobre, logo é sempre uma boa prática de programação inicializar
todos os campos independentemente do construtor utilizado.
Se todos os construtores de uma classe necessitam designar um mesmo valor para uma
variável de instância específica, há uma sintaxe apropriada para esta situação. Basta
designar um valor ao campo dentro da definição da classe.
Para atribuir um mesmo valor para todos as instâncias de uma classe independentemente
do construtor utilizado use a sintaxe abaixo. No exemplo abaixo, deseja-se sempre
inicializar um objeto Cliente designando-se o valor “RJ” para a variável de instância
codigoEstado.
class Cliente
{ public Cliente (String n)
{ nome = n;
numeroDaConta = Conta.getNumber();
}
public Cliente (String n, int a)
{ nome = n;
numeroDaConta = a;
}
. . .
private String nome;
private int numeroDaConta;
private int codigoEstado = “RJ”;
}
No exemplo abaixo, cada objeto Cliente é inicializado com um call separado a
Conta.getNumero(), isto é, numeroDaConta não recebe uma constante, logo,
sugerimos que esta inicialização seja efetuada dentro do construtor e não fora dele como
vem abaixo:
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 80
class Cliente
{ public Cliente (String n)
{ nome = n;
}
. . .
private String nome;
private int numeroDaConta = Conta.getNumero();
}
Construtores Default
Um construtor default é um construtor sem argumentos. Se você escrever um código para
a sua classe sem nenhum construtor, Java provê um construtor default para você. Este
construtor default designa valores default para todas as variáveis de instância. Caso você
crie algum construtor para a sua classe o construtor default (sem argumentos) não será
criado.
A partir do momento que um construtor é criado para a classe Xxxx o construtor default
deixa de existir, logo, a partir deste momento, uma chamada a new Xxxx() causará um
erro de compilação.
Overloading
Em algumas circunstâncias você pode querer escrever vários métodos na mesma classe
que fazem basicamente a mesma coisa com diferentes argumentos. Considere um simples
método para imprimir a representação textual do seu argumento. Este método poderia se
chamar print().
Agora suponha que você necessite de diferentes métodos print para imprimir cada
variável membro do tipo int, float e String. Isto é razoável, uma vez que diferentes tipos
necessitam de formatações distintas. Você poderia criar 3 métodos: printint(),
printfloat() e printString().
Java permite, no entanto, que você reutilize um mesmo nome de método em mais de um
método. Por exemplo:
public void print(int i);
public void print(float f);
public void print(String s);
Quando você escreve código para chamar um destes métodos, o método apropriado é
escolhido de acordo com o tipo do argumento que você fornece.
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 81
Duas regras se aplicam para métodos overloaded:
1. A lista de argumentos deve ser diferente para que o método correto seja chamado.
2. O tipo de retorno pode ser diferente, mas isto não é suficiente. A lista de argumentos
deve ser diferente.
Exemplo de Overloading
import java.util.*;
import corejava.*;
public class TestaEmpregado
{ public static void main (String[] args)
{ Empregado[] vetEmpregados = new Empregado[3];
vetEmpregados [0] =
new Empregado ("Luis Claudio", 3500,
new GregorianCalendar (1989, 10, 1));
vetEmpregados [1] =
new Empregado ("Vinicius Aguiar", 4500,
new GregorianCalendar (1992, 12, 6));
vetEmpregados [2] =
new Empregado ("Luciana Arruda", 2500,
new GregorianCalendar (1993, 1, 12));
int i;
for (i = 0; i < 3; i++)
{ vetEmpregados[i].print(vetEmpregados[i].getNome());
vetEmpregados[i].print(vetEmpregados[i].getSalario());
}
}
}
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 82
import java.util.*;
class Empregado
{ public Empregado (String n, double s, GregorianCalendar d)
{ nome = n;
salario = s;
dataContratacao = d;
}
public void print()
{ System.out.println (nome + " " + salario + " " +
anoContratacao());
}
public void aumentaSalario (double percentualDeAumento)
{ salario = salario * (1 + percentualDeAumento) / 100;
}
public void alteraDataContratacao (int ano, int mes, int dia)
{ dataContratacao.set(Calendar.YEAR, ano);
dataContratacao.set(Calendar.MONTH, mes);
dataContratacao.set(Calendar.DATE, dia);
}
public int anoContratacao()
{ return dataContratacao.get(Calendar.YEAR);
}
public String getNome()
{ return nome;
}
public double getSalario()
{ return salario;
}
public void print(int i)
{ System.out.println (i);
}
public void print(String s)
{ System.out.println (s);
}
public void print(float f)
{ System.out.println (f);
}
public void print(double d)
{ System.out.println (d);
}
private String nome;
private double salario;
private GregorianCalendar dataContratacao;
}
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 83
O Objeto this
Ocasionalmente você pode desejar acessar um objeto por inteiro, e não uma variável de
instância particular. Neste caso Java provê uma forma conveniente para realizar este
acesso através da palavra-chave this. Em um método a palavra-chave this referencia o
objeto no qual o método opera.
Em uma função tradicional os dados que devem ser manipulados não referenciados
através de nomes de argumentos passados para a função. Em Java não há nomes de
argumentos. No entanto você pode referenciar o objeto que é o alvo da operação através
da palavra-chave this. Por exemplo:
Em linguagens que não suportam esta associação entre dados e código, a declaração do
método amanha especifica uma data em particular que deve ser incrementada em um dia.
Por exemplo:
Uma função convencional:
umaData = amanha(Data d);
A chamada a um método Java:
Data d = new Data(); // Um objeto do tipo Data (referenciado
// por d) contendo a data de hoje é
// criado.
d.amanha();
Um método Java com a palavra-chave this:
public class Data
{ public void amanha()
{ this.day = this.day + 1; // Simplificação ...
}
private int dia, mes, ano;
}
Java automaticamente associa todas as referências a variáveis e métodos à palavra-chave
this, logo utilizar a palavra-chave this como utilizado acima é uma redundância.
public class Data
{ public void amanha()
{ day = day + 1; // Simplificação ...
}
private int dia, mes, ano;
}
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 84
Há ocasiões, no entanto, em que a palavra-chave this não é redundante. Por exemplo,
você pode querer chamar um método de uma classe completamente diferente e passar
uma instância de um objeto como argumento.
Exemplo: A implementação de uma associação entre Empregados e Departamentos.
Há um outro significado para a palavra-chave this. Se o primeiro comando de um
construtor possui a forma this (. . .), então o construtor chama outro construtor da mesma
classe.
Exemplo:
class Cliente
{ public Cliente (String n)
{ this (n, Conta.getNumero(nc));
}
public Cliente (String n, int a)
{ nome = n;
numeroDaConta = nc;
}
. . .
}
Quando você chama new Cliente (“Luiza Thomé”), então o construtor
Cliente (String) chama o construtor Cliente (String, int).
O Método toString
Muitas classes em Java possuem um método chamado toString() que retorna um string
que descreve o objeto. Se você passar um objeto (por inteiro) para o método
System.out.println, este método chamará o método toString definido na classe deste
objeto e será impresso o string que descreve o objeto. Esta é uma estratégia interessante
ao se fazer debug de uma aplicação.
Exemplo:
System.out.println (nomeDoObjeto);
// Funciona como se tivesse sido escrito:
// System.out.println (nomeDoObjeto.toString());
Em vez de:
System.out.println (nomeDoObjeto.getNomeDeUmAtributoPrivate);
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 85
Blocos de Inicialização
Você já viu duas maneiras de inicializar um campo:
• designando um valor em um construtor.
• designando um valor em uma declaração. (uma cte ou uma chamada a um método)
Há uma terceira maneira, denominada bloco de inicialização. Estes blocos são executados
sempre que um objeto da classe é construído.
Exemplo:
class Cliente
{ public Cliente (String n)
{ nome = n;
}
. . .
// Um bloco de inicialização
static
{ numeroDaConta = Conta.getNumero();
}
. . .
}
Este mecanismo nunca é necessário, embora comum. É geralmente mais claro colocar o
código de inicialização dentro de um construtor. No entanto, para inicializar variáveis
estáticas, um bloco de inicialização de variáveis estáticas pode ser útil.
Ordem de execução quando um construtor é chamado:
1. Todos os campos são inicializados, na ordem em que foram declarados. Valores
default são assumidos, quando valores iniciais não tiverem sido especificados.
2. Todos os blocos de inicialização são executados, na ordem em que aparecem.
3. Se a primeira linha de um construtor chama outro construtor, então aquele construtor
é executado.
4. O corpo do construtor é executado.
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 86
Métodos e Campos Estáticos
Campos estáticos não mudam de uma instância de uma classe para outra, logo você deve
pensar neles como campos pertencentes à classe. Da mesma forma, métodos estáticos
pertencem à classe e não podem ser executados sobre nenhuma instância da classe. Por
exemplo, os métodos da classe Console, assim como todos os métodos da classe Math.
Exemplos:
double x = Console.readDouble();
double x = Math.pow (3, 4);
Já vimos também a utilização de campos estáticos:
Math.PI
System.out
São campos estáticos das classes Math e System.
Como Inicializar um Campo Estático
Há duas maneiras:
• Fornecendo um valor inicial.
Exemplo:
public static final CM_POR_POLEGADA = 2.54;
• Através de um bloco de inicialização estático. Este método é utilizado quando a
inicialização não cabe em uma única expressão. A inicialização das variáveis estáticas
ocorre quando a classe é carregada pela primeira vez. Primeiramente todas as
variáveis estáticas são inicializadas na ordem em que foram declaradas e
posteriormente são executados todos os blocos estáticos de inicialização.
Exemplo 1:
static double[] buffer = new double[BUFFER_SIZE];
static
{ int i;
for (i = 0; i < buffer.length; i++)
buffer[i] = java.lang.Math.random();
}
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 87
Exemplo 2:
Um outro exemplo de bloco estático é o método main de uma classe. Seu header possui a
forma:
public static void main (String[] args)
Como o método main é estático, não é necessário instanciar a classe para se poder chamá-
lo. Ao se executar java abc o interpretador java simplesmente inicia o método main
sem criar um objeto da classe abc. Por esta razão, o método main só pode acessar
campos de instância estáticos. Não é raro o método main criar um objeto da sua própria
classe, conforme exemplo abaixo:
public class Aplicacao
{ . . .
public static void main (String[] args)
{ Aplicacao a = new Aplicacao();
// Agora é possível chamar o método a.xyz();
}
}
Quando o programa começa, o método main, ao ser executado, cria um objeto do tipo
Aplicacao. A partir deste momento ele pode invocar métodos de instância sobre o objeto
criado.
Como Compilar Programas
Para compilar várias classes que começam com a letra “C”, por exemplo digite:
Javac C*.java
Uma outra alternativa de compilação é compilar apenas a classe que se deseja executar.
Quando o compilador encontrar uma referência a outra classe (Xxxx, por exemplo) ele irá
procurar por um arquivo denominado Xxxx.class. Se o arquivo não for encontrado, o
arquivo Xxxx.java será procurado e, então, compilado. E tem mais: se o timestamp do
arquivo Xxxx.java for mais novo do que o timestamp do arquivo Xxxx.class, Xxxx.java
será recompilado.
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 88
Packages
Java nos permite agrupar classes em uma coleção denominada package. Packages são
utilizados com o objetivo de organizar o seu trabalho e para separar o seu código das
bibliotecas de código fornecidas por terceiros. Por exemplo, a biblioteca standard de Java
é distribuída com uma série de packages, como, java.lang, java.util,
java.net, etc. Como você já deve ter notado, é possível organizar os packages
utilizando níveis de aninhamento. Todos os packages standard de Java estão dentro da
hierarquia de packages java.
Uma razão para aninhar pacotes é garantir a unicidade dos nomes utilizados para nomear
pacotes e classes. Por exemplo, você poderia criar um package denominado sistemas e
dentro de sistemas seriam criados outros, um para cada aplicação.
Exemplo:
sistemas
comuns
Aqui seriam criadas as classes comuns a todos os sistemas.
contas.receber
Aqui seriam criadas as classes do sistema de Contas a receber
contas.pagar
Idem Contas a Pagar.
faturamento
Idem Faturamento.
fluxocaixa
Idem Fluxo de Caixa.
Quando se escreve um package é preciso por o nome do pacote no topo do arquivo fonte,
antes do código que define as classes existentes no package.
Por exemplo:
Package faturamento;
Isto significa que o arquivo .java faz parte do package denominado faturamento. Este
comando deve ser o primeiro comando em um arquivo. Antes dele, só comentários.
Se você não colocar este comando em um arquivo fonte, Java adicionará as classes
existentes neste arquivo a um package conhecido como default package. O default
package não possui um path, logo, se o diretório corrente (.) faz parte do class path, então
as classes sem declaração de package no diretório corrente automaticamente se tornam
parte do default package. Todas as classes existentes no class path fazem parte do default
package.
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 89
Utilizando Packages
Há duas maneiras de utilizar as classes públicas de um package:
• Forneça o nome completo do package.
Exemplo:
java.util.GregorianCalendar hoje = new java.util.GregorianCalendar();
• Indique em que packages as classes se encontram.
Exemplo:
import java.util.*
GregorianCalendar hoje = new GregorianCalendar();
Observações:
• O asterisco (*) é utilizado para importar as classes pertencentes a um único package.
• Não é possível utilizar import java.* esperando importar todos os packages cujos
nomes começam por java.
• Se duas classes possuem o mesmo nome, embora em packages diferentes, não é
possível importar ambas.
Como o Compilador Localiza os Packages
O compilador do JDK e a maioria dos compiladores Java armazenam os packages em
subdiretórios do sistema de arquivos ou em arquivos .zip ou .jar. Se você utiliza o
JDK para Windows, o class path é determinado pela variável de ambiente CLASSPATH
conforme vem abaixo:
SET CLASSPATH=.;C:\JDK\LIB\CLASSES.ZIP;C:\COREJAVA
O Escopo de um Package
Se um método ou variável é definido sem um access modifier (public, private, etc)
então ele poderá ser acessado por todos os métodos no mesmo package. Para os métodos
este default é razoável, mas para variáveis, não, uma vez que se você esquecer de definir
uma variável como private ela ficará visível dentro de todo o package, o que quebra o
encapsulamento.
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 90
Como a classe TestaEmpregado não está dentro de um package, ela se encontra dentro do
package default que é formado pelo CLASSPATH.
import java.util.*;
public class TestaEmpregado
{ public static void main (String[] args)
{ // Projeto umProjeto = new Projeto("Bacuri");
Empregado [] empregados = new Empregado[2];
Empregado vinicius = new Empregado ("Vinicius Aguiar", 4500,
new GregorianCalendar (1992, 6, 13));
Empregado carlos = new Empregado ("Carlos Ribeiro", 5500,
new GregorianCalendar (1993, 7, 14));
empregados[0] = carlos;
empregados[1] = vinicius;
int n = FuncoesGerais.encontrar(empregados, carlos);
if (n==-1)
System.out.println("Não achou");
else
System.out.println("Achou na posição: " + n);
}
}
Examinando a classe abaixo você verá que ela não se encontra dentro de nenhum
package, logo, o método encontrar só poderá ser executado (isto é, só será
encontrado) caso a classe onde se encontra a chamada ao método encontrar também
estiver no package default.
Quando não se especifica nada para um método o método é visível no package.
O package default é o formado pelo conteúdo do CLASSPATH. Se uma classe está dentro
de um package e chama um método de outra classe, não basta que esta outra esteja no
CLASSPATH. É preciso que ela esteja dentro de um package no class path. O package
default não será pesquisado, a menos que a classe que chama o método da outra também
esteja no package default.)
public class FuncoesGerais
{ static int encontrar (Object[] a, Object b)
{ int i;
for (i = 0; i < a.length; i++)
if (a[i].equals(b)) return i;
return -1; // Caso não encontre.
}
}
Quando você utiliza a declaração de um package você não necessita importar o mesmo
package ou qualquer outro elemento do package. Lembre-se que o comando import é
utilizado para trazer classes em outros packages.
Como o Compilador Localiza os Arquivos .java
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 91
Através do CLASSPATH ou através do parâmetro –sourcepath. O parâmetro –d indica
onde os arquivos .class devem ser armazenados.
javac -sourcepath c:\java\packages -d c:\java\classes BemVindo.java
O parâmetro –classpath especificado abaixo indica ao interpretador onde encontrar o
arquivo .class.
java -classpath c:\java\classes BemVindo
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 92
HERANÇA
Neste capítulo vamos nos concentrar nas técnicas de derivação de uma classe a partir de
outra. Através da herança é possível reutilizar ou modificar métodos de classes existentes,
assim como adicionar novos campos de instância e métodos à nova classe.
Java permite que uma classe estenda apenas uma outra classe. Esta restrição é conhecida
como herança simples. Mais adiante examinaremos a facilidade denominada
interface que permite alguns dos benefícios da herança múltipla.
Vamos voltar a examinar a classe Empregado. Suponha que você trabalha em uma
companhia onde os gerentes são tratados de maneira substancialmente diferente dos
demais empregados. Seus salários são calculados de maneira diferente, possuem direito a
uma secretária, etc. Neste caso, será preciso criar uma classe Gerente e adicionar
funcionalidade a ela. Note que existe um relacionamento do tipo “é-um” entre Gerente e
Empregado, isto é, todo gerente é um empregado.
Classe Empregado
import java.util.*;
class Empregado
{ public Empregado (String n, double s, GregorianCalendar d)
{ nome = n;
salario = s;
dataContratacao = d;
}
public void print()
{ System.out.println (nome + " " + salario + " " +
anoContratacao());
}
public void aumentaSalario (double percentualDeAumento)
{ salario = salario * (1 + percentualDeAumento) / 100;
}
public int anoContratacao()
{ return dataContratacao.getYear();
}
public String getNome()
{ return nome;
}
public String setNome(String n)
{ nome = n;
}
private String nome;
private double salario;
private GregorianCalendar dataContratacao;
}
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 93
Classe Gerente
import corejava.*;
class Gerente extends Empregado
{ public Gerente (String n, double s, GregorianCalendar d)
{ super(n, s, d);
nomeDaSecretaria = " ";
}
public void aumentaSalario (double percentualDeAumento)
{ // adiciona 0,5 % de bonus para cada ano de serviço
GregorianCalendar hoje = new GregorianCalendar();
double bonus = 0.5 * (hoje.getYear() - anoContratacao());
super.aumentaSalario (percentualDeAumento + bonus);
}
public String getNomeDaSecretaria()
{ return nomeDaSecretaria;
}
public void setNomeDaSecretaria(String nome)
{ nomeDaSecretaria = nome;
}
private String nomeDaSecretaria;
}
Observações:
• A palavra-chave extends indica que você está criando uma nova classe a partir de
uma classe existente. A classe existente é denominada superclasse, classe base ou
classe pai. A nova classe é denominada subclasse, classe derivada ou classe filho.
• A seguir observe o construtor da classe Gerente:
public Gerente (String n, double s, GregorianCalendar d)
{ super(n, s, d);
nomeDaSecretaria = " ";
}
• A palavra-chave super se refere à superclasse (Empregado), e a linha super(n, s, d);
chama o construtor da classe Empregado passando os parâmetros n, s e d. Se o
construtor da subclasse não chamar explicitamente o construtor da superclasse, o
construtor default (sem parâmetros) será chamado. Neste caso, se não existir um
construtor default, ocorrerá um erro de compilação. Logo, a menos que a subclasse
esteja satisfeita com o construtor default da superclasse, ela sempre deverá utilizar a
palavra chave super com os parâmetro apropriados para chamar o construtor
desejado.
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 94
• O compilador exige que a chamada com a palavra-chave super seja o primeiro
comando do construtor da subclasse.
• Uma subclasse geralmente possui mais campos de instância do que a superclasse,
logo, seguindo a boa prática de programação, designamos um string vazio para o
campo de instância nomeDaSecretaria a fim de inicializá-lo. (Por default ele seria
inicializado com nulo.)
• Dentre os vários métodos existentes na classe Empregado, muitos não são redefinidos
na classe Gerente. Deve-se indicar apenas as diferenças entre a superclasse e a
subclasse. Por exemplo, não foi preciso fornecer uma nova definição do método
getNome uma vez que este método (na superclasse) já faz o que desejamos. O fato da
funcionalidade comum ter sido colocada na superclasse (factoring) é essencial para o
bom uso da herança na programação orientada a objetos.
• Por outro lado, note que foi fornecida uma nova definição do método aumentaSalario,
uma vez que desejamos que este método funcione diferentemente para gerentes e
empregados comuns.
public void aumentaSalario (double percentualDeAumento)
{ // adiciona 0,5 % de bônus para cada ano de serviço
GregorianCalendar hoje = new GregorianCalendar();
double bonus = 0.5 * (hoje.getYear() - anoContratacao());
super.aumentaSalario (percentualDeAumento + bonus);
}
Suponhamos, agora, que a empresa resolva conceder um aumento de 5% para todos
os seus funcionários. Para os gerentes, no entanto, este aumento é calculado da
seguinte forma:
Primeiramente é calculado um percentual de bônus em função do tempo de casa.
Em seguida, em função da utilização da palavra-chave super, é chamado o método
aumentaSalario da superclasse que só então calculará o aumento a ser concedido
ao gerente.
Note que o método aumentaSalario não possui acesso direto aos campos de instância
privados da superclasse. Isto significa que o método aumentaSalario da classe
Gerente não pode mudar diretamente o campo salário, embora todo objeto gerente
possua um campo salário. Se os métodos da classe Gerente necessitarem acessar os
campos de instância privados da classe Empregado, eles terão que fazer o que todos
os outros métodos fazem: utilizar a interface pública da superclasse. Neste caso, o
método aumentaSalario da classe Empregado.
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 95
Exemplo de Utilização das Classes Empregado e Gerente:
Em linguagens orientadas a objetos é possível criar coleções de coisas que possuem uma
classe ancestral comum. Podemos, por exemplo, escrever um método de ordenação para
ordenar empregados por idade ou salário sem termos de nos preocupar se um empregado
é gerente ou não.
Como em Java toda classe é uma subclasse de Object, você pode utilizar um array de
Objects como um container para qualquer tipo de objeto. A única coisa que não pode ser
adicionada a um array de Objects são variáveis primitivas. Melhor ainda do que utilizar
um array de Object seria utilizar a classe Vector que foi projetada para armazenar
coleções de objetos heterogêneos.
import java.util.*;
public class TestaEmpregadoGerente
{ public static void main (String[] args)
{ Gerente mandaChuva =
new Gerente ("Ricardo Silva", 7500,
new GregorianCalendar(1987, 12, 15));
mandaChuva.setNomeDaSecretaria ("Claudia Lins");
// Cadastramos 3 empregados:
Empregado[] vetEmpregados = new Empregado[3];
vetEmpregados [0] = mandaChuva;
vetEmpregados [1] =
new Empregado ("Claudia Lins", 2000,
new GregorianCalendar (1992, 12, 6));
vetEmpregados [2] =
new Empregado ("Luciana Arruda", 2500,
new GregorianCalendar (1993, 1, 12));
int i;
System.out.println ("Empregados e Salarios antes do aumento");
for (i = 0; i < 3; i++) vetEmpregados[i].print();
System.out.println ("");
for (i = 0; i < 3; i++) vetEmpregados[i].aumentaSalario(5);
System.out.println ("Empregados e salarios apos o aumento");
for (i = 0; i < 3; i++) vetEmpregados[i].print();
System.out.println ("");
System.out.println ("A secretaria do departamento se chama " +
mandaChuva.getNomeDaSecretaria());
}
}
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 96
Observações:
Note que vetEmpregados [1] e vetEmpregados [2] recebem aumento de 5% uma vez
que eles são objetos do tipo empregado. Já vetEmpregados [0] é um objeto do tipo
gerente, e por isso obtém um aumento maior.
Como não foram definidos métodos de impressão específicos para a classe Gerente,
os três empregados são impressos com o método de impressão da classe Empregados.
Trabalhando com Subclasses
No exemplo abaixo as variáveis vetEmpregados[0] e mandaChuva apontam para a
mesma área de memória. No entanto, para o compilador vetEmpregados [0] é apenas um
objeto do tipo empregado.
Empregado[] vetEmpregados = new Empregado[3];
Gerente mandaChuva =
new Gerente ("Ricardo Silva", 7500,
new GregorianCalendar(1987, 12, 15));
vetEmpregados [0] = mandaChuva;
Um objeto da subclasse pode ser passado como argumento para qualquer método que
espere receber um objeto da superclasse. Já o contrário não é possível (necessita de um
cast). Por exemplo, o comando abaixo resulta em um erro uma vez que o objeto da
subclasse pode possuir mais campos do que o objeto da superclasse.
mandaChuva = vetEmpregados [i]; // Seria preciso fazer um cast
Uma subclasse não pode possuir menos campos do que sua superclasse. Não há como
retirar campos de uma subclasse. Campos podem apenas ser acrescentados.
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 97
Polimorfismo
Na OOP estamos sempre enviando mensagens para objetos, solicitando que tarefas sejam
executadas. Quando você envia uma mensagem solicitando que uma subclasse execute
um método com determinados parâmetros acontece o seguinte:
A subclasse checa se ela possui um método com o mesmo nome e com exatamente os
mesmos parâmetros. Caso possua, o método é executado. Caso não possua, a classe pai
fica responsável por manipular a mensagem, isto é, por procurar em seu domínio por um
método com o mesmo nome e com exatamente os mesmos parâmetros. Se o método for
encontrado, será executado.
Como a manipulação desta mensagem pode continuar subindo na cadeia da hierarquia,
classes pais são checadas até que a cadeia termine ou até que o método seja encontrado.
Note que métodos com o mesmo nome podem existir em muitos níveis na cadeia
hierárquica. Isto nos leva a uma regra fundamental relativa a herança: Um método
definido em uma subclasse com o mesmo nome e lista de parâmetros de um método
definido em uma classe ancestral, esconde o método da classe ancestral da subclasse. Por
exemplo, o método aumentaSalario da classe Gerente é chamado em vez do método
aumentaSalario da classe Empregado quando você envia uma mensagem para um objeto
da classe Gerente.
O nome e a lista de parâmetros de um método costumam ser conhecidos como a
“assinatura” do método. Logo os métodos aumentaSalario (double) e aumentaSalario
(boolean) são métodos com o mesmo nome mas com assinaturas diferentes. O tipo do
dado retornado pelo método não faz parte da assinatura. Em java, uma superclasse não
pode possuir um método com a mesma assinatura de um método de uma subclasse mas
com tipos de retorno diferentes.
A idéia por traz do polimorfismo é que objetos pertencentes a classes diferentes (em uma
mesma hierarquia) podem responder diferentemente a mensagens iguais.
A técnica utilizada para se conseguir que um método polimórfico seja executado se
chama “late binding”. Isto significa que o compilador não gera o código para a chamada
de um método em tempo de compilação. Sempre que um método é aplicado a um objeto
o compilador gera o código necessário para calcular que método deve ser chamado. Este
processo é conhecido como “dynamic binding” ou “dynamic dispatch”. O mecanismo
tradicionalmente utilizado para a chamada de métodos é conhecido como “static
binding”.
Em Java não é necessário declarar um método como virtual (como ocorre em C++ e em
Pascal). Este comportamento é o default. Se você não quer que uma função seja virtual,
declare-a como final.
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 98
Polimorfismo em uma hierarquia de herança, é às vezes chamado de verdadeiro
polimorfismo para distingui-lo do outro tipo mais limitado denominado overloading que
não é resolvido dinamicamente, e sim estaticamente em tempo de compilação.
O que é menos óbvio é o que acontece quando temos:
Empregado[] vetEmpregados = new Empregado[2];
vetEmpregados [0] =
new Gerente ("Ricardo Silva", 7500,
new GregorianCalendar(1987, 12, 15));
vetEmpregados [1] =
new Empregado ("Luis Alberto", 2500,
new GregorianCalendar(1988, 10, 11));
vetEmpregados[0].aumentaSalario(5);
vetEmpregados[1].aumentaSalario(5);
Neste caso, para cada posição do vetor vetEmpregados, teremos o comportamento
associado ao tipo real de cada objeto armazenado em cada posição do vetor (runtime
type). Esta é uma outra importante característica do polimorfismo e é conhecida como
“virtual method invocation”.
Em C++ você apenas obtém este comportamento se definir o método como virtual. Em
linguagens “puras” orientadas a objetos, no entanto, isto não é normal. C++ faz isso,
naturalmente, para aumentar o desempenho.
Você irá às vezes ouvir que em linguagens orientadas a objetos são passadas mensagens a
objetos solicitando que sejam executadas determinadas ações. Uma forma interessante de
pensar sobre chamadas a métodos virtuais é imaginar que o método é uma instrução
passada para o objeto. Logo, em vez de dizer à CPU para executar um trecho de código,
você diz a um objeto “faça xxx para mim”. Este modelo faz com que seja fácil entender
que o comportamento será sempre o comportamento adequado associado a um objeto
real, de um determinado tipo.
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 99
Como Impedir a Herança
Java permite que a palavra-chave final seja aplicada a classes. Se isto for feito, a classe
não poderá ser estendida. A classe java.lang.String, por exemplo é uma classe final. Isto
foi feito por questões de segurança, uma vez que isto garante que se um método possui
uma referência a um String, se trata realmente de um String e não alguma subclasse que
pode ter sido acrescentada maliciosamente visando modificar seu comportamento.
Exemplo:
final class Vendedor
{ . . .
}
Também é possível fazer com que um método não possa ser sobreposto por outro de uma
subclasse. Basta defini-lo como final.
Todos os métodos em uma classe final são automaticamente final.
Métodos marcados como static ou private são automaticamente final uma vez
que em ambos os casos o “dynamic binding” não pode ser utilizado.
Razões para tornar um método final:
1. Eficiência. O “dynamic binding” leva mais tempo do que o “static binding”. Se um
método é final o compilador faz o seguinte:
Exemplo:
Se e.getNome() é final o compilador pode substituir este código por e.nome.
Assim você tem o benefício do acesso direto às variáveis de instância sem quebrar o
encapsulamento.
2. Segurança. A flexibilidade do mecanismo de “dynamic dispatch” significa que você
não possui controle sobre o que acontece quando você chama um método. Quando
você envia uma mensagem como e.getNome(), é possível que e seja um objeto de
uma classe derivada que redefiniu o método getNome() para retornar um string
completamente diferente, por exemplo. Tornando o método final, o chamador de
um método sabe que o comportamento que irá ocorrer é o esperado (o original) e não
um outro qualquer.
Toda classe que não faz sentido derivar deve ser definida como final, por questão de
eficiência.
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 100
Instanceof
Se um método recebe um objeto do tipo Empregado, este empregado pode ser um simples
empregado, um Gerente ou um Vendedor. Se você desejar, é possível verificar o tipo de
um objeto através de instanceof:
public void metodo(Empregado e)
{ if (e instanceof Gerente)
{ // Processamento de gerente
}
else if (e instanceof Vendedor)
{ // Processamento de Vendedor
}
else
{ // Processamento de empregado comum
}
}
Casting
É o processo de conversão de um tipo básico em outro.
Exemplo:
double x = 3.45;
int nx = (int) x;
Em situações em que um método recebe uma referência a uma classe pai, e você
determinou (através de instanceof) que o objeto é de uma subclasse particular é possível
restaurar toda a funcionalidade do objeto efetuando um cast na referência.
Por exemplo:
public void metodo(Empregado e)
{ if (e instanceof Gerente)
{ System.out.println ("O nome da secretária é: " +
((Gerente)e).getNomeDaSecretaria();
}
// O resto do código
}
Sem efetuar o teste com instanceof corre-se o risco do cast falhar.
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 101
Regras de conversão de classes:
• cast para cima na hierarquia das classe é sempre legal. Neste caso não é necessário
utilizar o operador cast, bastando uma simples designação.
• Para efetuar um cast para baixo, é preciso que a classe destino da conversão seja uma
subclasse da classe de origem. Esta verificação é efetuada em tempo de compilação.
• Se o compilador permitiu o cast, então o tipo da referência é verificado em tempo de
execução. Se o operador instanceof não foi utilizado e o objeto que está sofrendo o
cast não possui de fato o tipo especificado, será gerada uma exceção.
Gerente g = (Gerente) e;
No exemplo abaixo é preciso converter um objeto empregado em gerente para que se
possa acessar qualquer dos seus campos (e métodos).
Como você sabe, em Java toda variável objeto possui um tipo. O tipo descreve o tipo de
objeto a que a variável se refere e o que pode ser feito com ela. No exemplo abaixo,
vetEmpregados[i] se refere a um objeto Empregado (logo, também pode referenciar um
objeto Gerente).
Empregado[] vetEmpregados = new Empregado[3];
Se você designar um objeto de uma subclasse a uma variável de uma superclasse, como
você está prometendo fazer com ele menos do que é permitido, o compilador lhe permite
fazer isso. Por outro lado, se você designar um objeto de uma superclasse a uma variável
de uma subclasse, como você está dizendo que pretende fazer com ele mais do que a
superclasse permite, será preciso confirmar esta conversão com a utilização do cast
(Subclasse).
E o que acontece se ao executar o comando abaixo você estiver mentindo sobre o
conteúdo do objeto?
Gerente mandaChuva = (Gerente) vetEmpregados[1];
Ocorrerá um erro de execução, isto é, será gerada uma exceção. Para evitar este erro você
deveria fazer o seguinte:
if (vetEmpregados[1] instanceof Gerente)
{ mandaChuva = (Gerente) vetEmpregados[1];
. . .
}
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 102
Resumindo:
É possível utilizar o cast apenas dentro de uma hierarquia de herança.
Utilize o instanceof para verificar a hierarquia antes de converter um objeto de uma
superclasse para uma subclasse.
Na verdade, converter o tipo de um objeto através de um cast não é, geralmente, uma boa
prática de programação. No exemplo apresentado não é preciso converter um objeto
Empregado em um objeto Gerente para a maioria dos fins. Por exemplo, o método
aumentaSalario funcionará corretamente em ambos os tipos em função do “dynamic
binding”. A única razão para se realizar o cast é utilizar um método pertencente apenas à
classe Gerente. Por exemplo, getNomeDaSecretaria(). Se for importante obter o nome da
secretaria para um objeto do tipo Empregado, você poderia reprojetar a classe e adicionar
um método getNomeDaSecretaria, que simplesmente retorna um string vazio. Isto faz
mais sentido do que ter que descobrir que posições do array armazenam que tipo de
objeto.
Classe Gerente
Exercício: O que é preciso fazer na classe Gerente abaixo para armazenarmos uma
referência para a secretária e não apenas o seu nome?
import java.util.*;
class Gerente extends Empregado
{ public Gerente(String n, double s, GregorianCalendar d)
{ super(n, s, d);
}
public void aumentaSalario(double percentual)
{ // adiciona 0.5% de bonus para cada ano de serviço
GregorianCalendar hoje = new GregorianCalendar();
double bonus = 0.5 * (hoje.getYear() - anoContratacao());
super.aumentaSalario(percentual + bonus);
}
}
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 103
Esta abordagem reflete melhor a realidade do que simplesmente utilizar o nome da
secretária. O registro de Empregado para a secretária pode agora ser processado sem a
necessidade de fazer uma busca no array vetEmpregados.
Uma vez feito isto, você deve ter em mente que o objeto Gerente agora contém uma
referência para o objeto Empregado, que descreve a secretária.
Classe Empregado
import corejava.*;
class Empregado
{ public Empregado (String n, double s, GregorianCalendar d)
{ nome = n;
salario = s;
dataContratacao = d;
}
public void print()
{ System.out.println (nome + " " + salario + " " +
anoContratacao());
}
public void aumentaSalario (double percentualDeAumento)
{ salario = salario * (1 + percentualDeAumento) / 100;
}
public int anoContratacao()
{ return dataContratacao.getYear();
}
public String getDataContratacao()
{ int dia = dataContratacao.get(Calendar.DAY_OF_MONTH);
int mes = dataContratacao.get(Calendar.MONTH);
int ano = dataContratacao.get(Calendar.YEAR);
return (dia + "/" + (mes + 1) + "/" + ano);
}
public String getNome()
{ return nome;
}
public double getSalario()
{ return salario;
}
private String nome;
private double salario;
private GregorianCalendar dataContratacao;
}
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 104
Exemplo de Utilização das Classes Empregado e Gerente:
Exercício: Escreva o código necessário para obter e imprimir o nome de cada empregado
seguido do nome da secretária, quando se tratar de um gerente. Utilize instanceof.
import java.util.*;
public class TestaEmpregadoGerente
{ public static void main (String[] args)
{ Gerente mandaChuva =
new Gerente ("Ricardo Silva", 7500,
new GregorianCalendar(1987, 12, 15));
// Cadastramos 3 empregados:
Empregado[] vetEmpregados = new Empregado[3];
vetEmpregados [0] = mandaChuva;
vetEmpregados [1] =
new Empregado ("Claudia Lins", 2000,
new GregorianCalendar (1992, 12, 6));
vetEmpregados [2] =
new Empregado ("Luciana Arruda", 2500,
new GregorianCalendar (1993, 1, 12));
mandaChuva.setSecretaria(vetEmpregados[1]);
System.out.println ("Relação de Empregados e suas Secretárias");
}
}
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 105
Classes do tipo “Abstract”
A medida que você sobe na hierarquia de herança de uma família de classes, as classes se
tornam mais gerais e provavelmente mais abstratas. Em algum ponto uma classe ancestral
se torna tão geral que passa a fazer parte apenas da hierarquia para outras classes. Estas
classes (muito gerais) não possuem instâncias específicas que você desejaria utilizar.
Considere, por exemplo, um sistema de mensagens eletrônicas que integre e-mails,
mensagens por fax e mensagens por voz. Naturalmente uma caixa de correio necessita
armazenar uma mistura destes três tipos de mensagens, logo as mensagens deverão ser
acessadas através de referências à classe pai comum denominada Mensagem.
Mensagem
MensagemTexto
MensagemVoz
Definir a classe Mensagem faz com que o projeto das suas classes mais limpo. Neste
caso, todas as mensagens possuem um método comum denominado mostrar(). É fácil
imaginar como mostrar uma mensagem de voz – basta enviá-la para o auto falante. Já
uma mensagem no formato texto pode ser mostrada exibindo-a em uma janela de texto.
Mas como implementar o método mostrar() na classe Mensagem? A resposta é que não é
possível. Em Java utiliza-se a palavra-chave abstract para indicar que um método não
pode ser especificado nesta classe. Uma classe com um ou mais métodos definidos como
abstract devem ser declaradas como abstract.
public abstract class Mensagem
{ . . .
public abstract void mostrar();
}
Além de métodos do tipo abstract, uma classe abstrata pode possuir dados e métodos
concretos. Por exemplo, a classe Mensagem pode armazenar o remetente da mensagem e
pode possuir um método concreto que retorne o nome deste remetente.
abstract class Mensagem
{ public Mensagem (String de)
{ remetente = de;
}
public abstract void mostrar();
public String getRemetente()
{ return remetente;
}
private String remetente;
}
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 106
Um método do tipo abstract promete que todos os descendentes desta classe abstrata irão
implementar este método. Uma classe pode ser declarada como abstrata embora não
possua métodos abstratos. Uma classe abstrata não pode ser instanciada, isto é, não
podem ser criados objetos desta classe. Será preciso estender (extend) a classe para ser
possível criar uma instância da classe. Note que é possível criar variáveis de instância na
classe abstrata, mas estas variáveis devem referenciar objetos de uma subclasse não
abstrata.
Exemplo:
Mensagem msg = new MensagemTexto (“Bom dia”);
Neste exemplo msg é uma variável da classe abstrata Mensagem que se refere a uma
instância da subclasse não abstrata MensagemTexto.
Para ver um exemplo concreto de utilização da classe abstrata Mensagem e do método
mostrar, vejamos o código nas próximas 3 páginas.
Note que só foi preciso fornecer uma definição concreta do método mostrar nas classes
MensagemTexto e MensagemVoz. Ao executar o programa abaixo deverão ser
fornecidas mensagens escritas e mensagens de voz. Para fornecer uma mensagem de voz
é preciso digitar o nome de um arquivo no formato .au. No diretório da classe
TesteDeCorreio há dois arquivos neste formato que podem ser utilizados para executar o
programa: comehome.au e meeting.au.
No momento não tente entender o código para tocar um arquivo de som e para tratar
exceções. Estes assuntos serão examinados mais adiante.
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 107
import java.net.*;
import java.applet.*;
import corejava.*;
public class TesteDeCorreio
{ public static void main(String[] args)
{ CaixaDeCorreio umaCaixa = new CaixaDeCorreio();
while (true)
{ System.out.println(umaCaixa.status());
String cmd = Console.readLine ("mostrar, texto, voz, fim> ");
if (cmd.equals("mostrar"))
{ Mensagem m = umaCaixa.remove();
if (m != null)
{ System.out.println("De: " + m.getRemetente());
m.mostrar();
}
}
else if (cmd.equals("texto"))
{ String de = Console.readLine("Seu nome: ");
boolean mais = true;
String msg = "";
System.out.println ("Digite uma mensagem e 'fim' " +
"para encerrar.");
while (mais)
{ String linha = Console.readLine();
if (linha.equals("fim"))
mais = false;
else msg = msg + linha + "\n";
}
umaCaixa.insert(new MensagemTexto(de, msg));
}
else if (cmd.equals("voz"))
{ String de = Console.readLine("Seu nome: ");
String msg
= Console.readLine("Nome do arquivo de Som: ");
umaCaixa.insert(new MensagemVoz(de, msg));
}
else if (cmd.equals("fim"))
System.exit(0);
}
}
}
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 108
abstract class Mensagem
{ public Mensagem(String de)
{ remetente = de;
}
public abstract void mostrar();
public String getRemetente()
{ return remetente;
}
private String remetente;
}
class MensagemTexto extends Mensagem
{ public MensagemTexto(String de, String t)
{ super(de);
texto = t;
}
public void mostrar()
{ System.out.println(texto);
}
private String texto;
}
class MensagemVoz extends Mensagem
{ public MensagemVoz(String de, String f)
{ super(de);
nomeDoArquivo = f;
}
public void mostrar()
{ try
{ URL u = new URL("file", "localhost", nomeDoArquivo);
AudioClip clip = Applet.newAudioClip(u);
clip.play();
}
catch(Exception e)
{ System.out.println("Nao pode abrir " + nomeDoArquivo);
}
}
private String nomeDoArquivo;
}
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 109
class CaixaDeCorreio
{ public Mensagem remove()
{ if (nmsg == 0) return null;
Mensagem r = mensagens[saida]; // Saida indica qual a
// mensagem
nmsg--; // que vai sair. Simula uma lista
saida = (saida + 1) % MAXMSG; // circular.
return r;
}
public void insert(Mensagem m)
{ if (nmsg == MAXMSG) return;
mensagens[entrada] = m; // Entrada indica a posicao onde
nmsg++; // a mensagem vai entrar. Simula
entrada = (entrada + 1) % MAXMSG; // uma lista circular.
}
public String status()
{ if (nmsg == 0) return "Caixa de Correio vazia";
else if (nmsg == 1) return "1 message";
else if (nmsg < MAXMSG) return nmsg + " mensagens";
else return "Caixa de Correio cheia";
}
private final int MAXMSG = 10;
private int entrada = 0;
private int saida = 0;
private int nmsg = 0;
private Mensagem[] mensagens = new Mensagem[MAXMSG];
}
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 110
Exemplo de Método Abstract
Exercício: Defina uma classe vendedor com as seguintes características:
1. A classe Vendedor deve estender Empregado.
2. Deve possuir duas variáveis de instância: percentualDeComissao e vendasNoMes,
ambas do tipo double.
3. Quando um vendedor é cadastrado estas informações devem ser fornecidas.
4. Deve possuir um método aumentaSalario (double
percentualAumento). Sempre que um vendedor recebe um aumento, o
percentual de aumento não incide sobre o salário e sim sobre o percentual de
comissão.
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 111
import java.util.*;
class Gerente extends Empregado
{ public Gerente (String n, double s, GregorianCalendar d)
{ super(n, s, d);
nomeDaSecretaria = " ";
}
public void aumentaSalario (double percentualDeAumento)
{ // adiciona 0,5 % de bonus para cada ano de serviço
GregorianCalendar hoje = new GregorianCalendar();
double bonus = 0.5 * (hoje.get(Calendar.YEAR) –
anoContratacao());
double novoSalario = super.getSalario() * (1 +
(percentualDeAumento + bonus)/100);
super.setSalario(novoSalario);
}
public String getNomeDaSecretaria()
{ return nomeDaSecretaria;
}
public void setNomeDaSecretaria(String nome)
{ nomeDaSecretaria = nome;
}
private String nomeDaSecretaria;
}
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 112
import java.util.*;
abstract class Empregado
{ public Empregado (String n, double s, GregorianCalendar d)
{ nome = n;
salario = s;
dataContratacao = d;
}
public void print()
{ System.out.println (nome + " " + salario + " " +
anoContratacao());
}
public abstract void aumentaSalario (double percentualDeAumento);
public void alteraDataContratacao (int ano, int mes, int dia)
{ dataContratacao.set(Calendar.YEAR, ano);
dataContratacao.set(Calendar.MONTH, mes);
dataContratacao.set(Calendar.DATE, dia);
}
public int anoContratacao()
{ return dataContratacao.get(Calendar.YEAR);
}
public String getNome()
{ return nome;
}
public double getSalario()
{ return salario;
}
public void setSalario(double sal)
{ salario = sal;
}
private String nome;
private double salario;
private GregorianCalendar dataContratacao;
}
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 113
Utilize o programa abaixo para testar o que você programou.
import java.util.*;
import corejava.*;
public class TestaEmpregadoGerente
{ public static void main (String[] args)
{ Gerente mandaChuva =
new Gerente ("Ricardo Silva", 7500,
new GregorianCalendar(1987, 12, 15));
mandaChuva.setNomeDaSecretaria ("Claudia Lins");
// Cadastramos 3 empregados:
Empregado[] vetEmpregados = new Empregado[2];
vetEmpregados [0] = mandaChuva;
vetEmpregados [1] =
new Vendedor ("Luciana Arruda", 2500,
new GregorianCalendar (1993, 1, 12), 5, 10000);
int i;
System.out.println ("Empregados e Salarios antes do aumento");
for (i = 0; i < 2; i++) vetEmpregados[i].print();
System.out.println ("");
for (i = 0; i < 2; i++) vetEmpregados[i].aumentaSalario(5);
System.out.println ("Empregados e salarios apos o aumento");
for (i = 0; i < 2; i++)
{ System.out.println ("Nome = " + vetEmpregados[i].getNome());
System.out.println ("Salario = " + vetEmpregados[i].getSalario());
}
System.out.println ("");
System.out.println ("A secretaria do departamento se chama " +
mandaChuva.getNomeDaSecretaria());
}
}
Observe que uma classe abstrata pode conter métodos e variáveis não abstratas.
Você não pode construir uma instância de uma classe abstract, exceto indiretamente
construindo uma instância de alguma subclasse dela.
Subclasses de classes abstratas devem prover implementações para todos os métodos
abstratos nos seus pais, caso contrário também serão classes abstratas.
Você pode declarar reference variables do tipo da classe abstact.
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 114
Métodos do Tipo Protected
Como você já sabe, os campos de instância de uma classe devem ser declarados como
private e os métodos são geralmente declarados como public. Qualquer coisa declarada
como private não será visível por outras classes e isto também vale para subclasses:
uma subclasse não pode acessar os dados privados de sua superclasse.
Há, no entanto, situações em que você deseja que uma subclasse tenha acesso a um
método ou a um dado da sua classe pai. Neste caso, devemos declarar o método ou a
variável como protected. Por exemplo, se o objeto dataContratacao da classe Empregado
tivesse sido declarado como protected em vez de private, então os métodos da classe
Gerente poderiam acessar este campo diretamente.
Na prática, evite utilizar atributos protected. Suponha que você projetou uma classe com
campos protected e que esta classe esteja sendo utilizada por outros programadores. Este
programadores poderão derivar classes a partir da sua classe e, então, poderão acessar
diretamente os campos definidos como protected. Você não poderá mudar a
implementação da sua classe sem incomodar os outros programadores. Isto é contra o
espírito da OOP, que estimula o encapsulamento.
Métodos do tipo protected fazem mais sentido. Uma classe pode declarar uma
método como protected se ele é difícil de usar (cheio de truques). Protected indica
que a subclasse (que presumivelmente conhece bem os seus ancestrais) pode utilizar o
método, mas outras classes, não. Mais adiante veremos um exemplo deste assunto.
Abaixo segue um resumo dos 4 modificadores de acesso que controlam a visibilidade:
1. Visível apenas na classe: private.
2. Visível por todas as classes: public.
3. Visível no package e por todas as subclasses: protected.
4. Visível no package: o default – quando não se especifica nada.
Object: A Superclasse Cósmica.
A classe Object é o último ancestral de qualquer classe. Toda classe em java estende a
classe Object, no entanto não é preciso escrever:
class Empregado extends Object;
Como toda classe em Java estende a classe Object, é importante conhecer os serviços
fornecidos pela classe Object. Aqui serão apresentados os serviços básicos.
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 115
É possível utilizar uma variável do tipo Object para fazer referência a objetos de qualquer
tipo:
Object obj = new Empregado (“Vinicius Ribeiro”, 3500);
Naturalmente uma variável do tipo Object somente é útil como um armazenador genérico
para valores arbitrários. Para fazer qualquer coisa específica com um objeto do tipo
Empregado é preciso ter conhecimento sobre o tipo original do objeto:
Empregado e = (Empregado) obj;
O método equals da classe Object testa se um objeto é igual a outro. Na verdade este
método testa apenas se dois objetos apontam para a mesma área de memória, o que não é
um teste muito útil. Se você deseja testar se dois objetos são iguais é preciso redefinir
equals:
class Empregado
{ . . .
public boolean equals (Object obj)
{ if (!(obj instanceof Empregado))
return false;
Empregado b = (Empregado) obj;
return nome.equals (b.nome) &&
salario == b.salario &&
dataContratacao.equals (b.dataContratacao);
}
}
Abaixo vem o código do programa que testa se um objeto qualquer é igual a um objeto
Empregado específico:
import java.util.*;
public class TestaEmpregado
{ public static void main (String[] args)
{ Projeto umProjeto = new Projeto("Bacuri");
Empregado umEmpregado = new Empregado ("Vinicius Aguiar", 4500,
new GregorianCalendar (1992, 6, 13));
Object umObjeto = umEmpregado;
if (umEmpregado.equals(umObjeto))
System.out.println("É igual");
else
System.out.println("Não é igual");
umObjeto = umProjeto;
if (umEmpregado.equals(umObjeto))
System.out.println("É igual");
else
System.out.println("Não é igual");
}
}
Um outro importante método da classe Object é o método toString que retorna um string
que representa o valor deste objeto. Quase todas as classes fazem um override deste
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 116
método a fim de lhe dar uma representação impressa do estado corrente do objeto.
Sempre que um objeto é concatenado a um string, através do operador “ +”, o compilador
Java chama automaticamente o método toString para obter o estado atual do objeto.
GregorianCalendar d = new GregorianCalendar (1999, 12, 31);
String ultimoDia = “O ultimo dia do milenio e “ + d;
// Automaticamente o método toString é chamado.
Todos os valores de qualquer tipo de classe podem ser armazenados em variáveis do tipo
Object. Por exemplo, valores da classe String são objetos, logo:
Object obj = “Oi”; // OK
No entanto, números, caracteres e valores boleanos não são objetos, logo:
obj = 5; // Erro!
obj = false; // Erro!
Todos os tipos de arrays são classes que derivam da classe Object.
Empregado[] empregados = new Empregado[10];
Object arr = empregados; // Um array de empregados é um
// objeto logo pode ser armazenado em uma variável do tipo
// Object. Não é preciso escrever:
// Object [] arr = empregados; Escrevendo desta forma
// dará erro na linha abaixo pois um array de inteiros não é
// um array de objetos.
arr = new int[10]; // Um array de inteiros é um objeto,
// logo pode ser armazenado em uma
// variável do tipo Object.
Um array de objetos pode ser convertido para um array de objects. Por exemplo, um array
do tipo Empregado[] pode ser passado para uma função que espera receber um array
do tipo Object[]. Esta conversão é útil quando se está programando de forma
genérica.
Exemplo:
static int encontrar (Object[] a, Object b)
{ int i;
for (i = 0; i < a.length; i++)
if (a[i].equals(b)) return i;
return –1; // Caso não encontre.
}
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 117
Código que chama a função:
Empregado[] empregados = new Empregado[10];
Empregado Silvia;
. . .
int n = encontrar (empregados, silvia);
// Observe que encontrar é um método estático, logo:
// 1. Pertence à classe que o está chamando ou
// 2. Pertence a uma classe que contém apenas métodos
// gerais e estáticos.
//
// Veja os exemplos 1 e 2 abaixo.
Exemplo 1: (Método encontrar definido na própria classe)
import java.util.*;
public class TestaEmpregado
{ public static void main (String[] args)
{ // Projeto umProjeto = new Projeto("Bacuri");
Empregado [] empregados = new Empregado[2];
Empregado vinicius = new Empregado ("Vinicius Aguiar", 4500,
new GregorianCalendar (1992, 6, 13));
Empregado carlos = new Empregado ("Carlos Ribeiro", 5500,
new GregorianCalendar (1993, 7, 14));
empregados[0] = carlos;
empregados[1] = vinicius;
int n = encontrar(empregados, carlos);
if (n==-1)
System.out.println("Não achou");
else
System.out.println("Achou na posição: " + n);
}
static int encontrar (Object[] a, Object b)
{ int i;
for (i = 0; i < a.length; i++)
if (a[i].equals(b)) return i;
return -1; // Caso não encontre.
}
}
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 118
Exemplo 2: (Método encontar definido em outra classe)
import java.util.*;
public class TestaEmpregado
{ public static void main (String[] args)
{ // Projeto umProjeto = new Projeto("Bacuri");
Empregado [] empregados = new Empregado[2];
Empregado vinicius = new Empregado ("Vinicius Aguiar", 4500,
new GregorianCalendar (1990, 5, 26));
Empregado carlos = new Empregado ("Carlos Ribeiro", 5500,
new GregorianCalendar (1993, 7, 14));
empregados[0] = carlos;
empregados[1] = vinicius;
int n = FuncoesGerais.encontrar(empregados, carlos);
if (n==-1)
System.out.println("Não achou");
else
System.out.println("Achou na posição: " + n);
}
}
public class FuncoesGerais
{ static int encontrar (Object[] a, Object b)
{ int i;
for (i = 0; i < a.length; i++)
if (a[i].equals(b)) return i;
return -1; // Caso não encontre.
}
}
Note que você só pode converter um array de objetos em um array do tipo Objects. Não é
possível converter um array de inteiros (int[]) em um array do tipo Object[], uma
vez que int[] é um objeto mas não é um array de objetos. No entanto, como já vimos,
tanto o array de inteiros quanto um array de objetos pode ser armazenado em uma
variável do tipo Object.
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 119
Se você converter um array de objetos em um array do tipo Objects[], o array genérico
ainda se lembra, em tempo de execução, do seu tipo original.
import java.util.*;
public class TestaEmpregado
{ public static void main (String[] args)
{ Empregado[] empregados = new Empregado[10];
Object[] arr = empregados;
Projeto proj = new Projeto("Bacuri");
Empregado aaa =
new Empregado ("Vinicius Aguiar", 4500,
new GregorianCalendar (1992, 6, 13));
arr[0] = aaa;
arr[1] = proj;
}
}
O comando arr[1] = proj provoca um erro pois arr aponta para um vetor de
Empregados, e proj é do tipo Projeto.
É possível armazenar objetos de classes diferentes em um vetor de Objects. No exemplo
abaixo, por exemplo, para recuperar a informação e mandar executar um método é
preciso efetuar o cast uma vez que a classe Object não implementa o método
aumentaSalario, nem mesmo como abstract.
import java.util.*;
import java.applet.*;
public class TestaEmpregado
{ public static void main (String[] args)
{ Empregado emp = new Empregado ("Luis Claudio", 3500,
new GregorianCalendar (1989, 10, 1));
Projeto proj = new Projeto("Bacuri");
Object[] arr = new Object[10];
arr[0] = emp;
arr[1] = proj;
// O comando abaixo retorna uma exceção quando
// acessa empregados[1] que não aponta para um
// empregado.
for (int i = 0; i < 2; i++)
if (arr[i] instanceof Empregado)
((Empregado)arr[i]).aumentaSalario(3);
};
}
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 120
java.lang.object
• Class getClass()
Retorna um objeto do tipo Class que contém informações sobre o objeto.
• Boolean equals(Object obj)
Compara dois objetos. Verifica se os dois objetos apontam apara a mesma região de
memória.
• Object clone()
Cria um clone de um objeto. O run-time aloca memória para a nova instância do
objeto e copia a memória alocada para o objeto corrente.
• String toString
Retorna um string que representa a descrição do objeto.
Vectors
Em Java é possível definir o tamanho de um array em tempo de execução.
int n;
. . .
Item[] itensPedidos = new Item[n + 1];
Este código não resolve completamente o problema. Com ele pode-se definir o tamanho
de um array em tempo de execução, mas uma vez definido este tamanho, não se pode
modificá-lo facilmente.
Há em Java um objeto que trabalha de forma similar a um array e que pode encolher e
crescer ao longo da execução de um programa. Este objeto se chama Vector.
Há uma importante diferença entre um Vector e um array. Arrays são implementados
na própria linguagem Java. Já o Vector é uma classe definida no package java.util.
Este tipo Vector armazena elementos do tipo Object, logo será preciso utilizar o
cast quando você desejar extrair um item de um Vector.
Para construir um novo Vector é preciso especificar, no construtor do Vector, a sua
dimensão inicial.
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 121
Exemplo:
Vector itensPedidos = new Vector(3);
Existe uma importante distinção entre a capacidade de um Vector e o tamanho de um
array. Se você alocar um array com 3 entradas, então o array possuirá 3 entradas e estará
pronto para uso. Já um Vector com capacidade para armazenar 3 elementos possui o
potencial para armazenar 3 elementos (e até mais do que 3), mas a princípio, mesmo após
a sua construção inicial, um Vector não armazena nenhum elemento.
Utilize o método add para adicionar novos elementos a um Vector. Por exemplo,
suponha que você possui uma classe denominada Item e utiliza o seguinte código para
criar 3 objetos do tipo Item:
Item item1 = new Item( . . . );
Item item2 = new Item( . . . );
Item item3 = new Item( . . . );
Então, utiliza-se o seguinte código para adicionar estes itens ao Vector itensPedidos:
itensPedidos.add(item1);
itensPedidos.add(item2);
itensPedidos.add(item3);
Se você inserir um quarto item neste Vector, será encontrado um local maior para
armazená-lo e as entradas existentes no Vector são automaticamente copiadas para este
novo local. Por default, o novo espaço alocado para o Vector dobra o espaço
atualmente ocupado. Em função do problema do crescimento exponencial do espaço
alocado, é possível especificar, no construtor, de quanto será o crescimento do Vector a
cada nova realocação.
Exemplo:
Vector itensPedidos = new Vector (3, 10);
Agora o vector cresce em incrementos de 10 a cada nova realocação. Por outro lado, se
Java precisar realocar o espaço ocupado por um Vector com freqüência o desempenho
do programa cairá muito, logo tome cuidado quando definir a alocação inicial de um
Vector.
Para pequenos programas pode-se utilizar o construtor default que aloca inicialmente 10
posições para o Vector e dobra o espaço ocupado a cada nova realocação:
Vector itensPedidos = new Vector ();
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 122
Trabalhando com um Vector Existente
O método size() retorna o número corrente de elementos em um Vector. Logo,
v.size() equivale ao a.length para arrays.
Quando você estiver certo que o Vector se encontra no seu tamanho permanente, você
poderá chamar o método trimToSize(). Este método ajusta o tamanho do bloco de
memória para utilizar exatamente a quantidade de memória necessária para armazenar o
número corrente de elementos no Vector. O coletor de lixo irá recuperar a memória
excedente.
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 123
Acessando um Elemento de um Vector
As duas maiores diferenças entre Vectors e arrays são:
1. Em vez de utilizar [] para referenciar os elementos deve-se utilizar os métodos get e
set.
Arrays Vectors
x = a[i]; x = v.elementAt(i); ou x = v.get(i);
a[i] = x; v.setElementAt(x, i); ou v.set(i, x);
Os métodos get e set foram adicionados na versão 1.2 do JDK.
Para obter o melhor de ambos os recursos pode-se fazer o seguinte:
Vector v = new Vector();
while ( . . . )
{ String s = . . .
v.add (s);
}
E quando o tamanho do Vector não for mais mudar efetua-se uma cópia dos elementos
do Vector para um array com o método copyInto desenvolvido para este fim:
String[] a = new String[v.size()];
v.copyInto(a);
Atenção, não chame o método v.setElementAt (x, i) enquanto o tamanho (size) do
Vector não for maior do que i.
Exemplo:
Vector v = new Vector(10); // capacidade 10, tamanho zero.
v.set (0, x); // Isto provoca um erro. A posição
// zero ainda não existe.
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 124
Existem duas soluções para este problema:
a) Utilize o método addElement em vez de setElementAt.
Vector v = new Vector (10);
v.add (0, x); // Adiciona um elemento ao vector e faz
// v.size() ser igual a 1.
b) Chame o método setSize após criar o vector.
Vector v = new Vector(10); // capacidade 10, tamanho zero.
v.setSize (10); // capacidade 10, tamanho 10.
v.set (0, x); // Isto não provoca um erro.
2. Existe uma única classe Vector e esta classe armazena elementos de qualquer tipo:
um Vector armazena uma seqüência de objetcs.
Isto não é um problema no momento da inserção de elementos em um Vector – afinal
todas as classes são derivadas da classe Object. Haverá um problema no entanto, se você
desejar construir um Vector de números. Neste caso será preciso utilizar uma classe
que funcione como um invólucro tais como as classes Integer e Double – veja a próxima
sessão. Mas como os itens são armazenados como objects será preciso utilizar o cast para
recuperá-los do Vector.
Exemplo:
Vector itensPedidos = new Vector(10);
itensPedidos.setSize (10);
Item umItem = new Item();
itensPedidos.set (posicao, umItem);
A variável umItem é automaticamente convertida do tipo Item para o tipo Object quando
inserida no Vector. Mas quando você lê umItem de um Vector, você obtém um
Object e então é preciso converter de volta para o tipo original. Sem esta conversão não é
possível trabalhar com o objeto, isto é, sem ela o compilador gera uma mensagem de
erro.
Item itemCorrente = (Item) itensPedidos.get(posicao);
Vector são inerentemente pouco seguros. É possível acidentalmente adicionar um
elemento de um tipo errado a um Vector.
Retangulo r = new Retangulo ();
itensPedidos.set(n, r); // Erro!
A atribuição do objeto r ao Vector não é o problema. A questão é que, posteriormente, ao
se recuperar este objeto armazenado no Vector, provavelmente será feita a sua
conversão (cast) para o tipo Item. Este é um problema dos Vectors pois eles armazenam
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 125
valores do tipo Objects. Se itensPedidos fosse um array de valores do tipo Item, então o
compilador não aceitaria a atribuição de um objeto do tipo Retângulo para o array.
Item[] itensPedidos = new Item[3];
Retangulo r = new Retangulo();
itensPedidos[1] = r; // Erro!
Na prática, no entanto, é geralmente possível garantir que os elementos inseridos em um
Vector possuem o tipo desejado. Veja o código abaixo:
class Pedido
{ . . .
public void adicionarItem (Item i)
{ itensPedidos.add(i);
}
. . .
private Vector itensPedidos;
}
O Vector itensPedidos é um campo private da classe Pedido e o único método que
adiciona objetos neste Vector é Pedido.adicionarItem. Como o argumento de
Pedido.adicionarItem é um objeto do tipo Item, somente objetos deste tipo
podem ser adicionados ao Vector. Logo, em tempo de compilação o compilador tem
como verificar o tipo do objeto que se pretende acrescentar ao Vector.
Pedido.adicionarItem(new Retangulo()); // Resultará em um erro!
Assim poderemos, com segurança, recuperar objetos do Vector e convertê-los em Item.
Em ocasiões raras Vectors são úteis para armazenar coleções heterogêneas. Objetos de
classes completamente diferentes podem ser inseridos em um Vector. Neste caso,
quando uma entrada no Vector é recuperada é preciso testar o tipo do objeto
recuperado, conforme vem no código abaixo:
Vector pedidoDeCompra;
pedidoDeCompra.add(new Nome( . . . ));
pedidoDeCompra.add(new Endereco( . . . ));
pedidoDeCompra.add(new Item( . . . ));
. . .
Object obj = pedidoDeCompra.get(n);
if (obj instanceof Item)
{ Item i = (Item) obj;
sum += i.preco();
}
Na verdade, esta não é uma boa forma de se escrever código. Não se deve jogar fora o
tipo de uma dado para depois tentar recuperá-lo programaticamente.
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 126
Inserindo e Removendo Elementos de um Vector
É possível inserir elementos no meio de um Vector:
int n = itensPedidos.size() – 2;
itensPedidos.add(n, proximoItem);
Os elementos localizados nas posições >= n são movidos para cima de forma a abrir
espaço para o novo elemento.
Da mesma forma, é possível remover um elemento localizado no meio de um Vector.
Item i = (Item) itensPedidos.remove(n);
Os elementos localizados acima desta posição são copiados para baixo e o tamanho do
vector é reduzido em um.
A inserção e a remoção de elementos em um Vector não é muito eficiente, logo, se
você necessita armazenar muitos elementos e, com freqüência, precisa acrescentar e
remover elementos no meio do Vector, considere a utilização de uma lista encadeada.
Não faz parte do escopo desta apostila tratar listas encadeadas.
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 127
Desempenho: Vectors x Arrays
A alocação de um Vector bem como o acesso a um elemento de um Vector é
significativamente mais lento do que a alocação e o acesso a um elemento de um array.
Há duas razões para isto:
1. Leva mais tempo chamar um método do que acessar um array diretamente.
2. Alem disso, os métodos de Vectors são sincronizados. Quando duas threads
acessam o mesmo Vector ao mesmo tempo, as chamadas aos métodos são
enfileiradas para que apenas uma thread mexa no Vector de cada vez. Esta
sincronização das threads provoca uma queda no desempenho.
No entanto, a utilização de Vectors é bastante conveniente: crescem dinamicamente,
fornecem a distinção entre tamanho e capacidade e são úteis em programas que trabalham
com várias threads. Recomenda-se a utilização de Vectors quando se pretende
trabalhar com pequenos conjuntos de dados de tamanho variável.
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 128
Exemplo de um programa para controlar os empregados lotados em determinado
departamento utilizando Vector.
Programa Principal:
import java.util.*;
public class TestaEmpregado
{ public static void main (String[] args)
{ Departamento umDepartamento = new Departamento ("Compras");
Empregado vinicius = new Empregado ("Vinicius Aguiar", 4500,
new GregorianCalendar (1992, 6, 13));
umDepartamento.adicionarEmpregado(vinicius);
Empregado carlos = new Empregado ("Carlos Ribeiro", 5500,
new GregorianCalendar (1993, 7, 14));
umDepartamento.adicionarEmpregado(carlos);
Empregado luiz = new Empregado ("Luiz Carlos", 6500,
new GregorianCalendar (1995, 11, 12));
umDepartamento.adicionarEmpregado(luiz);
System.out.println ("Lista de Empregados");
umDepartamento.listarEmpregados();
Empregado empRemovido = umDepartamento.removerEmpregado(carlos);
if (!(empRemovido == null))
System.out.println ("Nome do Empregado removido = " +
empRemovido.getNome());
else
System.out.println ("Empregado Inexistente. Nome = "
+ carlos.getNome());
System.out.println ("Lista de Empregados");
umDepartamento.listarEmpregados();
empRemovido = umDepartamento.removerEmpregado(carlos);
if (!(empRemovido == null))
System.out.println ("Nome do Empregado removido = " +
empRemovido.getNome());
else
System.out.println ("Empregado Inexistente. Nome = "
+ carlos.getNome());
}
}
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 129
Classe Empregado:
import java.util.Calendar;
class Empregado
{ public Empregado (String n, double s, GregorianCalendar d)
{ nome = n;
salario = s;
dataContratacao = d;
}
public boolean equals (Object obj)
{ if (!(obj instanceof Empregado))
return false;
Empregado b = (Empregado) obj;
return nome.equals (b.nome)
&& salario == b.salario
&& dataContratacao.equals (b.dataContratacao);
}
public void print()
{ System.out.println (nome + " " + salario + " " +
anoContratacao());
}
public void aumentaSalario (double percentualDeAumento)
{ salario = salario * (1 + percentualDeAumento) / 100;
}
public void alteraDataContratacao (int ano, int mes, int dia)
{ dataContratacao.set(Calendar.YEAR, ano);
dataContratacao.set(Calendar.MONTH, mes);
dataContratacao.set(Calendar.DATE, dia);
}
public int anoContratacao()
{ return dataContratacao.get(Calendar.YEAR);
}
public String getNome()
{ return nome;
}
private String nome;
private double salario;
private GregorianCalendar dataContratacao;
}
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 130
Classe Departamento:
import java.util.*;
class Departamento
{ public Departamento (String n)
{ nome = n;
}
public void print()
{ System.out.println (nome);
}
public void adicionarEmpregado (Empregado e)
{ empregados.add(e);
}
public Empregado removerEmpregado (Empregado e)
{ int n = encontrar (empregados, e);
if (n==-1)
return null;
else
return (Empregado)empregados.remove(n);
}
public void listarEmpregados ()
{ for (int i = 0; i < empregados.size(); i++)
System.out.println
(((Empregado)empregados.get(i)).getNome());
}
private static int encontrar (Vector v, Empregado b)
{ int i;
for (i = 0; i < v.size(); i++)
if (((Empregado)v.get(i)).equals(b)) return i;
return -1; // Caso não encontre.
}
private String nome;
private Vector empregados = new Vector(10);
}
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 131
Objetos Empacotadores
Ocasionalmente é preciso converter um tipo básico como int para um objeto. Todos os
tipos básicos possuem classes respectivas. Por exemplo, existe uma classe Integer
correspondente ao tipo básico int. Estes tipos de classes são geralmente denominadas
Object Wrappers (Objetos Empacotadores).
Estas classes são: Integer, Long, Float, Double, Byte, Character e
Boolean. As primeiras 5 são especializações da classe pai Number. Todas estas classes
são do tipo final, logo não é possível, por exemplo, fazer o override do método
toString da classe Integer para exibir o número em algarismos Romanos. Também
não é possível modificar os valores que você armazena em um objeto destas classes.
Suponha que desejamos um Vector de números do tipo double. Como dissemos
anteriormente não é possível adicionar um número a um Vector, uma vez que um
número não pertence à classe Object.
Vector v = new Vector();
v.add(3.14); // Erro!
Seria preciso utilizar a classe Double:
v.add(new Double(3.14));
E para recuperar um número armazenado em um Vector de objetos do tipo Double
seria preciso extrair o valor utilizando o método doubleValue() da classe Double.
double x = ((Double) v.get(n)).doubleValue();
Cuidado: wrapper classes não podem ser utilizadas para implementar métodos que
modifiquem argumentos numéricos. Lembre-se que não é possível escrever uma função
que incremente um número inteiro uma vez que os argumentos dos métodos são sempre
passados por valor.
static void incrementa (int x) // Não vai funcionar.
{ x++; // Incrementa uma cópia local.
}
static void main (String[] args)
{ int a = 3;
incrementa (a);
. . .
}
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 132
Modificar o valor de x em incrementa não tem nenhum efeito sobre a variável “a”. E se
utilizarmos a classe Integer em vez de int?
static void incrementa (Integer x) // Não vai funcionar.
{ . . .
}
static void main (String[] args)
{ Integer a = new Integer(3);
incrementa (a);
. . .
}
Aparentemente deveria funcionar uma vez que, agora a e x são referências ao mesmo
objeto. Ao atualizarmos x, a deveria também ser atualizado. O problema é que objetos da
classe Integer são imutáveis: a informação contida em uma classe wrapper não pode ser
modificada. Não existe, por exemplo, um método análogo a x++ para os objetos da classe
Integer. Logo, não é possível utilizar classes do tipo wrapper para criar um método que
modifique argumentos numéricos.
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 133
Convertendo Strings em Números
Freqüentemente você verá as classes do tipo wrappers sendo utilizadas por outra razão:
int x = Integer.parseInt(s);
Isto não tem nada a ver com objetos do tipo Integer – parseInt é um método
estático. Mas os projetistas de Java acharam que a classe Integer era um bom local
para colocar isso.
Até a versão 1.2 não havia o correspondente parseDouble na classe Double. Para
obter o mesmo efeito é preciso fazer o seguinte:
double x = new Double(s).doubleValue();
O que isto faz:
1. Utiliza um construtor da classe Double que aceita um string de dígitos na forma de
um double e lhe retorna um objeto do tipo Double.
2. Utiliza o método doubleValue da classe Double que retorna o valor
correspondente do tipo double.
Na prática, pode ser ainda pior: é possível que o string contenha espaços em branco a
direita ou a esquerda ou pode ainda não conter dígitos, logo, esta conversão deveria ser
feita assim:
x = new Double(s.trim()).doubleValue();
Existe um outro método para converter números. Você pode utilizar o método parse da
classe DecimalFormat. Supondo s um string e df um objeto do tipo
DecimalFormat, então a chamada ao método df.parse(s) retorna um objeto do
tipo Number.
DecimalFormat df = new DecimalFormat(); // Utiliza o
// default locale
Number n = df.parse(s);
Number é uma classe do tipo abstract e o objeto retornado é do tipo Long ou do tipo
Double, dependendo do conteúdo do string s. Você pode utilizar o operador
instanceof para descobrir o tipo do objeto retornado.
if (n instanceof Double)
Double d = (Double) n;
Mas na prática, geralmente não estamos ligando para o tipo do objeto retornado. O
método doubleValue encontra-se definido na classe Number e retorna o um número
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 134
ponto-flutuante equivalente ao objeto number, independente de se tratar de um Long
ou um Double.
Exemplo:
try
{ x = new DecimalFormat().parse(s.trim()).doubleValue();
}
catch(ParseException e)
{ x = 0;
}
Há uma vantagem na utilização de DecimalFormat: o string pode conter separadores
de milhar e de casas decimais tal como: 12,301.40.
A Classe Class (Identificação de Tipo em Tempo de Execução)
Enquanto o seu programa está executando o “Java run-time System” sempre mantém o
“run-time type identification” de todos os objetos. Com estas informações o interpretador
Java consegue saber a que classe cada objeto pertence. Assim, em tempo de execução
esta informação é utilizada para que o interpretador possa selecionar corretamente que
método deve ser executado.
No entanto, é possível acessar esta informação trabalhando com uma classe especial
denominada Class. O método getClass da classe Object retorna uma instância do tipo
Class.
Exemplo:
Empregado e;
. . .
Class c1 = e.getClass();
Provavelmente o método mais comumente utilizado da classe Class é o getName. Este
método retorna o nome da classe. Você pode utilizá-lo, por exemplo, para imprimir o
nome da classe:
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 135
Exemplo:
import java.util.*;
public class ImprimeClasse
{ public static void main (String[] args)
{ Gerente mandaChuva =
new Gerente ("Ricardo Silva", 7500,
new GregorianCalendar(1987, 12, 15));
mandaChuva.setNomeDaSecretaria ("Claudia Lins");
// Cadastramos 3 empregados:
Empregado[] vetEmpregados = new Empregado[3];
vetEmpregados [0] = mandaChuva;
vetEmpregados [1] =
new Empregado ("Claudia Lins", 2000,
new GregorianCalendar (1992, 12, 6));
vetEmpregados [2] =
new Empregado ("Luciana Arruda", 2500,
new GregorianCalendar (1993, 1, 12));
int i;
System.out.println ("Classe a que Cada Empregados Pertence");
System.out.println ("");
for (i = 0; i < 3; i++)
System.out.println (vetEmpregados[i].getClass().getName() +
" " + vetEmpregados[i].getNome());
}
}
Observações:
• vetEmpregados[i].getClass(); // retorna um objeto do tipo Class.
• vetEmpregados[i].getClass().getName(); // retorna o nome da classe.
• vetEmpregados[i].getNome(); // retorna o nome do empregado.
Este código imprimirá:
Classe a que Cada Empregados Pertence
Gerente Ricardo Silva
Empregado Claudia Lins
Empregado Luciana Arruda
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 136
Pode-se obter um objeto do tipo Class de três formas:
• Recuperando-se o objeto tipo Class de um determinado objeto.
vetEmpregados[i].getClass(); // retorna um objeto do tipo Class.
• Fornecendo-se um string para o método forName da classe Class que retorna um
objeto tipo Class relativo ao string fornecido.
try
{ c1 = Class.forName("Gerente");
}
catch(Exception e)
{ System.out.println("Deu erro!");
}
System.out.println (c1.getName() + " " + vetEmpregados[i].getNome());
Esta forma seria utilizada quando o nome da classe é armazenado em um string que
varia em tempo de execução.
• Supondo T um tipo Java, então T.class retorna o objeto Class correspondente.
Class cl1 = Gerente.class;
Class cl2 = int.class;
Class cl3 = Double[].class;
Note que Class realmente descreve um tipo que pode ou não ser uma classe.
Um outro exemplo de método útil é um que nos permite criar uma instância de uma
classe facilmente. Este método denomina-se newInstance().
Exemplo:
e.getClass().newInstance();
O código acima cria uma nova instância da mesma classe de e. O método newInstance
chama o construtor default (aquele que não possui argumentos) para inicializar o objeto
recém criado.
Utilizando uma combinação de forName e de newInstance é possível criar um objeto de
uma classe cujo nome encontra-se armazenado em um string.
String s = “Gerente”;
Object m = Class.forName(s).newInstance();
Se você necessitar fornecer parâmetros para o construtor de uma classe que você deseja
criar a partir do nome da classe, não será possível utilizar os comandos especificados
acima. Será preciso utilizar o método newInstance da classe Constructor. Esta é uma das
várias classes existentes no package java.lang.reflect.
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 137
INTERFACES E INNER CLASSES
Já estudamos todas as ferramentas básicas para desenvolver programas orientados a
objetos em Java. Este capítulo mostra duas técnicas avançadas um pouco mais
complexas:
A primeira, denominada Interface, é a maneira de Java fazer com que uma classe tenha o
comportamento de dois ou mais pais. Isto é geralmente conhecido como herança
múltipla.
E a segunda técnica trata das “Inner Classes” que são mais uma conveniência do que uma
necessidade. Inner classes são importantes para se escrever código conciso e profissional
para manipular eventos provenientes de uma interface gráfica.
Interfaces
Utilizando uma Superclasse do tipo Abstract
Suponha que desejamos escrever um código genérico de ordenação que funcionasse em
diferentes tipos de objetos Java. Começamos com uma classe abstract denominada
Sortable com um método denominado compareTo que determina se um objeto é
menor, maior ou igual a outro.
Abaixo vem um algoritmo de ordenação genérico (ArrayAlg) capaz de ordenar objetos do
tipo Sortable. Não se preocupe com o funcionamento interno deste algoritmo. Apenas
observe que o algoritmo trabalha com array de elementos, os compara e os rearruma.
Este algoritmo poderá ser utilizado com todas as subclasses da classe abstrata
Sortable, através de um override do método compareTo na subclasse.
Por exemplo, para ordenar um array de empregados pelo salário é preciso:
a) Derivar Empregado de Sortable.
b) Implementar o método compareTo para empregados.
c) Chamar ArrayAlg.ordena fornecendo o array de empregados.
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 138
Exemplo:
import java.util.*;
public class TestaOrdenacaoDeEmpregados
{ public static void main(String[] args)
{ Empregado[] vetEmpregados = new Empregado[3];
vetEmpregados [0] =
new Empregado ("Ricardo Silva", 35000,
new GregorianCalendar(1989,10,1));
vetEmpregados[1] =
new Empregado ("Claudia Lins", 75000,
new GregorianCalendar(1987,10,1));
vetEmpregados [2] =
new Empregado ("Luciana Arruda", 38000,
new GregorianCalendar(1990,3,15));
ArrayAlg.ordena(vetEmpregados);
int i;
for (i = 0; i < vetEmpregados.length; i++)
System.out.println(vetEmpregados [i]);
}
}
abstract class Sortable
{ public abstract int compareTo(Sortable b);
}
class ArrayAlg
{ public static void ordena(Sortable[] a)
{ int n = a.length;
boolean troca = true;
while (troca)
{ troca = false;
for (int i = 0; i < n - 1; i++)
{ Sortable temp = a[i];
if (temp.compareTo(a[i+1]) > 0)
{ a[i] = a[i + 1];
a[i + 1] = temp;
troca = true;
}
}
}
}
}
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 139
class Empregado extends Sortable
{ public Empregado (String n, double s, GregorianCalendar d)
{ nome = n;
salario = s;
dataContratacao = d;
}
public void aumentaSalario (double percentualDeAumento)
{ salario = salario * (1 + percentualDeAumento/100);
}
public String getNome()
{ return nome;
}
public double getSalario()
{ return salario;
}
public String toString()
{ return nome + " " + salario + " " + anoContratacao();
}
public int anoContratacao()
{ return dataContratacao.getYear();
}
public int compareTo(Sortable b)
{ Empregado eb = (Empregado)b;
if (salario < eb.salario) return -1;
if (salario > eb.salario) return 1;
return 0;
}
private String nome;
private double salario;
private GregorianCalendar dataContratacao;
}
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 140
Utilizando uma Interface
Infelizmente há um problema na utilização de uma classe abstrata base para expressar
uma propriedade genérica.
O problema é que em Java uma classe pode possuir apenas uma superclasse. Para
resolver este problema Java introduz a idéia de interfaces para prover grande parte da
funcionalidade que a herança múltipla nos fornece. Os projetistas de Java escolheram este
caminho pois a herança múltipla ou torna os compiladores muito complexos (como em
C++) ou muito ineficientes (como em Eiffel).
Então o que é uma interface? É um compromisso de que a sua classe irá implementar
certos métodos com determinadas assinaturas. Você inclusive utiliza a palavra-chave
implements para indicar que a sua classe irá manter este compromisso.
Por exemplo:
public interface Comparable
{ public int compareTo (Object b);
}
O código acima indica que qualquer classe que implemente a interface Comparable
terá o método compareTo. Naturalmente, a forma como o método compareTo
funciona em uma classe específica depende da classe que está implementando a interface
Comparable. A questão é que qualquer classe pode assumir o compromisso de
implementar Comparable – independentemente da sua superclasse assumir ou não
este mesmo compromisso. Todos os descendentes desta classe automaticamente
implementariam Comparable, e assim todos eles teriam acesso a um método
compareTo com a correta assinatura.
Para dizer a Java que a sua classe implementa Comparable, é preciso escrever a
seguinte definição:
class Empregado implements Comparable
Obs.: A interface Comparable deve estar definida em um arquivo .java separado.
Então, tudo o que você precisa fazer é implementar o método compareTo dentro da classe
Empregado.
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 141
Exemplo:
import java.util.*;
public class TestaOrdenacaoDeEmpregados
{ public static void main(String[] args)
{ Empregado[] vetEmpregados = new Empregado[3];
vetEmpregados [0] =
new Empregado ("Ricardo Silva", 35000,
new GregorianCalendar(1989,10,1));
vetEmpregados[1] =
new Empregado ("Claudia Lins", 75000,
new GregorianCalendar(1987,10,1));
vetEmpregados [2] =
new Empregado ("Luciana Arruda", 38000,
new GregorianCalendar(1990,3,15));
ArrayAlg.ordena(vetEmpregados);
int i;
for (i = 0; i < vetEmpregados.length; i++)
System.out.println(vetEmpregados [i]);
}
}
class ArrayAlg
{ public static void ordena(Comparable[] a)
{ int n = a.length;
boolean troca = true;
while (troca)
{ troca = false;
for (int i = 0; i < n - 1; i++)
{ Comparable temp = a[i];
if (temp.compareTo(a[i+1]) > 0)
{ a[i] = a[i + 1];
a[i + 1] = temp;
troca = true;
}
}
}
}
}
Obs.: Observe que a interface abaixo já se encontra definida em
java.lang.Comparable, logo bastaria importar esta classe assim:
import java.lang.Comparable;
public interface Comparable
{ public int compareTo(Object b);
}

_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 142
class Empregado implements Comparable
{ public Empregado (String n, double s, GregorianCalendar d)
{ nome = n;
salario = s;
dataContratacao = d;
}
public void aumentaSalario (double percentualDeAumento)
{ salario = salario * (1 + percentualDeAumento/100);
}
public String getNome()
{ return nome;
}
public double getSalario()
{ return salario;
}
public String toString()
{ return nome + " " + salario + " " + anoContratacao();
}
public void alteraDataContratacao (int ano, int mes, int dia)
{ dataContratacao.set(Calendar.YEAR, ano);
dataContratacao.set(Calendar.MONTH, mes);
dataContratacao.set(Calendar.DATE, dia);
}
public int anoContratacao()
{ return dataContratacao.get(Calendar.YEAR);
}
public int compareTo(Object b)
{ Empregado eb = (Empregado)b;
if (salario < eb.salario) return -1;
if (salario > eb.salario) return 1;
return 0;
}
private String nome;
private double salario;
private GregorianCalendar dataContratacao;
}
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 143
Observe que se a classe pai já implementa uma interface, a sua subclasse não
necessita explicitamente utilizar a palavra-chave implements.
java.lang.Comparable
java.util.Arrays
Propriedades das Interfaces
Embora interfaces não sejam instanciadas com new, elas possuem certas propriedades
similares às classes normais.
• Uma vez definida uma interface, pode-se declarar uma variável objeto com o mesmo
tipo da interface.
Comparable x = new Empregado ( . . . );
Empregado y = new Empregado ( . . . );
if (y.compareTo(x) < 0) . . .
Como você pode ver é sempre possível designar uma variável objeto de um tipo que
implementa uma interface - new Empregado ( . . . ) - a uma variável
objeto declarada com mesmo tipo da interface - x. É como se a interface fosse uma
superclasse da classe que implementa a interface.
• Da mesma forma como você utiliza instaceof para verificar se um objeto possui um
determinado tipo, é possível utilizar instanceof para verificar se um objeto pertence a
uma classe que implementa a interface:
if (umObjeto instanceof Comparable) { . . . }
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 144
• Na impede que você estenda uma interface para criar outra. Isto permite a criação de
uma cadeia de interfaces que possuam desde um maior grau de generalidade até um
alto grau de especialização.
Exemplo: Suponha que você possui uma interface denominada Xxxxx.
public interface Xxxxx
{ public void umMetodo(double x, double y);
}
Então poderíamos imaginar uma interface denominada Powered que a estende:
public interface Yyyyy extends Xxxxx
{ public String outroMetodo();
}
• Embora não seja possível por um campo de instância ou um método estático em uma
interface, é possível definir constantes.
Exemplo:
public interface Yyyyy extends Xxxxx
{ public String outroMetodo(Kkkkk);
public static final int CONSTANTE = 95;
}
• Uma classe pode implementar várias interfaces. Isto nos dá grande flexibilidade na
definição do comportamento de uma classe. Por exemplo, Java possui uma importante
interface denominada Cloneable. Se a sua classe implementa Cloneable, o método
clone da classe Object fará uma cópia dos objetos da sua classe. Suponha, no entanto
que você deseje poder clonar e comparar objetos. Neste caso você deverá
implementar ambas as interfaces.
Exemplo:
public Gerente extends Empregado implements Cloneable, Comparable
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 145
A Interface Cloneable
Quando efetuamos uma cópia de uma variável, a variável original e a cópia são
referências para o mesmo objeto. Isto significa que uma modificação efetuada em
qualquer objeto também afeta o outro.
GregorianCalendar bday = new GregorianCalendar(1959, 6, 16);
GregorianCalendar d = bday;
d.set(Calendar.YEAR, 1970); // Também afeta bday.
Se você quiser que d seja um novo objeto que comece a sua vida igual a bday mas cujo
estado possa divergir ao longo do tempo, então utilize o método clone().
GregorianCalendar bday = new GregorianCalendar(1959, 6, 16);
GregorianCalendar d = (GregorianCalendar)bday.clone();
// É preciso efetuar o cast uma
// vez que clone retorna um objeto.
d.set(Calendar.YEAR, 1970); // Não afeta bday.
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 146
No entanto, se você possui uma classe que representa objetos únicos como empregados
em uma companhia, a operação de clone sobre objetos desta classe não faz sentido. Como
os métodos da classe Object são herdados por todas as classes (inclusive o método
clone() que é um método protected da classe Object), Java acrescenta um novo requisito
para que uma classe possa ser clonada – esta classe necessita implementar a interface
cloneable para indicar que a clonagem é válida. Concluindo, se você deseja clonar um
objeto, não basta simplesmente chamar o método clone() sobre este objeto. É preciso que
a classe deste objeto implemente a interface Cloneable.
Já a classe GregorianCalendar, por exemplo, não representa objetos únicos e portanto
pode ser clonada.
Logo, apenas a própria classe pode clonar objetos dela própria, e existe uma razão para
isto. A classe Object implementa o método clone() efetuando cópia bit a bit, uma vez que
ela não conhece nada sobre o objeto que está clonando. Se todos os campos do objeto que
está sendo clonado são números ou outro tipo básico, a cópia bit a bit funciona bem. Mas
se o objeto contém referências a outros objetos em um ou mais de seus campos, então
uma cópia bit a bit contém cópia exata dos campos que contém referências, logo o objeto
original e o objeto clonado ainda compartilham alguma informação. Veja a figura abaixo:
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 147
É responsabilidade do projetista decidir se:
a) O método clone default (da classe Object) é bom o suficiente.
b) O método clone default pode ser remendado de forma a ser chamado para variáveis
de instância que possuem referência a objetos.
c) A situação não tem solução e o método clone não deve ser chamado.
A terceira opção é o default. Para se optar pela primeira ou segunda opção é preciso:
a) Implementar a interface Cloneable e
b) Redefinir o método clone, com um access modifier do tipo public.
O usuário de um método clone() público ainda assim deve efetuar um cast do
resultado. O método clone() sempre retorna um objeto do tipo Object.
Neste caso o uso da interface Cloneable não tem nada a ver com o uso normal de
interfaces. A interface meramente serve como um indicador (“tag”) de que o projetista da
classe que está sendo clonada sabe o que está fazendo. A classe Object é tão paranóica
com clonagens que seu método clone() gera uma exceção de run-time se um objeto
requisita uma clonagem sem implementar a interface.
Lembre-se que o objetivo normal de um interface (tal como a interface Comparable) é
garantir que a classe implementa um método em particular ou um conjunto de métodos
especificados na interface. Já uma interface do tipo “tag” não possui métodos que devam
ser reimplementados pelas classes que a utilizam (implementam). Seu único objetivo é
permitir que você utilize instanceof para descobrir se um objeto pertence a uma classe
que implementa determinada interface.
if (obj instanceof Cloneable) . . .
Como a interface Cloneable é do tipo “tag” ela não possui métodos que devam ser
reimplementados pelas classes que a utilizam (implementam).
A seguir vem o que a classe Empregado deveria fazer para redefinir clone():
a) Chamar o método clone de Object para realizar uma cópia bit a bit.
b) Fazer o cast do resultado para Empregado para que possamos modificar o campo de
instância dataContratacao para que ele seja um clone do objeto GregorianCalendar
original.
c) Retornar o resultado.
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 148
Exemplo:
public class Empregado implements Cloneable
{ . . .
public Object clone() // Redefinido como public
{ try
{ Empregado e = (Empregado) super.clone();
e.dataContratacao =
(GregorianCalendar)dataContratacao.clone();
// A classe GregorianCalendar implementa Cloneable
return e;
}
catch (CloneNotSupportedException e)
{ // Isto não deveria acontecer uma vez que objetos do
// tipo Empregado podem ser clonados
return null;
}
}
}
Como você pode ver a clonagem é um assunto delicado, e faz sentido que o método clone
seja definido como protected na classe Object.
_______________________________________________________________________________________
_______________________________________________________________________________________
Apostila de Java – Introdução 149
BIBLIOGRAFIA
Cay S. HorstMann e Gray Cornell, Core Java – Volume I - Fundamentals, Sun
Microsystems, 1999