Linguagem de Programação II - Prof. Henrique M. Cristovão (até

Propaganda
Faculdades Integradas Espírito-Santenses
Unidade de Computação e Sistemas
Cursos: Ciência da Computação e Sistemas de Informação
Disciplina: Linguagem de Programação II - 2009/2
Última atualização: 04-08-2009
Prof. Henrique Monteiro Cristovão
Notas de Aula
Aqui você encontra os conteúdos da disciplina Linguagem de Programação II na ordem em que serão
trabalhados. Eles foram escritos de forma resumida, por isso é importante a complementação do estudo nas
bibliografias indicadas. Mas, lembre-se que para efetiva construção do seu conhecimento é imprescindível o
desenvolvimento dos exercícios indicados pelo professor para cada assunto trabalhado.
Não esqueça de:
 trocar idéias com o professor e com os colegas, tanto no laboratório quanto no meio virtual (lista de
discussão – endereço para cadastro disponível no site);
 fazer imediatamente os exercícios indicados para aquele assunto trabalhado afim de compreender
melhor as abstrações da programação orientada a objetos .
 procurar o monitor da disciplina em caso de dúvida.
Obs.: este material é dinâmico e pode ser alterado ao longo do período letivo – observe a data da última
atualização no início desta página.
Bibliografia recomendada:
CAELUM. Java e orientação a objetos. Disponível em:
<http://www.caelum.com.br/downloads/apostila/caelum-java-objetos-fj11.pdf>.
POO nos capítulos 4,6,7,9,10,11,12.
Java básico no capítulo 3.
SANTOS, Rafael. Introdução à programação orientada a objetos usando JAVA. Rio de Janeiro:
Campus, 2003.
DEITEL, H. M.; DEITEL, P. J. Java: Como Programar. 6. ed. Porto Alegre: Bookman, 2005.
1) Visão geral da programação orientada a objetos (POO)
Vantagens da POO:

Reusabilidade de código

Escalabilidade de aplicações

Manutenibilidade
Características:

Existência de classe

Encapsulamento

Polimorfismo

Herança
Notas de aula – 2009/2
- pág.: 1/44
2) Vantagens da escolha da linguagem Java como suporte a aprendizagem da POO.

Programar em Java induz ao programador a usar a orientação a objetos.

Java é simples.

Java é portável.

Java é gratuita.

Java é robusta.

Existem muitas bibliotecas para Java.
(leia uma boa discussão sobre este tema no início do livro de Rafael Santos – Introdução a Progr....).
3) Papéis dos usuários de Java.

Programador de classes.

Programador usuário de classes.

Usuário final.
De uma maneira geral o programador de classes não sabe quem é o usuário final e portanto deve escrever o
programa para o programador usuário.
4) Relações entre objeto, classe e herança:

Classificação/Instanciação.

Generalização/Especialização.

Composição/Decomposição
Alguns exemplos para as relações:
>> Classificação: percebemos vários alunos com os mesmos atributos (nome, matrícula etc), então
podemos classificá-los todos numa mesma classe. João e Maria pertencem a classe Aluno.
>> Instanciação: quando criamos alunos a partir da classe Aluno, ou seja preenchendo os atributos com
valores. O aluno cujo nome é Maria, e média igual a 9,5, etc, é objeto da classe Aluno.
>> Generalização: se percebemos que tanto a classe Aluno quanto a classe Professor tem em comum
vários atributos (nome, endereço, etc) e estes poderiam ser organizados numa terceira classe, por
exemplo Pessoa. Aluno é uma Pessoa, Professor é uma Pessoa.
>> Especialização: supondo a classe aluno, percebemos que existem alguns objetos que tem mais
atributos, como, por exemplo o AlunoMonitor por possuir a carga horária da monitoria, então a criação
desta classe ajusta melhor a utilização dos atributos para ambos os casos. AlunoMonitor é um Aluno.
>> Composição: usamos uma classe para compor outra, como a classe Endereço que é usada como um
dos atributos de Pessoa. Endereço faz parte de Pessoa.
>> Decomposição: quando percebemos que um grupo de atributos pertencentes a uma classe ficaria
mais reusável e encapsulado em outra classe. Os atributos relativos ao endereço de Pessoa ficariam mais
adequados na classe Endereço.
Notas de aula – 2009/2
- pág.: 2/44
5) Conceito de classe, objeto (ou instância), campo (ou atributo), método.
Diagrama UML para representar uma classe.
Em Java, atributo é comumente chamado de campo.
Aluno
-nome : string
-nota1 : double
-nota2 : double
+calculaMedia() : double
nome da classe
campos da classe
métodos da classe
Membros (campos ou métodos) privados são prefixados com ‘-’ e membros públicos com ‘+’
6) Encapsulamento de dados numa classe.
Encapsular é esconder os detalhes de uma classe para tornar a sua utilização mais simples ou mais segura. É
uma das abstrações mais importantes da POO.
Quando o encapsulamento ocorre com a criação de outra classe então usa-se a relação de agregação (ou
composição) entre as classes.
Exemplos: o dados relativos ao endereço de uma Pessoa fica melhor numa outra classe separada: Endereço.
O saldo bancário não deve ser acessível por qualquer método, pois acarretaria em inconsistência de dados,
como ao torná-lo negativo deve-se verificar se é possível para aquele tipo de conta.
7) Revisão de Java e nivelamento.
Abstrações para o primeiro exemplo:

Estudo de caso: uma classe para representar um aluno cujas informações são: o seu nome, duas notas
(de 0 a 10) e uma pontuação extra. Será também criado um campo para armazenar o cálculo da média
com o objetivo de diminuir o custo computacional (este cálculo poderia ser mais complexo). O
cálculo da média é feito pela soma das duas notas mais os pontos e dividido por dois.

Encapsulamento de campos e métodos privados.

Papeis do programador de classes e programador usuário na proteção dos campos de uma classe.

Métodos de interface.

Sobrecarregamento de métodos.

Construtores sobrecarregados.

Métodos de interface (set..., get...) e seus objetivos enquanto filtro e formatação de informações.

Método toString.

Criação de referências de objetos e alocação de memória para instância de objetos.
Notas de aula – 2009/2
- pág.: 3/44
public class Aluno {
private String nome;
private double nota1, nota2, media, pontos;
// quatro construtores sobrecarregados
public Aluno(String _nome,double _nota1,double _nota2,double _pontos) {
this.setNome(_nome);
this.setNotas(_nota1, _nota2, _pontos);
}
public Aluno(double _nota1, double _nota2, double _pontos) {
this("sem nome", _nota1, _nota2, _pontos);
}
public Aluno(double _nota1, double _nota2) {
this(_nota1, _nota2, 0.0);
}
public Aluno() {
this(0.0,0.0);
}
// métodos de interface para acesso aos campos privados
public String getNome() {
return this.nome;
}
public void setNome(String _nome) {
this.nome = _nome;
}
public double getNota1() {
return this.nota1;
}
public double getNota2() {
return this.nota2;
}
public double getMedia() {
return this.media;
}
public double getPontos() {
return this.pontos;
}
public void setPontos(double _pontos) {
this.pontos = _pontos;
this.calculaMedia(10);
}
// tratamento de erro ainda inadequado, será melhorado na próxima versão
public boolean setNotas(double _nota1, double _nota2, double _pontos) {
this.pontos = _pontos;
if(_nota1>=0 && _nota1<=10 && _nota2>=0 && _nota2<=10) {
this.nota1 = _nota1;
this.nota2 = _nota2;
this.calculaMedia(10);
return true;
}
else
return false;
}
public void setNotas(double _nota1, double _nota2) {
this.setNotas(_nota1, _nota2, 0);
}
Notas de aula – 2009/2
- pág.: 4/44
// método para calcular a média. Privado pois o uso é apenas interno a classe
private double calculaMedia() {
this.media = (this.getNota1() + this.getNota2() + this.getPontos())/2;
return this.media;
}
// sobrecarregamento do cálculo da média para limitar o valor máximo
private double calculaMedia(int _limite) {
this.calculaMedia();
if(this.media > _limite)
this.media = _limite;
return this.media;
}
// não é adequado colocar mensagens no método toString, pois
// o programador de classes não se conhece a priori o usuário final
public String toString(){
return this.getNome()+";"
+this.getNota1()+";"
+this.getNota2()+";"
+this.getPontos()+";"
+this.getMedia();
}
}
// Exemplo de uso da classe Aluno
public class Uso {
public static void main(String args[]) {
Aluno x = new Aluno("ze",9,8,6);
System.out.println(x);
Aluno y;
//
y = new Aluno();
//
y.setNotas(8,10);
//
y.setNome("maria");
//
System.out.println(y);//
‘y’ é apenas uma referência
‘y’ já é um objeto com alocação de memória
o acesso aos seus membros privados ocorre somente
por intermédio dos métodos de interface
ou contrutores
}
}
Notas de aula – 2009/2
- pág.: 5/44
8) Método equals como construção padrão em todas as classes.
Este método é útil para comparar dois objetos. É útil em aplicações que necessitam de pesquisa de dados por
exemplo.
public class Aluno {
...
public boolean equals(Object objeto) {
Aluno aux = (Aluno)objeto;
if (this.getNome().equals(aux.getNome()) &&
this.getNota1() == aux.getNota1() &&
this.getNota2() == aux.getNota2() &&
this.getPontos() == aux.getPontos())
return true;
return false;
}
import java.util.Scanner;
public class Uso {
public static void main(String args[]) {
Aluno x,y;
x = new Aluno();
x.setNome("ze");
x.setNotas(10,8);
System.out.println("Aluno x: "+ x);
y = new Aluno();
System.out.println("Entre com o nome:");
String nomeAux = new Scanner(System.in).nextLine();
System.out.println("Entre com as duas notas:");
double nota1Aux = new Scanner(System.in).nextDouble();
double nota2Aux = new Scanner(System.in).nextDouble();
y.setNome(nomeAux);
y.setNotas(nota1Aux, nota2Aux);
System.out.println("os dois objetos são iguais? "+ x.equals(y));
}
}
Exemplo de uma execução:
Aluno x: ze
10.0
8.0
9.0
Entre com o nome:
ze
Entre com as duas notas:
10
8
os dois objetos são iguais: true
Notas de aula – 2009/2
- pág.: 6/44
Método equals gerado pelo Eclipse:

Botão inverso sobre o código da classe desejada

‘Source’ – ‘Generate hashCode() and equals()…’ –

Selecione os campos a serem usados na comparação – ‘Ok’
public class Aluno {
...
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
final Aluno other = (Aluno) obj;
if (Double.doubleToLongBits(media) != Double
.doubleToLongBits(other.media))
return false;
if (nome == null) {
if (other.nome != null)
return false;
} else if (!nome.equals(other.nome))
return false;
if (Double.doubleToLongBits(nota1) != Double
.doubleToLongBits(other.nota1))
return false;
if (Double.doubleToLongBits(nota2) != Double
.doubleToLongBits(other.nota2))
return false;
if (Double.doubleToLongBits(pontos) != Double
.doubleToLongBits(other.pontos))
return false;
return true;
}

Marcando a opção ‘Use instanceof to compare types’
será trocado o trecho:
if (getClass() != obj.getClass())
return false;
pelo trecho:
if (!(obj instanceof Aluno))
return false;
Ambos com o mesmo efeito.
Notas de aula – 2009/2
- pág.: 7/44
9) Método toString com a classe StringBuffer.
Concatenações realizadas com objetos da classe String tem uma performance ruim pois cada vez que ocorre a
concatenação, uma alocação de memória é feita para armazenar a nova string e a antiga e abandonada para o
coletor de lixo.
Uma solução para este problema é o uso da classe StringBuffer
// método toString reescrito com a classe StringBuffer ao invés da String
public String toString(){
StringBuffer dados = new StringBuffer();
dados.append(this.getNome());
dados.append(";");
dados.append(this.getNota1());
dados.append(";")
dados.append(this.getNota2());
dados.append(";")
dados.append(this.getPontos());
dados.append(";")
dados.append(this.getMedia());
return dados.toString();
}
Notas de aula – 2009/2
- pág.: 8/44
10) O problema do tratamento de erros na construção de classes.
O Tratamento de Erros na visão do Programador Usuário:
Exemplo 1: gravação de três nomes num arquivo texto
import java.io.*;
public class TesteArquivo {
public static void main(String[] args) {
BufferedWriter arqSaida;
try{
arqSaida = new BufferedWriter(new FileWriter("nomes.txt"));
arqSaida.write("Maria\r\n");
arqSaida.write("José\r\n");
arqSaida.write("João\r\n");
arqSaida.close();
}
catch(IOException e) {
System.out.println("erro na criação ou acesso ao arquivo");
}
}
}
Exemplo 2: cálculo do novo preço a partir da divisão pelo fator e a devida verificação se ocorreu divisão por
zero
import java.util.Scanner;
public class Principal {
public static void main(String[] args) {
int preco;
int fatorDesconto;
int novoPreco = 0;
preco
= new Scanner(System.in).nextInt();
fatorDesconto = new Scanner(System.in).nextInt();
try {
novoPreco = preco/fatorDesconto;
}
// A classe ArithmeticException serve também para outros erros aritméticos,
// assim, a mensagem “/ by zero” determina especificamente que
// o erro foi de divisão por zero
// O método getMessage serve para capturar a mensagem enviada ao catch
catch(ArithmeticException e) {
if(e.getMessage().equals("/ by zero"))
System.out.println("fator do desconto não pode ser zero!");
}
}
}
Notas de aula – 2009/2
- pág.: 9/44
Exemplo 3: método para adicionar elementos em 'vetor' a partir de 'posicao' usando informações de um arquivo
public static boolean adicionaElementosVetor(int[] vetor, int posicao) {
while(naoForFimDeArquivo()) {
try{
vetor[posicao++] = leElementoDoArquivo();
}
catch(ArrayindexOutOfBound e) {
// o vetor chegou ao fim, então sai do método indicando
// que nem todos os elementos foram incluídos
return false;
}
}
// indica que todos os elementos foram incluídos no vetor
return true;
}
Ordem das classes nos comandos cath:
Exemplo de ordem inadequada (o objeto capturado cai na classe mais abrangente – Exception):
catch(Exception e) {
System.out.println("erro");
}
catch(IOException e) {
System.out.println("erro no acesso ao arquivo");
}
catch(FileNotFoundException e) {
System.out.println("arquivo inexistente");
}
Exemplo de ordem adequada (super classes devem vir primeiro):
catch(FileNotFoundException e) {
System.out.println("arquivo inexistente");
}
catch(IOException e) {
System.out.println("erro no acesso ao arquivo");
}
catch(Exception e) {
System.out.println("erro");
Notas de aula – 2009/2
- pág.: 10/44
O Tratamento de Erros na visão do Programador de Classes:
O programador de classes deve implementar a sua classe de tal forma que o seu funcionamento não fique
prejudicado caso a mesma seja utilizada incorretamente.
Algumas soluções inadequadas para o problema do tratamento de erros:
1. retorno de valor boolean para indicar o erro
2. retorno de valor fora da faixa de escopo de trabalho do método
3. exibição de mensagem de erro
4. parada do processamento
5. substituir o valor inválido por outro válido
6. indicar o erro num campo criado somente para este propósito
Os comandos try-catch-throw-throws oferecem uma solução mais adequada.
Usa objetos das classes Exception (para erros verificados) e RuntimeException (para erros não verificados).
Observe o exemplo da classe Aluno, agora, com tratamento adequado de erros para o problema da nota com
valor fora da faixa permitida (0 a 10). As alterações estão sublinhadas:
public class Aluno {
private String nome;
private double nota1, nota2, media, pontos;
// quatro construtores sobrecarregados
public Aluno(String _nome,double _nota1,double _nota2,double _pontos) throws Exception {
this.setNome(_nome);
this.setNotas(_nota1, _nota2, _pontos);
}
public Aluno(double _nota1, double _nota2, double _pontos) throws Exception {
this("sem nome", _nota1, _nota2, _pontos);
}
public Aluno(double _nota1, double _nota2) throws Exception {
this(_nota1, _nota2, 0.0);
}
public Aluno() throws Exception {
this(0.0,0.0);
}
// métodos de interface para acesso aos campos privados
public String getNome() {
return this.nome;
}
public void setNome(String _nome) {
this.nome = _nome;
}
public double getNota1() {
return this.nota1;
}
public double getNota2() {
return this.nota2;
}
public double getMedia() {
return this.media;
}
Notas de aula – 2009/2
- pág.: 11/44
public double getPontos() {
return this.pontos;
}
public void setPontos(double _pontos) {
this.pontos = _pontos;
this.calculaMedia(10);
}
// tratamento de erros, escopo do programador de classes (comandos throw e throws)
public void setNotas(double _nota1, double _nota2,double _pontos)throws Exception {
this.pontos = _pontos;
if(_nota1>=0 && _nota1<=10 && _nota2>=0 && _nota2<=10) {
this.nota1 = _nota1;
this.nota2 = _nota2;
this.calculaMedia(10);
}
else
//
//
//
//
O comando throw desvia o processamento para o interior do catch que possui o
objeto de erro que está sendo lançado throw
O comando throw obriga o uso de try-catch, mas pode-se passar esta obrigação
adiante (para quem fizer a chamada) com o comando throws no cabeçalho do método
throw new Exception("nota invalida");
// Ao invés da classe Exception pode-se usar a classe RuntimeException, neste
// caso não há a obrigação do tratamento do erro com try-cath, e também não se usa
// o comando throws para repassar o tratamento do erro mais adiante
}
public void setNotas(double _nota1, double _nota2) throws Exception {
this.setNotas(_nota1, _nota2, 0);
}
...
}
public class Uso {
public static void main(String args[]) {
// tratamento de erros, escopo do programador usuário (comandos try-catch)
// o comando try deve envolver todo o código que contém chamadas
// de métodos que podem provocar erro.
// Neste caso apenas a linha x.setNotas(10,8) teria a obrigatoriedade
// de ser envolvida com try-catch
try {
Aluno x,y;
x = new Aluno();
x.setNome("ze");
x.setNotas(10,8);
System.out.println(x);
}
catch(Exception e) { // cria-se um argumento onde o tipo é o nome da
// classe que gerou o objeto de erro
System.out.println("nota inválida");
// se houver mais mensagens de erro, pode-se selecionar com o método getMessage()
// por exemplo:
// if(e.getMessage().equals("nota negativa"))
//
System.out.println("A nota é negativa")
// else if(e.getMessage().equals("nota maior que 10")
//
System.out.println("A nota é maior que 10")
// else
//
...
}
}
}
Notas de aula – 2009/2
- pág.: 12/44
--------------------------------------------------------------------------------------------------------//outro exemplo de uso do tratamento de erros
public class Uso {
public static void main(String args[]) {
Aluno x = new Aluno();
double n1Aux, n2Aux;
System.out.println("Entre com o nome do aluno:");
x.setNome(new Scanner(System.in).nextLine());
while(true){
try {
System.out.println("Entre com as duas notas do aluno:");
n1Aux = new Scanner(System.in).nextDouble();
n2Aux = new Scanner(System.in).nextDouble();
x.setNotas(n1Aux,n2Aux);
break;
}
catch(Exception e) {
System.out.println("nota inválida, faixa permitida: 0 a 10");
}
}
System.out.println("Aluno digitado: "+x);
}
}
Exemplo de execução:
Entre com o nome do aluno:
josé da Silva
Entre com as duas notas do aluno:
8
11
nota inválida, faixa permitida: 0 a 10
Entre com as duas notas do aluno:
8
10
Aluno digitado: josé da Silva
8.0
10.0
9.0
Notas de aula – 2009/2
- pág.: 13/44
11)
Membros de classe estáticos
Um campo estático é útil para representar uma informação que é igual para todos os objetos, consequentemente
só ocupa uma alocação de memória, independente de quantos objetos foram criados. Desta forma este campo
tem a inicialização obrigatória.
Um método estático é útil para não exigir do usuário um objeto para a sua chamada, mas isto só acontece
quando o método não trabalha com o objeto this.
Exemplos de membros estáticos da API do Java:
 Math.PI – constante que contém π
 Math.sqrt(n) – método que retorna a raiz quadrada de n
 Math.random() – método que retorna um número double randômico de 0 a menor do que 1.
 Integer.parseInt(s) – método que retorna o inteiro que equivale a string s
 String.valueOf(n) – método que retorna uma string que equivale a tipo numérico n
O acesso ao membro estático se dá pelo nome da classe: nomeClasse.membroEstático
public class Aluno {
private String nome;
private double nota1, nota2, media, pontos;
// campos estáticos (devem ser inicializados). O modificador final indica que o campo é uma
// constante, isto é, não pode ser modificado
static final public double DEFAULT_APROVACAO = 7.0;
static private double minimoAprovacao = Aluno.DEFAULT_APROVACAO;
public Aluno(String _nome, double _nota1, double _nota2, double _pontos) throws Exception {
this.setNome(_nome);
this.setNotas(_nota1, _nota2, _pontos);
}
public Aluno(double _nota1, double _nota2, double _pontos) throws Exception {
this("sem nome", _nota1, _nota2, _pontos);
}
public Aluno(double _nota1, double _nota2) throws Exception {
this(_nota1, _nota2, 0.0);
}
public Aluno() throws Exception {
this(0.0,0.0);
}
// métodos estáticos de interface para o campo estático minimoAprovacao
public static double getMinimoAprovacao() {
return Aluno.minimoAprovacao;
}
public static void setMinimoAprovacao(double _minimoAprovacao) {
Aluno.minimoAprovacao = _minimoAprovacao;
}
// métodos que usam membros estáticos, formato: “Classe.nomeMembroEstático”
static public boolean isAprovado(double _media) {
if(_media >= Aluno.minimoAprovacao)
return true;
else
return false;
}
public boolean isAprovado() {
return Aluno.isAprovado(this.media);
}
public
public
public
public
String getNome()
void setNome(String _nome)
double getNota1()
double getNota2()
public double getMedia()
{
{
{
{
return this.nome;
this.nome = _nome;
return this.nota1;
return this.nota2;
{ return this.media;
public double getPontos()
{ return this.pontos;
public void setPontos(double _pontos) {
}
}
}
}
}
}
Notas de aula – 2009/2
- pág.: 14/44
this.pontos = _pontos;
this.calculaMedia(10);
}
public void setNotas(double _nota1, double _nota2) throws Exception {
this.setNotas(_nota1, _nota2, 0);
}
public void setNotas(double _nota1, double _nota2, double _pontos) throws Exception {
this.pontos = _pontos;
if(_nota1>=0 && _nota1<=10 && _nota2>=0 && _nota2<=10) {
this.nota1 = _nota1;
this.nota2 = _nota2;
this.calculaMedia(10);
}
else
throw new Exception("nota invalida");
}
// método para calcular a média. Privado pois o uso é apenas interno a classe
private double calculaMedia() {
this.media = (this.getNota1() + this.getNota2() + this.getPontos())/2;
return this.media;
}
// sobrecarregamento do cálculo da média para limitar o valor máximo
private double calculaMedia(int _limite) {
this.calculaMedia();
if(this.media > _limite)
this.media = _limite;
return this.media;
}
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
final Aluno other = (Aluno) obj;
if (Double.doubleToLongBits(media) != Double
.doubleToLongBits(other.media))
return false;
if (nome == null) {
if (other.nome != null)
return false;
} else if (!nome.equals(other.nome))
return false;
if (Double.doubleToLongBits(nota1) != Double
.doubleToLongBits(other.nota1))
return false;
if (Double.doubleToLongBits(nota2) != Double
.doubleToLongBits(other.nota2))
return false;
if (Double.doubleToLongBits(pontos) != Double
.doubleToLongBits(other.pontos))
return false;
return true;
}
public String toString(){
StringBuffer dados = new StringBuffer();
dados.append(this.getNome());
dados.append(";");
dados.append(+this.getNota1());
dados.append(";")
dados.append(+this.getNota2());
dados.append(";")
dados.append(+this.getPontos());
dados.append(";")
dados.append(this.getMedia());
return dados.toString();
}
}
Notas de aula – 2009/2
- pág.: 15/44
class Uso {
public static void main(String args[]) {
try {
Aluno x = new Aluno("ze",9,8,4);
System.out.println(x);
// usos de membros estáticos diversos
Aluno.setMinimoAprovacao(5.0);
System.out.println("minimo para aprovação modificado:"+Aluno.getMinimoAprovacao());
System.out.println("minimo para aprovação default:"+Aluno.DEFAULT_APROVACAO);
System.out.println("o aluno "+x.getNome()+" está aprovado? "+x.isAprovado());
System.out.println("aluno com média 4.0 está aprovado? "+Aluno.isAprovado(4.0));
}
catch(Exception e) {
System.out.println("nota inválida");
}
}
}
Notas de aula – 2009/2
- pág.: 16/44
12)
Reutilização de Classes através de Herança
A reutilização de código é uma das características mais interessantes das OOP (Object-Oriented Languages Linguagens Orientadas a Objetos). As linguagens procedurais também fornecem reutilização de código através de
funções e procedimentos. Porém, as OOP vão bem além pois permitem criar classes a partir de outras já existentes
possibilitando adicionar informações (campos) e comportamentos (métodos) e ainda substituir métodos existentes
através da abstração da sopreposição.
Uma OOL possui os três seguintes elementos:
1. Classes
2. Herança/agregação
3. Polimorfismo
Problema motivador.
Suponha a necessidade de representar um aluno que também pode ser um monitor.
1ª solução: usando apenas uma classe.
Aluno
-nome : string
-qtdeDisciplinas : int
-chMonitoria : int
+calculaMensalidade() : int
+caculaMensalidadeMonitor() : int
Esta solução traz desperdício de memória pois o campo chMonitoria apenas serve quando o objeto for um
Monitor.
2ª solução: usando duas classes independentes.
Aluno
-nome : string
-qtdeDisciplinas : int
+calculaMensalidade() : int
Monitor
-nome : string
-qtdeDisciplinas : int
-chMonitoria : int
+calculaMensalidade() : int
Aqui tem-se o problema do não reaproveitamento do código, gerando um nível de alterabilidade muito ruim, uma
vez que os algoritmos, quando alterados, deverão ser modificados em ambas as classes.
3ª solução: usando duas classes relacionadas com agregação.
Monitor
-chMonitoria : int
+calculaMensalidade() : int
1
1
Aluno
-nome : string
-qtdeDisciplinas : int
+calculaMensalidade() : int
Esta solução dificulta o entendimento, uma vez que a relação Aluno faz parte de Monitor é confusa, apesar de
computacionalmente viável.
Notas de aula – 2009/2
- pág.: 17/44
4ª solução: usando duas classes relacionadas com herança.
Aluno
-nome : string
-qtdeDisciplinas : int
+calculaMensalidade() : int
Monitor
-chMonitoria : int
+calculaMensalidade() : int
Esta solução é a melhor!
Obs.:
o
A leitura da relação é em direção ao triângulo: Monitor.é um Aluno
o
A classe pai (Aluno) também é chamada de superclasse ou classe ancestral.
o
A classe filha (Monitor) também é chamada de subclasse ou classe descendente.
o
A sobreposição ocorre no método calculaMensalidade pois ele possui a mesma assinatura (nomes
e tipos de argumentos iguais) em ambas as classes.
o
Os membros não privados da classe pai podem ser acessados diretamente na classe filha com this.
o
O construtor da classe pai pode ser chamado por um construtor da classe filha com super(
o
Um método sobreposto pode ser chamado de dentro da classe filha com super.
Implementação da 4ª solução – acrescentados os construtores e a sobreposição do método toString:
public class Aluno {
private String nome;
private int qtdeDisciplinas;
public Aluno(String _nome, int _qtdeDisciplinas) {
this.setNome(_nome);
this.setQtdeDisciplinas(_qtdeDisciplinas);
}
public Aluno() {
this("",0);
}
public String getNome()
{
return this.nome;
}
public void setNome(String _nome) {
this.nome = _nome;
}
public int getQtdeDisciplinas() {
return this.qtdeDisciplinas;
}
public void setQtdeDisciplinas(int _qtdeDisciplinas) {
this.qtdeDisciplinas = _qtdeDisciplinas;
}
public String toString(){
return this.getNome()+";"+this.getQtdeDisciplinas();
}
public double calculaMensalidade(double _valor) {
return this.qtdeDisciplinas * _valor;
}
}
Notas de aula – 2009/2
- pág.: 18/44
public class Monitor extends Aluno {
private int chMonitoria;
public Monitor(String _nome, int _qtdeDisciplinas, int _chMonitoria) {
super(_nome, _qtdeDisciplinas);
this.setChMonitoria(_chMonitoria);
}
public int getChMonitoria() {
return this.chMonitoria;
}
public void setChMonitoria(int _chMonitoria) {
this.chMonitoria = _chMonitoria;
}
public double calculaMensalidade(double _valor) {
return super.calculaMensalidade(_valor) - this.getChMonitoria() * 17;
}
public String toString() {
return super.toString() + ";" + this.getChMonitoria();
}
}
13)
Polimorfismo
Trataremos aqui o polimorfismo como o uso de métodos em vinculação dinâmica (ou ligação tardia).
Alguns autores também consideram a sobreposição como polimorfismo – neste caso a vinculação é estática pois é feita
em tempo de compilação, já que o próprio compilador decide qual método chamar pois os mesmos possuem assinatura
diferente.
Na vinculação dinâmica a decisão de qual método chamar só é resolvida em tempo de execução pela máquina virtual Java
JVM (Java Virtual Machine), pois ambos possuem a mesma assinatura e somente o tipo de objeto que faz a chamada do
método é que irá desempatar.
Um detalhe importante que viabiliza o polimorfismo: uma referência da classe pai pode apontar para objetos de
subclasses (em qualquer nível de parentesco). Observe, no exemplo a seguir, o vetor de referências da classe Aluno
apontando também para objetos da classe Monitor.
Exemplo:
public class Uso {
final static double VALORDISCIPLINA = 95;
public static void main(String[] args) {
Aluno x;
int opcao = InOut.leInt("Aluno=1 ou Monitor=2");
if(opcao == 1)
x = new Aluno("Ze",5);
else
x = new Monitor("Maria",5,6);
// chamada polimórfica:
System.out.println(x.toString());
// chamada polimórfica:
System.out.println("Mensalidade: " + x.calculaMensalidade(Uso.VALORDISCIPLINA));
}
}
Notas de aula – 2009/2
- pág.: 19/44
Outro uso muito comum é a passagem de argumentos de forma polimórfica para um método. Observe, no exemplo, o
tipo do argumento x, que permite tanto receber objetos da classe Aluno quanto da classe Monitor.
Exemplo:
public static void imprime(Aluno x) {
System.out.println(x.toString());
}
Observações importantes:

Uma chamada polimórfica também pode ocorrer na solicitação de um método cuja definição possui como
parâmetro uma referência da classe pai. Neste caso a chamada do método pode acontecer passando-se um objeto
da classe pai ou da classe filha.

Para acessar um método da classe filha a partir de uma referência da classe pai é necessário fazer um
cast de conversão, que neste caso é conhecido como downcasting:
((ClasseFilha)referênciaClassePai).métodoClasseFilha
Exemplo:
...
Aluno x = new Monitor("Maria",5,6);
// chamada com erro, pois o método getChMonitoria pertence a clase Monitor:
x.getChMonitoria();
// chamada correta, com downcasting:
((Monitor)x).getChMonitoria();
Notas de aula – 2009/2
- pág.: 20/44
Classe Abstrata
Como problema motivador, suponha a necessidade de representar informações de contas correntes e contas poupança.
Vamos também supor que os algoritmos do depósito sejam diferentes para contas corrente e contas poupança.
Já visando o reaproveitamento de código, através de herança, e também o uso polimórfico do método que realiza o
depósito, poderíamos organizar estas classes da seguinte forma:
Conta
-nome
-cpf
-saldo
+deposita()
Corrente
Poupanca
-cheques
-diaAniversário
+deposita()
+deposita()
Ainda sim existem dois problemas nesta solução:
1º) um usuário desavisado poderia criar um objeto da classe Conta, e gerar transtornos uma vez que não faz sentido um
objeto desta classe.
2º) numa futura extensão deste sistema, por exemplo, na criação da classe Investimento, que também possui a relação “é
uma Conta”, um outro usuário descuidado poderia esquecer de implementar o método deposita() ou então implementá-lo
com outro nome como fazDeposito(). Neste caso, numa chamada polimórfica do método deposita, o algoritmo da classe
pai é que seria executado.
Soluções dos problemas:
 para se prevenir do primeiro problema podemos declarar a classe Conta como abstrata.
 para o segundo problema temos que declarar o método deposita() como abstrato. Métodos abstratos não possuem
código, mas somente a declaração do seu cabeçalho.
Em ambos os casos o próprio compilador detecta os problemas e acusa erro.
Uma classe que possui um método abstrato também deve ser abstrata, pois senão um objeto seu poderia invocar a
execução deste método sem algoritmo. Campos nunca são abstratos.
Exemplo:
Conta
-nome
-cpf
-saldo
+abstract deposita()
Corrente
Poupanca
Investimento
-cheques
-diaAniversário
-tipo
+deposita()
+deposita()
+deposita()
No diagrama de classes UML uma classe abstrata é representada escrevendo seu nome em itálico. Ou então
incluindo <abstract> abaixo do nome.
Notas de aula – 2009/2
- pág.: 21/44
Exemplo de implementação de uma classe abstrata com método(s) abstrato(s):
abstract public class Conta {
private String nome;
private String cpf;
private double saldo;
abstract public void deposita(double _valor);
}
Como conseqüência destas declarações temos: a classe Conta não pode ser instanciada e todas as subclasses de
Conta tem por obrigação a implementação do método deposita, ao menos que ele seja declarado como
abstract também.
Notas de aula – 2009/2
- pág.: 22/44
14)
Interface (tipo de classe)
Uma classe do tipo interface pode ser entendida como uma classe totalmente abstrata É como se fosse um
contrato que diz o que as subclasses deverão fazer mas nada sobre como fazer.
Algumas características:
 Não é declarada com a palavra class, mas com interface.
 Os métodos são implicitamente declarados como abstract e public.
 Os campos são static e final, ou seja, só são permitidas constantes.
 A herança é feita com a palavra implements ao invés de extends e é permitida para várias classes
simultaneamente.
Exemplo 1:
Obs.:
 a interface ObjetoGeometrico herda da interface Cor, que é uma classe formada por constantes com
os códigos das cores. Esta também é uma aplicação de interface muito usada. As constantes ficam
disponíveis em todas as subclasses de ObjetoGeometrico.
 Novos objetos geométricos, tais como triângulos e trapézios, representados por suas classes, herdarão da
interface ObjetoGeometrico e serão obrigados a implementar os três métodos (calculaCentro,
calculaArea e calculaPerimetro). Ales disto terão disponíveis os constantes dos códigos de
cores.
 Herança de interface com interface deve ser escrita com extends.
Notas de aula – 2009/2
- pág.: 23/44
Código:
public
int
int
int
}
interface Cor {
VERMELHO = 234;
AZUL = 178;
AMARELO = 112;
public interface ObjetoGeometrico extends Cor {
double calculaCentro();
double calculaArea();
double calculaperimetro();
}
public class Circulo implements ObjetoGeometrico {
private Ponto centro;
private double raio;
public Ponto calculaCentro() {
return this.centro;
}
public double calculaArea() {
return Math.PI*this.raio*this.raio;
}
public double calculaPerimetro() {
return 2*Math.PI*this.raio;
}
}
public class Retangulo implements ObjetoGeometrico {
// ...
}
Notas de aula – 2009/2
- pág.: 24/44
Exemplo 2:
«interface»
ItemDeBiblioteca
+isEmprestado()
+empresta()
+devolve()
+localiza()
+apresentaDescricao()
Livro
-titulo
-autor
-numeroPaginas
-anoEdicao
+toString()
LivroDeBiblioteca
-statusEmprestimo
-localizacao
-descricao
+isEmprestado()
+empresta()
+devolve()
+localiza()
+apresentaDescricao()
+toString()
Obs.:
 Este é um caso de herança múltipla, pois LivroDeBiblioteca herda da classe Livro e da interface
ItemDeBiblioteca.
 A interface ItemDeBiblioteca serve para moldar todos os novos itens (subclasses) que venham a
aparecer neste sistema, e garantir uma padronização além diminuir a quantidade de problemas advindos
de usos polimórficos.
 A declaração da classe LivroDeBiblioteca fica desta forma:
class LivroDeBiblioteca extends Livro implements ItemDeBiblioteca {
// ...
}
A palavra extends deve ser escrita primeiro que implements.
Notas de aula – 2009/2
- pág.: 25/44
15)
Pacotes e os quatro modificadores de acesso
Um pacote é praticamente uma pasta onde você põe as classes que tem algo em comum. No Eclipse ele pode
ser criado a partir do menu: File – New – Package. Uma pasta será criada com o nome do pacote.
Até o momento todas as classes criadas ficaram no pacote dafault.
Agora, com o conhecimento da abstração de pacote é que podemos apresentar de forma completa os quatro
modificadores de acesso:
Visibilidade do membro segundo cada uma das
possibilidades de declaração de acesso
private
default
protected
Public
Sim
Sim
Sim
Sim
Não
Sim
Sim
Sim
Não
Sim
Sim
Sim
Não
Não
Sim
Sim
Não
Não
Não
Sim
Local onde o membro (campo ou método) será
usado:
Métodos da mesma classe
Métodos de classes herdeiras e no mesmo pacote
Métodos de outras classes no mesmo pacote
Métodos de classes herdeiras e em outro pacote
Métodos de outras classes em outro pacote
A declaração do pacote é feita no início do arquivo com a palavra reservada package:
package tempo; // por convenção usa-se letra minúsculas
public class Data {
...
}
A utilização de uma classe em outro pacote é feita com a palavra reservada import:
import tempo.Data;
...
// permite a utilização da classe Data neste arquivo
Exemplo:
pacote: agenda
pacote: tempo
Feriado
Pessoa
Data
1
*1
1
Agenda
pacote: default
*
1
1
*
*
Hora
*
Uso
1*
Compromisso
Notas de aula – 2009/2
- pág.: 26/44
16)
Enumerações
Uma enumeração é um conjunto de contantes que ajuda ao desenvolvedor definir valores fixos que serão
usados no sistema. A enumeração dá controle e performance, ajudando a reduzir o número de erros na
aplicação, pois o usuário terá um conjunto bem definido de valores possíveis para trabalhar.
As enums são derivadas da classe java.lang.Enum fornecidas na API do Java.
Entenda a aplicabilidade e a sintaxe através dos exemplos que se seguem.
Exemplo 1:
public enum DiaSemana {
SEGUNDA, TERCA, QUARTA, QUINTA, SEXTA, SABADO, DOMINGO;
}
public class Uso {
public static void main(String[] args) {
System.out.println(DiaSemana.QUINTA);
}
}
Saída na console:
QUINTA.
O método values() retorna um vetor de DiaSemana preenchido com os valores,
por exemplo, o código:
DiaSemana[] vet = DiaSemana.values();
System.out.println(vet[0]);
provoca a saída:
SEGUNDA
Observe outro exemplo de uso desta enumeração, agora com o método values()conjugado com o for:
public class Uso {
public static void main(String[] args) {
for(DiaSemana dia : DiaSemana.values()) {
System.out.println(dia);
}
}
}
Saída na console:
SEGUNDA
TERCA
QUARTA
QUINTA
SEXTA
SABADO
DOMINGO
Notas de aula – 2009/2
- pág.: 27/44
Exemplo 2:
public enum Cor { BRANCO, PRETO, VERMELHO, AMARELO, AZUL; }
public class Figura {
private Cor cor;
private int tamanho;
public Figura(Cor cor, int tamanho) {
this.setCor(cor);
this.setTamanho(tamanho);
}
public Cor getCor() {
return cor;
}
public int getTamanho() {
return tamanho;
}
public void setCor(Cor cor) {
this.cor = cor;
}
public void setTamanho(int tamanho) {
this.tamanho = tamanho;
}
// ...
public String toString() {
StringBuffer resultado = new StringBuffer();
resultado.append(this.getTamanho());
resultado.append(" - ");
resultado.append(this.getCor());
return resultado.toString();
}
}
public class Uso {
public static void main(String[] args) {
Figura a = new Figura(Cor.AMARELO, 500);
// ...
a.setCor(Cor.VERMELHO);
System.out.println(a);
}
}
Saída na console:
500 - VERMELHO
A enumeração também pode ter campos. Veja uma extensão para a enum Cor onde tem-se a necessidade de
armazenar o código das cores:
public enum Cor {
BRANCO(45), PRETO(37), VERMELHO(12), AMARELO(98), AZUL(29);
private int codigo;
private Cor(int c) {
this.codigo = c;
}
public int getCodigo() {
return this.codigo;
}
}
Notas de aula – 2009/2
- pág.: 28/44
class Figura {
private Cor cor;
private int tamanho;
public Figura(Cor cor, int tamanho) {
this.setCor(cor);
this.setTamanho(tamanho);
}
public Cor getCor() {
return cor;
}
public int getTamanho() {
return tamanho;
}
public void setCor(Cor cor) {
this.cor = cor;
}
public void setTamanho(int tamanho) {
this.tamanho = tamanho;
}
public String toString() {
StringBuffer resultado = new StringBuffer();
resultado.append(this.getTamanho());
resultado.append(" - ");
resultado.append(this.getCor());
return resultado.toString();
}
// ...
}
public class Uso {
public static void main(String[] args) {
Figura a = new Figura(Cor.AMARELO,
// ...
a.setCor(Cor.VERMELHO);
// ...
System.out.println("Objeto:
System.out.println("Cor:
System.out.println("Tamanho:
System.out.println("Código da cor:
}
500);
"
"
"
"
+
+
+
+
a.toString());
a.getCor());
a.getTamanho());
a.getCor().getCodigo());
}
Saída na console:
Objeto:
Cor:
Tamanho:
Código da cor:
500 - VERMELHO
VERMELHO
500
12
Notas de aula – 2009/2
- pág.: 29/44
17)
Serialização de Objetos
Objetos Java podem ser colocados à disposição num fluxo, por exemplo, para serem gravados num arquivo.
Para isto, a classe deve implementar a interface Serializable (pacote Java.io).
Normalmente classes da biblioteca Java já são implementações de Serializable, como por exemplo,
String, Date.
Exemplo:
public class Data implements Serializable {
...
}
Gravação de objetos num arquivo.
Todas estas chamadas de métodos requerem tratamento de erros.
Use IOException ou outra classe derivada de Exception.
// cria um arquivo
ObjectOutputStream arq = new ObjectOutputStream(new FileOutputStream("arquivo.obj"));
// grava um objeto
arq.writeObject(objeto);
// descarrega os dados pendentes no buffer para o arquivo
arq.flush();
// fecha o arquivo
arq.close();
Leitura de objetos num arquivo.
Se a classe de recebimento dos dados não for uma implementação da classe Serializable deverá ocorrer o
erro:
java.lang.ClassCastException em tempo de execução.
// Abertura do arquivo para leitura dos objetos
ObjectInputStream arq = new ObjectInputStream(new FileInputStream("arquivo.obj"));
// Lê um
// Obs.:
//
//
//
//
//
objeto =
objeto
- É necessário tratar a exceção classNotFoundException
- O objeto deve pertencer a uma classe que tenha implementado Serializable
- É necessário cast de conversão para dar forma ao objeto lido da classe Object
- A leitura é realizada na mesma ordem em que os objetos foram gravados (fila)
- Todos os objetos dependentes/agregados são também carregados
(tipo_do_objeto)arq.readObject();
// Fechamento do arquivo:
arq.close();
Notas de aula – 2009/2
- pág.: 30/44
18)
Classes Wrapper (dependendo da tradução: empacotadoras ou invólucro)
Existem várias situações em classes oferecidas pela biblioteca padrão do Java que não são aceitos valores
primitivos, mas apenas objetos.
Para resolver este problema a biblioteca oferece as classes Wraper, uma para cada tipo primitivo, que além de
armazenar o valor primitivo, oferecem muitas funcionalidades, entra elas, conversões de tipos e métodos para
comparação (equals e compareTo).
Todas as classes são derivadas da classe abstrata Number:
Veja alguns exemplos de utilização:
// obj1 é inicializado com um objeto da classe Integer representando o valor primitivo int 5
Integer obj1 = new Integer(5);
// a recebe o valor primitivo int 5
int a = obj1.intValue();
// b recebe o valor primitivo int 8
int b = Integer.parseInt("8");
// obj1 recebe um objeto da classe Integer representando o valor primitivo 9
obj1 = Integer.valueOf("9");
// str recebe obj1 convertido para String
String s = obj1.toString();
// bin recebe o valor 12 em binário
String bin = Integer.toBinaryString(12);
// hex recebe o valor 12 em hexadecimal
String hex = Integer.toHexString(12);
// obj2 é inicializado com um objeto da classe Double representando o valor primitivo double 3.7
Double obj2 = new Double("3.7");
// x recebe o valor primitivo double 2.5
double x = obj2.doubleValue();
// y recebe o valor primitivo double 4.8
double y = Double.parseDouble("4.8");
// obj1 recebe um objeto da classe Double representando o valor primitivo 5.9
obj2 = Double.valueOf("5.9");
// str recebe obj2 convertido para String
String t = obj2.toString();
Notas de aula – 2009/2
- pág.: 31/44
19)
Coleções
A biblioteca de classes fornecida no Java possui estruturas de dados prontas para atender diversas necessidades.
Faremos um breve estudo sobre três que são bastante utilizadas e cobrem um grande leque de possibilidades:
LinkedList, HashMap e TreeSet. Elas só aceitam objetos. Para inserir um tipo primitivo deve-se utilizar uma
classe Wrapper. Além disto será apresentada também a classe de opoio Collections.
Pacote:
import java.util.*;
Organização das classes:
Notas de aula – 2009/2
- pág.: 32/44
LinkedList
É derivada da interface List.
Aceita elementos repetidos, permite acesso aleatório do n-esimo elemento apesar de percorrer sequencialmente
a lista (da frente para trás ou vice-versa, dependendo do que for mais próximo),
Mantém a mesma ordem da inserção.
Para permitir uma pesquisa binária (rápida) os elementos devem estar ordenados
Alguns métodos úteis desta classe:

List x = new LinkedList(); // instancia um objeto LinkedList e o armazena em x

x.add(obj);
// insere obj no final da lista exp

x.size();
// retorna a quantidade de elementos que já foram inseridos

x.get(n);
// pega o n-ezimo elemento da lista exp

x.addFirst(obj); // insere obj no início da lista exp

x.getFirst();
// retorna o primeiro elemento da lista exp

x.getLast();
// retorna o último elemento da lista exp

x.removeFirst(); // remove e retorna o primeiro elemento da lista exp

x.removeLast(); // remove e retorna o último elemento da lista exp

x.removeAll();
// remove todos os elementos da lista exp
HashMap
É derivada da interface Map.
Esta classe implementa uma tabela hashing onde cada registro possui uma chave e um objeto com os dados a
serem armazenados. A chave não pode ser repetida.
Bom para casos onde o importante é acessar um elemento a partir de uma chave, não aceita repetição da chave
de busca mas permite que o elemento seja repetido, é ruim para acessar os elementos sequencialmente.
Declaração:
static HashMap tab = new HashMap();
Inserção de um elemento:
tab.put(objeto.chave, objeto);
Verificação da existência de uma chave
tab.containsKey(chave); // retorna true ou false
Retorno da quantidade de elementos:
tab.size();
Pesquisa de um elemento na tabela:
tab.get(chave); // retorna o objeto associado a chave.
// Deve ser convertido com um cast,
// de object para o tipo desejado
Notas de aula – 2009/2
- pág.: 33/44
Imprimir a tabela:
System.out.println(tab); // é importante que os objetos inseridos em tab
// pertençam a classes que tenha sobreposto o método toString
Percorrer toda a tabela (isto não é muioto comum):
Iterator i = tab.keySet().iterator(); // cria um ponteiro para percorrer
// a lista de chaves da tabela
while(i.hasNext())
// enquanto existir um próximo elemento
(TipoDaChave)i.next(); // retorna uma chave. Para acessar os outros
// campos da tabela, use esta chave para consultá-la com o método get.
TreeSet
É derivada da interface Set.
Não aceita elementos repetidos, mantém os elementos ordenados a cada inserção, possui busca rápida, é ruim
para acessar os elementos sequencialmente.
Exercite a navegação da documentação do Java: dê uma olhada nos métodos desta classe.
Atalho: http://java.sun.com/javase/6/docs/api/java/util/TreeSet.html
TreeMap
É derivada da interface SortedMap.
Muito boa esta coleção, pois tem características da interface Map, que permite pesquisar com rapidez e
facilidade, e organiza os seus elementos numa árvore, mantendo-os ordenados segundo uma chave.
Exercite a navegação da documentação do Java: dê uma olhada nos métodos desta classe.
Atalho: http://java.sun.com/javase/6/docs/api/java/util/TreeMap.html
Collections
É uma classe de apoio às coleções, pois possui vários métodos estáticos para diversas funcionalidades como:
ordenar, pequisar, converter, embaralhar etc.
Dê uma olhada em: http://java.sun.com/javase/6/docs/api/java/util/Collections.html
Por exemplo, pode-se usar o método sort para ordenar uma lista:
Collections.sort(lista);
Mas, para isto, os objetos da lista devem pertencer a uma classe que implementa a interface Comparator, que
obriga a implementação do método compareTo.
Notas de aula – 2009/2
- pág.: 34/44
Exemplo 1:
Programa simples para demonstrar a sintaxe de criação e inclusão de elementos em coleções do tipo
LinkedList, HashMap e TreeSet.
import java.util.*;
public class Uso2 {
public static void main(String[] args) {
List<Integer> l = new LinkedList<Integer>();
Map<Integer,Integer> m = new HashMap<Integer,Integer>();
Set<Integer> s = new TreeSet<Integer>();
// adiciona os valores 99,45,13,99 na lista 'l'
l.add(new Integer(99));
l.add(new Integer(45));
l.add(new Integer(13));
l.add(new Integer(99));
// adiciona os valores 99,45,13,99 no mapa 'm',
// inclui também uma chave para cada valor
m.put(new Integer(1), new Integer(99));
m.put(new Integer(2), new Integer(45));
m.put(new Integer(3), new Integer(13));
m.put(new Integer(4), new Integer(99));
// adiciona os valores 99,45,13,99 no conjunto 's'
s.add(new Integer(99));
s.add(new Integer(45));
s.add(new Integer(13));
s.add(new Integer(99));
// exibe o conteúdo de cada coleção com o toString nativo:
System.out.println("Lista: " + l);
System.out.println("Mapa: " + m);
System.out.println("Conjunto: " + s);
}
}
Saída na console:
Lista: [99, 45, 13, 99]
Mapa: {1=99, 2=45, 3=13, 4=99}
Conjunto: [13, 45, 99]
Notas de aula – 2009/2
- pág.: 35/44
Exemplo 2:
Programa para demonstrar a serialização de objetos e várias situações envolvedo as classes LinkedList,
HashMap, TreeSet e Collections.
import
import
import
import
import
import
import
import
java.io.FileInputStream;
java.io.FileNotFoundException;
java.io.FileOutputStream;
java.io.IOException;
java.io.ObjectInputStream;
java.io.ObjectOutputStream;
java.io.Serializable;
java.util.*;
/**
* Exemplo de:
* 1. uso das coleções: LinkedList, TreeSet e HashMap
* 2. uso de tipificação em coleções
* 3. serialização de objetos
* 4. classes wrapper
* <p>
* <b> Coleções usadas: </b>
* <p>
* <b> LinkedList </b> - aceita elementos repetidos, permite acesso aleatório do
* n-esimo elemento apesar de percorrer sequencialmente a lista (da frente para trás
* ou vice-versa, dependendo do que for mais próximo),
* mantém a mesma ordem da inserção, para permitir uma pesquisa binária (rápida) os
* elementos devem estar ordenados
* <p>
* <b> TreeSet </b> - não aceita elementos repetidos, mantém os elementos
* ordenados a cada inserção, possui busca rápida, é ruim para acessar os
* elementos sequencialmente
* <p>
* <b> HashMap </b> - bom para casos onde o importante é acessar um elemento a
* partir de uma chave, não aceita repetição da chave de busca mas permite que o
* elemento seja repetido, é ruim para acessar os elementos sequencialmente
* <p>
* Estas classes de coleções foram encapsuladas em outras classes para facilitar o seu
uso
* <p>
* Os objetos são depois gravados em disco através da serialização.
* <p>
* Tipos primitivos só podem ser inseridos em coleções por intermédio de
* classes wrapper (chamadas também de classes empacotadoras ou classes invólucro)
*
* @author Henrique Monteiro Cristovao
*
*/
class Uso {
public static void main(String[] args) {
try {
// -----------------------------------------------------------// teste 1: LinkedList
ColecaoAmigosLinkedList amigos1 = new ColecaoAmigosLinkedList();
amigos1.insere("Tia", "3232-2222", 23);
amigos1.insere("Bia", "2232-2223", 13);
amigos1.insere("Maria", "2121-2224", 53);
amigos1.insere("Luzia", "3333-2225", 43);
amigos1.insere("Maria", "3223-2226", 53);
System.out.println("\nLista não ordenada:\n"+amigos1);
amigos1.ordenaPorNome();
System.out.println("Lista ordenada por nome:\n"+amigos1.toString());
System.out.println("Resultado da busca por Maria:
"+amigos1.busca("Maria"));
Notas de aula – 2009/2
- pág.: 36/44
Arquivo.grava("amigos1.my",amigos1);
ColecaoAmigosLinkedList amigos1b;
amigos1b = (ColecaoAmigosLinkedList)Arquivo.le("amigos1.my");
System.out.println("Dados lidos do arquivo:\n"+amigos1b.toString());
// -----------------------------------------------------------// teste 2: TreeSet
// só funciona se colocar Strings dentro do TreeSet
ColecaoAmigosTreeSet amigos2 = new ColecaoAmigosTreeSet();
amigos2.insere("Tia", "3232-2222", 23);
amigos2.insere("Bia", "2232-2223", 13);
amigos2.insere("Maria", "2121-2224", 53);
amigos2.insere("Luzia", "3333-2225", 43);
amigos2.insere("Maria", "3223-2226", 53);
System.out.println("\nConteúdo do conjunto (sem
repetição):\n"+amigos2.toString());
System.out.println("Resultado da busca por Maria:
"+amigos2.existe("Maria"));
Arquivo.grava("amigos2.my",amigos2);
ColecaoAmigosTreeSet amigos2b;
amigos2b = (ColecaoAmigosTreeSet)Arquivo.le("amigos2.my");
System.out.println("Dados lidos do arquivo:\n"+amigos2b.toString());
// -----------------------------------------------------------// teste 3: HashMap
ColecaoAmigosHashMap amigos3 = new ColecaoAmigosHashMap();
amigos3.insere("Tia", "3232-2222", 23);
amigos3.insere("Bia", "2232-2223", 13);
amigos3.insere("Maria", "2121-2224", 53);
amigos3.insere("Luzia", "3333-2225", 43);
amigos3.insere("Maria", "3223-2226", 53);
System.out.println("\nConteúdo do mapa (sem
repetição):\n"+amigos3.toString());
System.out.println("Resultado da busca por Maria:
"+amigos3.busca("Maria"));
Arquivo.grava("amigos3.my",amigos3);
ColecaoAmigosHashMap amigos3b;
amigos3b = (ColecaoAmigosHashMap)Arquivo.le("amigos3.my");
System.out.println("Dados lidos do arquivo:\n"+amigos3b.toString());
// -----------------------------------------------------------// teste 4: inteiros primitivos
// sorteia 20 números de 1 a 50, armazena num TreeSet (sem repetição:
// é possível que sejam armazenados menos do que 20 números),
// grava em disco,
// lê do disco e exibe
TreeSet<Integer> conjuntoInteiros = new TreeSet<Integer>();
int sorteado;
for(int i = 0; i < 20; i++) {
sorteado = (int)(Math.random()*50) + 1;
// empacota o int (primitivo) num objeto Integer para poder
inserir na coleção
conjuntoInteiros.add(new Integer(sorteado));
}
System.out.println("\nConteúdo do conjunto (sem repetição e
ordenado):\n"+conjuntoInteiros.toString());
System.out.println("Resultado da busca do número 25:
"+conjuntoInteiros.contains(new Integer(25)));
Arquivo.grava("conjunto.my",conjuntoInteiros);
TreeSet<Integer> conjuntoInteiros2;
conjuntoInteiros2 = (TreeSet<Integer>)Arquivo.le("conjunto.my");
Notas de aula – 2009/2
- pág.: 37/44
System.out.println("Dados lidos do
arquivo:\n"+conjuntoInteiros2.toString());
}
catch(FileNotFoundException e) {
System.out.println("Arquivpo não encontrado");
}
catch(IOException e) {
System.out.println("Problemas na leitura ou gravação do arquivo");
}
catch(ClassNotFoundException e) {
System.out.println("Classe inexistente usada na conversão");
}
}
}
/**
* Usada como objeto de teste para compor as classes que encapsulam as coleções
* LinkedList, TreeSet e HashMap
* <p>
* Implementa a interface Comparable, pois implementa o método
* compareTo que é usado na coleção TreeSet. Também poderia ser usado
* pelo método sort (com um argumento) da classe Collections
* <p>
* Implementa a interface Serializable, para permitir que as suas instâncias possam
* ser usadas numa serialização (por exemplo, gravação em disco)
*/
class Amigo implements Comparable<Amigo>, Serializable {
/** nome do amigo */
private String nome;
/** telefone do amigo */
private String telefone;
/** idade do amigo */
private int idade;
/**
* construtor para inicializar valores nos campos
*
* @param _nome
*
nome do amigo
* @param _telefone
*
telefone do amigo
* @param _idade
*
idade do amigo
*/
public Amigo(String _nome, String _telefone, int _idade) {
this.nome = _nome;
this.telefone = _telefone;
this.idade = _idade;
}
/**
* construtor para inicializar somente o nome
*
* @param _nome
*
nome do amigo
*/
public Amigo(String _nome) {
this(_nome, "", 0);
}
/**
* retorna o nome
Notas de aula – 2009/2
- pág.: 38/44
*/
public String getNome() {
return this.nome;
}
/**
* retorna o telefone
*/
public String getTelefone() {
return this.telefone;
}
/**
* retorna a idade
*/
public int getIdade() {
return this.idade;
}
/**
* Método sobreposto para devolver os campos formatados numa String
* @return retorna ums String com todos os dados
*/
public String toString() {
StringBuffer retorno = new StringBuffer(this.getNome());
retorno.append(";");
retorno.append(this.getTelefone());
retorno.append(";");
retorno.append(this.getIdade());
return retorno.toString();
}
/**
* Método necessário para a classe TreeSet, pois ela tem que verificar se há
* elementnos repetidos no conjunto através do critério determinado por
* compareTo, além disso, fornece um algoritmo compararar elementos na busca
* feita com o método contains
*/
public int compareTo(Amigo obj) {
return this.nome.compareTo(obj.nome);
}
/**
* Este é método é necessário ser colocao numa classe sempre que acontecer
* buscas realizadas em HashMap quando a chave é um objeto desta classe
* <p>
* Neste caso será usado o código hash da concatenação dos campos
* Para que este método seja útil será necessário procurar por um
* objeto amigo completo numa HashMap que foi construída desta forma
* <p>
* Normalmente as classes fornecidas pelo Java já possuem hashCode.
*/
public int hashCode() {
return (this.nome+this.telefone+this.idade).hashCode();
}
}
/**
* Fornece um método para ser usado na ordenação de várias instâncias da classe
* Amigo pelo nome.
* <p>
* Esta ordenação é realizada pelo método estático sort da classe Collections
*
* @author Henrique Monteiro Cristovao
*
*/
class ComparaPorNome implements Comparator<Amigo> {
Notas de aula – 2009/2
- pág.: 39/44
public int compare(Amigo amigo1, Amigo amigo2) {
return amigo1.getNome().compareToIgnoreCase(amigo2.getNome());
}
}
/**
* Fornece um método para ser usado na ordenação de várias instâncias da classe
* Amigo pela idade.
* <p>
* Esta ordenação é realizada pelo método estático sort da classe Collections
*
* @author Henrique Monteiro Cristovao
*
*/
class ComparaPorIdade implements Comparator<Amigo> {
public int compare(Amigo amigo1, Amigo amigo2) {
int idade1 = amigo1.getIdade();
int idade2 = amigo2.getIdade();
if (idade1 > idade2)
return 1;
else if (idade1 < idade2)
return -1;
else
return 0;
}
}
/**
* Encapsula a classe LinkedList facilitando o seu uso e tipificando os objetos
* para inclusão como instâncias da classe Amigo
*
* @author Henrique Monteiro Cristovao
*/
class ColecaoAmigosLinkedList implements Serializable {
private List<Amigo> grupo;
ColecaoAmigosLinkedList() {
grupo = new LinkedList<Amigo>();
}
/**
* Insere uma instância da classe Amigo
*
* @param _amigo
*
objeto a ser incluído da coleção
*/
public void insere(Amigo _amigo) {
this.grupo.add(_amigo);
}
/**
* Insere uma instância da classe Amigo por intermédio dos seus campos
*
* @param _nome
*
nome a ser inserido
* @param _tel
*
telefone a ser inserido
* @param _idade
*
idade a ser inserida
*/
public void insere(String _nome, String _tel, int _idade) {
this.insere(new Amigo(_nome, _tel, _idade));
}
/**
* Realiza busca de uma instância da classe Amigo através do seu nome
Notas de aula – 2009/2
- pág.: 40/44
* <p>
* <b>IMPORTANTE:</b> a coleção deve estar ordenada por nome
*
* @param _nome
*
nome a ser pesquisado
* @return retorna a instância associada ao nome pesquisado (não existe
*
precisão da posição - se primeiro ou se último, ...)
*
<p>
*
Se não encontrar retorna null
*/
public Amigo busca(String _nome) {
// o método binarySearsh requer 3 argumentos: a coleção usada na
// pesquisa, o elemento a ser pesquisado, e a forma como o elemento será
pesquisado
int pos = Collections.binarySearch(this.grupo, new Amigo(_nome),
new ComparaPorNome());
if (pos >= 0)
// este cast para Amigo é necessário pois o retorno do get é um Object
return (Amigo) grupo.get(pos);
else
return null;
}
/**
* Ordena a coleção pelo nome
*/
public void ordenaPorNome() {
// o método estático sort da classe Collections necessita
// 2 argumentos: a coleção que será ordenada,
// e a forma como ela será ordenada.
// Existe uma versão sobrecarregada para 1 argumento, neste caso,
// é usado o método compareTo da classe base
Collections.sort(this.grupo, new ComparaPorNome());
}
/**
* Ordena a coleção pela idade
*/
public void ordenaPorIdade() {
Collections.sort(this.grupo, new ComparaPorIdade());
}
/**
* Sobreposição do método toString para retornar todo o conteúdo da coleção
* separando os elementos por espaços
* @return retorna ums String com todos os dados
*/
public String toString() {
StringBuffer s = new StringBuffer("LinkedList = ");
for (int i = 0; i < this.grupo.size(); i++)
s.append(this.grupo.get(i) + " ");
return s.toString();
}
}
/**
* Encapsula a classe TreeSet facilitando o seu uso e tipificando os objetos para
* inclusão como instâncias da classe Amigo
*
* @author Henrique Monteiro Cristovao
*/
class ColecaoAmigosTreeSet implements Serializable {
private Set<Amigo> grupo;
ColecaoAmigosTreeSet() {
grupo = new TreeSet<Amigo>();
Notas de aula – 2009/2
- pág.: 41/44
}
/**
* Insere uma instância da classe Amigo
*
* @param _amigo
*
objeto a ser incluído da coleção
*/
public void insere(Amigo _amigo) {
this.grupo.add(_amigo);
}
/**
* Insere uma instância da classe Amigo por intermédio dos seus campos
*
* @param _nome
*
nome a ser inserido
* @param _tel
*
telefone a ser inserido
* @param _idade
*
idade a ser inserida
*/
public void insere(String _nome, String _tel, int _idade) {
this.insere(new Amigo(_nome, _tel, _idade));
}
/**
* Verifica a existência de uma intância através do campo nome
* @param _nome nome a ser pesquisado
* @return true se achou <p>
*
false se não achou
*/
public boolean existe(String _nome) {
return this.grupo.contains(new Amigo(_nome));
}
/**
* Sobreposição do método toString para retornar todo o conteúdo da coleção
* separando os elementos por espaços
* @return retorna ums String com todos os dados
*/
public String toString() {
StringBuffer s = new StringBuffer("TreeSet = ");
Iterator i = this.grupo.iterator();
while (i.hasNext())
s.append(i.next() + " ");
return s.toString();
}
}
/**
* Encapsula a classe HashMap facilitando o seu uso e tipificando os objetos para
* inclusão como instâncias da classe Amigo
*
* @author Henrique Monteiro Cristovao
*/
class ColecaoAmigosHashMap implements Serializable {
private Map<String,Amigo> grupo;
ColecaoAmigosHashMap() {
grupo = new HashMap<String,Amigo>();
}
/**
* Insere uma instância da classe Amigo
*
Notas de aula – 2009/2
- pág.: 42/44
* @param _amigo
*
objeto a ser incluído da coleção
*/
public void insere(Amigo _amigo) {
this.grupo.put(_amigo.getNome(), _amigo);
}
/**
* Insere uma instância da classe Amigo por intermédio dos seus campos
*
* @param _nome
*
nome a ser inserido
* @param _tel
*
telefone a ser inserido
* @param _idade
*
idade a ser inserida
*/
public void insere(String _nome, String _tel, int _idade) {
this.insere(new Amigo(_nome, _tel, _idade));
}
/**
* Verifica a existência de uma intância através da chave nome
* @param _nome nome a ser pesquisado
* @return true se achou <p>
*
false se não achou
*/
public boolean existe(String _nome) {
return this.grupo.containsKey(_nome);
}
/**
* Realiza busca de uma instância da classe Amigo através da sua chave (nome)
* @param _nome nome a ser pesquisado
* @return retorna a instância associada ao nome pesquisado
*
<p>
*
Se não encontrar retorna null
*/
public Amigo busca(String _nome) {
// este cast para Amigo é necessário pois o retorno do get é um Object
return (Amigo) this.grupo.get(_nome);
}
/**
* Sobreposição do método toString para retornar todo o conteúdo da coleção
* separando os elementos por espaços
* @return retorna ums String com todos os dados
*/
public String toString() {
StringBuffer s = new StringBuffer("HashMap = ");
Iterator i = this.grupo.keySet().iterator();
while (i.hasNext())
s.append((Amigo) this.grupo.get((String) i.next()) + " ");
return s.toString();
}
}
/**
* Oferece métodos estáticos de gravação e leitura de objetos no disco
* através da serialização.
*
* @author Henrique Monteiro Cristovao
*/
abstract class Arquivo {
/**
Notas de aula – 2009/2
- pág.: 43/44
*
*
*
*
Realiza gravação de um objeto no disco
@param _nomeArquivo nome do arquivo que será criado na gravação
@param _objeto instância que será gravada no arquivo
@exception IOException problemas na criação do arquivo ou gravação
*/
static public void grava(String _nomeArquivo, Object _objeto) throws IOException {
ObjectOutputStream buffer = new ObjectOutputStream(new
FileOutputStream(_nomeArquivo));
buffer.writeObject(_objeto);
buffer.close();
}
/**
* Realiza leitura de um objeto no arquivo
* @param _nomeArquivo nome do arquivo a ser lido
* @return retorna uma instância da classe Object lida do arquivo
* @exception IOException problemas na leitura do arquivo
* @exception ClassNotFoundException problemas na conversão do objeto lido
*/
static public Object le(String _nomeArquivo) throws IOException,
ClassNotFoundException {
ObjectInputStream buffer = new ObjectInputStream(new
FileInputStream(_nomeArquivo));
Object _objeto = buffer.readObject();
buffer.close();
return _objeto;
}
}
Saída na console:
Lista não ordenada:
LinkedList = Tia;3232-2222;23
Maria;3223-2226;53
Lista ordenada por nome:
LinkedList = Bia;2232-2223;13
Tia;3232-2222;23
Resultado da busca por Maria:
Dados lidos do arquivo:
LinkedList = Bia;2232-2223;13
Tia;3232-2222;23
Bia;2232-2223;13 Maria;2121-2224;53 Luzia;3333-2225;43
Luzia;3333-2225;43 Maria;2121-2224;53 Maria;3223-2226;53
Maria;2121-2224;53
Luzia;3333-2225;43 Maria;2121-2224;53 Maria;3223-2226;53
Conteúdo do conjunto (sem repetição):
TreeSet = Bia;2232-2223;13 Luzia;3333-2225;43 Maria;2121-2224;53 Tia;3232-2222;23
Resultado da busca por Maria: true
Dados lidos do arquivo:
TreeSet = Bia;2232-2223;13 Luzia;3333-2225;43 Maria;2121-2224;53 Tia;3232-2222;23
Conteúdo do mapa (sem repetição):
HashMap = Maria;3223-2226;53 Luzia;3333-2225;43 Bia;2232-2223;13 Tia;3232-2222;23
Resultado da busca por Maria: Maria;3223-2226;53
Dados lidos do arquivo:
HashMap = Maria;3223-2226;53 Luzia;3333-2225;43 Tia;3232-2222;23 Bia;2232-2223;13
Conteúdo do conjunto (sem repetição e ordenado):
[1, 2, 3, 7, 12, 13, 14, 15, 19, 20, 22, 24, 26, 28, 32, 33, 40, 42, 46]
Resultado da busca do número 25: false
Dados lidos do arquivo:
[1, 2, 3, 7, 12, 13, 14, 15, 19, 20, 22, 24, 26, 28, 32, 33, 40, 42, 46]
Notas de aula – 2009/2
- pág.: 44/44
Download