Escolar Documentos
Profissional Documentos
Cultura Documentos
r
1.2 Atributos 16
.b
1.2.1 Declaração de atributos 16
1.2.2 Convenções para nomes de atributos 17
1.2.3 Acessando atributos a partir de outras classes 17
m
1.2.4 Inicialização de atributos 19
1.2.5 Laboratório : utilização de atributos 21
co
1.3 Métodos 22
1.3.1 Declaração de métodos 22
1.3.2 Convenções para nomes de métodos: 22
1.3.3
1.3.4
1.3.5
Retorno de métodos
Passagem de parâmetros
Acessando métodos a partir de outras classes
g. 23
24
25
in
1.3.6 Exemplos 26
1.3.7 Erros comuns 28
nn
2 Introdução a UML 41
2.1 UML e metodologias de desenvolvimento de software 42
2.2 Principais diagramas da UML 43
w
r
3.2.3 Quando utilizar private ou public? 61
.b
3.2.4 Exemplos 62
3.3 Objeto this 64
3.4 Encapsulamento de atributos compostos 66
m
3.5 Acoplamento (Coupling) 74
3.5.1 Acoplamento Forte 74
co
3.5.2 Acoplamento Fraco 74
3.6 Laboratório: encapsulamento 75
4 Sobrecarga de métodos 79
4.1
4.2
Exemplos
Sobrecarga com tipos ambíguos
g. 81
83
in
4.3 Varargs e sobrecarga 85
4.4 Laboratório: sobrecarga de métodos 87
4.5 Certificação Oracle Certified Java Programmer (OCJP) 88
nn
5 Construtores 91
5.1 Declarando construtores 94
5.1.1 Erros comuns na declaração de construtores 97
ai
r
7.6 Agregação 141
.b
7.7 Composição 142
7.8 Dependência 143
7.9 Classe Associativa 144
m
7.10 Estudo de caso: Modelagem de uma empresa 145
7.10.1 Laboratório: associação simples 147
co
7.10.2 Laboratório Opcional: atributos compostos 149
7.10.3 Laboratório Opcional de Modelagem 151
8 Herança 155
8.1
8.2
Representação de herança na UML
Exemplos
g. 156
157
in
8.3 Herança e modificador private 162
8.4 Modificador protected 163
8.5 Referência implícita super 165
nn
9 Enumerações 195
9.1 Introdução ao uso de enumerações 197
w
r
10.2.4 Estendendo uma Interface 229
.b
10.2.5 Representando interfaces na UML 231
10.2.6 Interface vs. Abstract 233
10.2.7 Erros comuns 234
m
10.2.8 Laboratório: interfaces 239
10.3 Certificação Oracle Certified Java Programmer (OCJP) 241
co
11 Polimorfismo 245
11.1 Cast de objetos 245
11.1.1 Cast up (Widening) 248
11.1.2
11.1.3
11.2
Cast down (Narrowing)
Operador instanceof
Polimorfismo
g. 249
250
251
in
11.2.1 Parâmetros polimórficos 254
11.2.2 Coleções Heterogêneas 255
nn
r
13 Tratamento de erros 305
.b
13.1 Exceções 306
13.1.1 Hierarquia das classes de erro 306
13.1.2 A classe Error 307
m
13.1.3 A classe Exception 308
13.1.4 RuntimeExceptions 309
co
13.1.5 Laboratório: runtime exceptions 311
13.2 Tratamento de exceções 312
13.2.1 A instrução throw 313
13.2.2
13.2.3
13.2.4
A instrução throws
A estrutura try / catch
A instrução finally
g. 315
319
323
in
13.2.5 Capturando múltiplas exceções 326
13.2.6 Laboratório: capturando exceções 332
nn
r
.b
m
co
g.
in
nn
ai
.tr
w
w
w
r
.b
m
co
g.
in
nn
ai
Métodos
w
w
No paradigma de programação procedural, trabalha-se com tipos de dados primitivos e estruturados. Os tipos
r
primitivos correspondem a valores numéricos, caracteres e valores lógicos (verdadeiro ou falso). Denominam-se
.b
tipos estruturados, os tipos definidos pelo usuário, que agrupam diversos outros tipos em uma estrutura mais
complexa, mas que permite uma manipulação mais fácil e consistente em um programa.
m
Para criar variáveis que representem datas utilizando apenas variáveis primitivas, existem algumas alternativas:
co
• declarar uma variável numérica para cada data, representando o tempo em segundos desde uma data
base (ex: 01/01/1970);
•
g.
para cada data, declarar um conjunto de variáveis numéricas representando cada uma das partes de uma
in
data (mês, dia, ano, etc.).
nn
Ambas as soluções criam dificuldades para seu gerenciamento em um programa. No primeiro caso, é preciso
w
fazer conversões toda vez que se quiser manipular apenas uma das partes da data, como por exemplo, retornar o
ano correspondente. No segundo caso, na presença de várias datas, nada garante que por algum erro na
w
programação não se misturem partes de datas diferentes, como por exemplo, o dia de uma data com o mês de
outra.
w
Com a programação orientada a objetos, podem ser criados tipos customizados, que agrupem características e
comportamento em um único elemento. Estes tipos são chamados de classes.
r
.b
1.1 Classes & Objetos
1.1.1 Classes
m
Uma classe é definida pelo usuário pelas especificações (características e comportamentos) que a identificam. É
possível dizer que uma classe é um molde que será usado para construir objetos que representam elementos da
co
vida real.
Produto
Identificação Preço
ai
.tr
Produto
w
Identificação Preço
w
Atributos nem sempre são úteis individualmente, e por isso é importante definir algumas ações que possam ser
executadas dentro do ambiente modelado, usando os atributos existentes na classe. Essas ações definem o
comportamento desejado para a classe.
r
.b
Denominam-se Métodos, as operações implementadas para definir as ações previstas para essa classe. No
exemplo da loja, podem ser atribuídos aos Produtos algumas funcionalidades tais como aumentar e diminuir o
m
preço e alterar a identificação. Note que as ações estão geralmente associadas aos atributos da classe (Preço,
Identificação).
co
Produto
Identificação Preço
Aumentar preço
Diminuir preço
g.
in
Alterar identificação
nn
*Alguns modificadores ou palavras reservadas podem ser aplicados alterando características e a forma de acesso
a classe (o assunto será abordado posteriormente).
w
1. class Data {
2. //declaração de atributos e métodos
w
3. }
1.1.4 Objetos
r
Nos exemplos anteriores (Livro de Java, R$ 150,00; Camiseta, R$ 15,00) estão representados casos particulares
.b
da classe Produto. Eles são denominados instâncias das classes, ou objetos, e têm vida independente entre si,
apesar de compartilharem o mesmo “molde” (Classe).
m
Para criar objetos utiliza-se o operador new, e o espaço necessário de memória para o objeto é então alocado.
Esse espaço de memória, quando não está mais sendo utilizado, é liberado por um elemento da Java Virtual
co
Machine (JVM), denominado Garbage Collector.
Sintaxe
<NomeDaClasse> <nomeVariavel> = new <NomeDaClasse>() g.
in
Para criar uma instância da classe Pessoa utilize o comando:
Pessoa p = new Pessoa();
nn
No mundo real utiliza-se um produto que possui informações de garantia, matéria-prima, etc. Porém, conforme o
tipo de aplicativo, é necessário analisar se tais informações devem ser colocadas na definição do objeto.
r
.b
No mundo real troca-se produtos entre pessoas, entretanto, num cenário de e-business, não é preciso
implementar este comportamento ao se definir o Produto.
m
Exemplo: abstração de Data
co
Qual é a estrutura básica de uma Data?
• Dia;
• mês;
• ano.
• preço;
• nome.
w
• aplicar desconto;
• alterar o nome.
w
Coesão em orientação a objetos é o grau de direcionamento de uma classe para uma única e bem definida
responsabilidade, e traz os seguintes benefícios:
• maior facilidade na manutenção do código;
• melhor reaproveitamento das classes criadas.
r
• arquivos de textos são gerados por um servidor de dados com informações cadastrais de clientes,
.b
com um registro por linha;
• através de uma interface gráfica o usuário seleciona um arquivo fornecido para processamento;
m
• o aplicativo deve ler os registros do arquivo selecionado e apresentá-los na interface gráfica
classificando-os por município e por nome de cliente.
co
É possível atender aos requisitos e construir este aplicativo com uma única classe Java. Mas será esta a melhor
abordagem a médio e longo prazo? Considere as seguintes solicitações de melhoria, após algum período de
utilização do aplicativo:
•
•
g.
o usuário poderá escolher através da interface gráfica outros critérios de classificação;
o servidor de dados passa a fornecer o código de município ao invés do nome de município nos
in
registros dos arquivos de clientes gerados. Um arquivo à parte é disponibilizado pelo mesmo servidor
com a relação de códigos e nomes de municípios;
nn
dados, e enviar por e-mail, automaticamente e semanalmente, relatórios para cada gerente de filial
com os clientes de sua região classificados por município e por nome.
.tr
Na melhor e mais rara das hipóteses essas solicitações de melhoria serão feitas de uma só vez, porém na prática
essas solicitações são sempre feitas individualmente e em datas diferentes! Qual o esforço de manutenção cada
w
vez que uma nova solicitação é apresentada? E quanto ao novo aplicativo, será que é o caso de uma construção
“a partir do zero”?
w
w
r
Graças à coesão, cada solicitação de melhoria demandará apenas um esforço de manutenção pontual, ou a
.b
criação de novas classes (coesas) se necessário:
m
Tabela 1.1 - Modelagem do sistema com alta coesão
co
Outros critérios de Lista com Parametrização do
classificação opções de critério de classificação
classificação
g.
in
Código do município Leitura do arquivo
em vez do nome de municípios e
nn
transformação de
código em nome
ai
r
métodos são chamadas variáveis locais e têm escopo menor do que um atributo.
.b
Para declarar um atributo em uma classe indica-se o tipo de dado que será armazenado nele.
m
Atributos podem ser de dois tipos:
• tipos primitivos (Ex: long,int,boolean, etc.);
• tipos reference (Ex: Arrays, String, Integer, Date, Cliente, Pessoa, etc.).
co
Sintaxe para declaração
*<tipo do atributo> identificador; g.
Observação: Quando o atributo é um array a declaração é feita da mesma forma que a declaração de uma
in
variável local do tipo array.
Exemplos: int[] array ou int array[]
nn
*.Alguns modificadores ou palavras reservadas podem ser aplicados a atributos alterando suas características e
forma de acesso.
ai
1. class Pessoa {
2. long rg;
.tr
3. String nome;
4. String sobrenome;
5. String dataNascimento;
6. String[] telefones;
w
7. }
r
facilmente o que são classes e o que são atributos. Imagine um desenvolvedor
.b
perguntando para outro quais os nomes dos atributos da classe Cliente, e o outro
respondendo: nome, telefone e endereço.
Quando as convenções são corretamente empregadas sabe-se como escrever os atributos
m
e não é preciso consultar toda a documentação para saber como eles foram declarados:
com maiúsculas, minúsculas, underscore, etc.
co
Identificadores de atributos devem ser declarados com letras minúsculas.
Sintaxe
w
nomeVariavel.nomeAtributo
w
1. class TestePessoa {
2. public static void main(String[] args) {
3. // Cria-se uma instância da classe Pessoa
4. Pessoa p = new Pessoa();
5. // Acessando os atributos da classe Pessoa para definir seus valores
r
6. p.nome = "Rodrigo";
7. p.sobrenome = "Monteiro";
.b
8. p.dataNascimento = "25/12/1975";
9. p.rg = 11111;
10. String[] osTelefones = { "1234-5678", "8765-4321"};
11. p.telefones = osTelefones;
m
12. // Acessando os atributos para leitura
13. System.out.println("Nome : " + p.nome + " " + p.sobrenome );
14. System.out.println("Data Nasc : " + p.dataNascimento );
co
15. System.out.println("RG : " + p.rg );
16. System.out.println("Telefones:" );
17. for (int i=0; i < p.telefones.length ; i++){
18. System.out.println(p.telefones[i]);
19. }
20.
21.}
} g.
in
Código 1.4 - TestePessoa.java
nn
ai
.tr
w
w
w
r
Tipo Valor de Inicialização
.b
byte 0
short 0
int 0
m
float 0.0f
long 0L
double 0.0d
co
char ‘\u0000’
boolean false
refer.objeto null
g.
in
Atenção! Atributos do tipo array são inicializados com null, e portanto qualquer acesso a
elementos resultará em um erro em tempo de execução chamado NullPointerException,
nn
lembrando que não é possível acessar atributos ou métodos de objetos que estão com o valor
null, ou acessar elementos de arrays que estão nulos.
ai
.tr
w
w
w
r
11. }
.b
12.}
m
co
g.
in
nn
ai
.tr
w
w
w
Objetivo:
Exercitar e fixar a criação de classes e utilização de atributos.
Tabela de atividades:
Atividade OK
r
1. Faça o download do arquivo aj2lab01_01.zip na URL indicada pelo instrutor(a).
.b
2. Descompacte o arquivo em seu diretório de trabalho.
m
• número do tipo String
• banco do tipo int
co
4. Siga as instruções encontradas na classe TestaAgencia.
r
Métodos são declarados dentro do corpo da classe, ou seja, dentro do bloco de código da definição da classe, que
.b
é delimitado pela abertura e fechamento de chaves ({ e }).
m
Um método é dividido em três partes:
• retorno do método;
• nome do método;
co
• parâmetros do método.
*Alguns modificadores ou palavras reservadas podem ser aplicados a métodos alterando suas características e
ai
formas de acesso.
.tr
• para retornar o valor para quem chamou o método utiliza-se a instrução return;
• o tipo do valor retornado deve ser compatível com o tipo indicado na assinatura do método;
• o valor de retorno de um método pode ser uma literal (um valor explícito);
•
r
o retorno de um método pode ser obtido através da chamada a um método ou expressão;
•
.b
variáveis que recebem valores retornados nos métodos devem obrigatoriamente ser de tipo
compatível com o declarado na assinatura do método;
• os valores retornados por métodos não precisam ser armazenados em variáveis e podem ser,
m
opcionalmente, ignorados;
• quando um método não retorna valor nenhum, indica-se em sua declaração que seu tipo de retorno é
co
void;
• métodos com retorno void podem utilizar a instrução return sem parâmetros.
1. class ExemploMetodos {
2.
g.
in
3. //void indica que o método não retorna nada
4. void imprime() {
5. System.out.println("Este metodo nao retorna nada!");
nn
6. }
7. //o método DEVE retornar um int ou tipo primitivo compatível, caso
8. //contrário haverá um erro de compilação!!
9. int calculaFrete() {
10. return 19;
ai
11. }
12. //o método DEVE retornar um objeto da classe String, ou seja, um tipo
13. //reference
.tr
r
int soma(int x, int y) {
.b
return x + y;
}
m
assinatura: soma (int, int)
Em Java, parâmetros são sempre passados por valor, ou seja, uma cópia do valor da variável é transmitida ao
co
método. Algumas linguagens permitem a passagem de parâmetros por referência, isto é, com a própria variável
sendo transmitida ao método.
1. class Calculadora {
2.
g.
3. // a passagem de dois valores do tipo int (ou tipo compatível) é obrigatória
in
4. int soma(int x, int y) {
5. return x + y;
6. }
nn
7.
8. //a passagem de dois valores do tipo double(ou tipo compatível)é obrigatória
9. double multiplicacao(double d1, double d2) {
10. double resultado = d1 * d2;
11. return resultado;
ai
12. }
13.
14. // a passagem de dois valores do tipo int (ou tipo compatível) é obrigatória
.tr
25. }
26.}
nomeVariavel.nomeDoMetodo(<parâmetros>);
r
Observe a seguir, a criação de uma classe chamada TesteCalculadora, cujo método main acessa os métodos
.b
da classe Calculadora definida anteriormente.
m
1. class TesteCalculadora {
2.
3. public static void main(String[] args) {
co
4. Calculadora c = new Calculadora();
5. c.print("Vamos testar a calculadora");
6. int resultado1 = c.soma(10, 10);
7. System.out.println(" 10 + 10 " + resultado1);
8.
9.
10.
11.
double resultado2 = c.multiplicacao(10, 10);
System.out.println(" 10 * 10 " + resultado2);
boolean resultado3 = c.maior(20, 1000);
System.out.println(" 20 > 1000 " + resultado3);
g.
in
12. }
13.}
nn
1. class Produto {
2. double preco;
3. String descricao;
4. String marca;
5.
6. void inicializaValores(double umPreco, String umaDescricao, String umaMarca)
7. {
8. preco = umPreco;
r
9. descricao = umaDescricao;
.b
10. marca = umaMarca;
11. }
12. void imprime() {
13. System.out.println("------------------------");
m
14. System.out.println("Produto : " + descricao);
15. System.out.println("Marca : " + marca);
16. System.out.println("Preco : " + preco);
17. System.out.println("------------------------");
co
18. }
19.}
1. class TesteProduto {
2.
3.
public static void main(String[] args) {
Produto p = new Produto();
g.
in
4. p.inicializaValores(9.0, "Produto X", "Marca Y");
5. p.imprime();
6. // aumento de 10% no preço do produto
nn
7. p.preco *= 1.1;
8. p.descricao = " Nova descricao";
9. p.marca = "Sem-marca";
10. p.imprime();
11. }
ai
12.}
r
13. System.out.println(dia + "/" + mes + "/" + ano);
.b
14. }
15. }
m
1. class TesteData {
2. public static void main(String[] args) {
co
3. Data d = new Data();
4. d.dia = 12;
5. d.mes = 1;
6. d.ano = 2004;
7. d.imprime();
8.
9.
10.
11.
String bi = d.isAnoBissexto() ? " " : " nao ";
g.
System.out.println("O ano " + d.ano + " " + bi + "e bissexto ");
in
12. Data d2 = new Data();
13. d2.imprime();
14.
nn
r
1. class CalculadoraRetornoIncorreto {
.b
2. int soma(int i, int j) {
3. float f = i + j;
4. return f;
5. }
m
6. }
co
g.
in
nn
ai
.tr
w
w
w
r
1. class ReturnInatingivel {
2. boolean teste() {
.b
3. int i = 0;
4. if (i > 0) {
5. return true;
6. } else {
m
7. return false;
8. }
9. return false;
co
10. }
11.}
g.
in
nn
ai
.tr
w
w
w
1. class SemReturn {
r
2. boolean teste() {
.b
3. int i = 0;
4. if (i > 0) { return true; }
5. }
6.}
m
Código 1.15 - SemReturn.java
co
g.
in
nn
ai
.tr
w
w
w
Veja um exemplo:
r
1. class ImpressoraSemVarargs {
2. void listaNomes(String[] nomes) {
.b
3. System.out.println("Lista de nomes recebidos: ");
4. for (int i = 0; i < nomes.length; i++) {
5. System.out.println("\t " + nomes[i]);
m
6. }
7. }
8.
9. void listaNotas(double[] notas) {
co
10. System.out.println("Lista de notas recebidas: ");
11. for (int i = 0; i < notas.length; i++) {
12. System.out.println("\t " + notas[i]);
13. }
14. }
15.} g.
Código 1.16 - ImpressoraSemVarargs.java
in
1. class TesteImpressoraSemVarargs {
nn
co
Indica-se na assinatura do método ou do construtor que ele recebe um número de parâmetros variável com três
pontos, entre o tipo da variável e o nome da variável, como se observa no trecho de código abaixo.
O TipoObjeto pode ser um tipo primitivo, Object ou um tipo mais específico como Cliente, String, Date ou
outro qualquer e deve ser sempre o último parâmetro da lista.
ai
Os parâmetros devem ser passados na chamada do método separados por vírgula, e o próprio Java se encarrega
de transformar os objetos em um array.
.tr
Dentro do método o parâmetro objetos pode ser utilizado como um array, afinal de contas ele poderá conter um ou
mais objetos da classe indicada no TipoObjeto. Desta forma utiliza-se a propriedade length do array para
w
conhecer o número de elementos contidos no array, e é possível acessar seus elementos através do índice do
array, como se vê no exemplo a seguir:
w
w
r
11. for (int i = 0; i < notas.length; i++) {
12. System.out.println("\t " + notas[i]);
.b
13. }
14. }
15.}
m
Código 1.18 - ImpressoraComVarargs.java
co
1. class TesteImpressoraComVarargs {
2. public static void main(String[] args) {
3. ImpressoraComVarargs imp = new ImpressoraComVarargs();
4. imp.listaNomes("Marcos da Silva", "Roberto da Costa",
5.
6.
7.
8. }
"Ana Maria Soares"); g.
// método que recebe uma String e varargs do tipo double
imp.listaNotas("Lista de notas recebidas:", 7.5, 8.75, 5.5);
in
9. }
1. class CalculadoraMaluca {
2. int multiplicaValores(int numero1, int numero2) {
3. int resultado = numero1 * numero2;
4. numero1 = 0;
5. numero2 = 0;
r
6. return resultado;
7. }
.b
8. }
m
1. class TestePassagemPrimitivos {
2. public static void main(String[] args) {
co
3. int num1 = 4;
4. int num2 = 2;
5. CalculadoraMaluca calculadora = new CalculadoraMaluca();
6. System.out.println("O valor de num1 antes do metodo multiplica:" + num1);
7. System.out.println("O valor de num2 antes do metodo multiplica:" + num2);
8.
9.
10.
11.
g.
//o método multiplica atribui zero aos dois parâmetros recebidos
calculadora.multiplicaValores(num1, num2);
in
12. System.out.println("O valor de num1 apos o metodo:" + num1);
13. System.out.println("O valor de num2 apos o metodo:" + num2);
14. }
nn
15.}
As variáveis numero1 e numero2 armazenam uma cópia dos valores de num1 e num2, ou seja, depois da
execução do método multiplicaValores o valor das variáveis num1 e num2 continuam intactos, conforme se
.tr
Ao passar uma variável do tipo reference, uma cópia da referência para o objeto é gerada, portanto serão duas
variáveis armazenando referências para o mesmo objeto e as modificações feitas dentro do método serão
percebidas fora do método. Este comportamento não ocorre com variáveis primitivas, conforme observado no item
anterior.
r
.b
m
co
g.
Figura 1.1 - passagem de variáveis tipo reference
in
1. class TestePassagemObjetos {
nn
14. }
15. void alteraData(Data data2) {
16. data2.dia = 10;
w
20.}
1. class Foo {
2.
3. int i1 = 25;
4. int i2;
5. char c = 1;
6. boolean b;
r
7.
8. public static void main(String... args) {
.b
9. Foo foo = new Foo();
10. foo.method();
11. }
m
12.
13. void method() {
14. boolean b;
15. System.out.println(i2);
co
16. System.out.println(b);
17. }
18.}
r
.b
m
co
g.
in
nn
ai
Introdução a UML
.tr
r
O propósito dos diagramas é apresentar múltiplas visões de um sistema. O conjunto dessas múltiplas visões é
.b
chamado de modelo. É importante deixar claro que um modelo UML diz o que um sistema deve fazer, mas não
como implementá-lo.
m
A UML unifica os métodos de Grady Booch, James Rumbaugh e Ivar Jacobson.
co
Nos anos 80 e no começo dos anos 90 esses três pesquisadores trabalhavam em diferentes empresas, cada um
desenvolvendo sua própria metodologia para análise e projeto de softwares orientados a objetos. Na metade dos
anos 90 eles resolveram unir suas metodologias e criar uma nova , aproveitando o que havia de bom em cada
uma delas.
submetida ao OMG, que então adotou a UML como linguagem de modelagem de software padrão, e passou a
manter e produzir suas novas versões. Atualmente a UML está em sua versão 2.
ai
.tr
w
w
w
Uma metodologia define as etapas que devem ser seguidas para desenvolvimento de um sistema. Algumas
metodologias, como o RUP (Rational Unified Process), utilizam a UML como base para a modelagem. Quando
isso ocorre, a metodologia é responsável por indicar em qual fase do desenvolvimento cada diagrama deve ser
utilizado. Desta forma, a UML não define quando cada diagrama deve ser utilizado, e sim qual visão do sistema
r
ele oferece.
.b
Muitas vezes é inviável desenhar todos os diagramas para o projeto e, por isso, selecionam-se os diagramas mais
interessantes para cada caso. É importante conhecer o objetivo e o público alvo de cada diagrama, de forma a
m
criar diagramas úteis. Um mesmo tipo de diagrama pode ser criado com diferentes níveis de detalhe, dependendo
de quem deve lê-lo, isto é diagramas mais detalhados para um público mais técnico, e outros com menos detalhes
co
para um público de nível gerencial.
No próximo tópico serão comentados diagramas utilizados com freqüencia independentemente de sua
metodologia. g.
in
nn
ai
.tr
w
w
w
r
.b
m
co
g.
in
nn
ai
.tr
O diagrama de classes é utilizado para representar a estrutura do sistema, ou seja, as classes do sistema.
Normalmente este diagrama é utilizado durante todo o projeto. No início, na fase de análise de requisitos ele é
utilizado para fazer a modelagem conceitual do sistema e representar as entidades mais evidentes e o seu
w
Algumas variações do diagrama de classes são: o diagrama de pacotes e o diagrama de objetos. O primeiro
agrupa classes semelhantes em estruturas denominadas pacotes e o segundo representa instâncias de classes
(objetos) em um determinado instante no tempo.
r
.b
m
co
g.
in
nn
ai
.tr
r
.b
m
co
g.
in
nn
O diagrama de seqüência representa a parte dinâmica de um sistema. Demonstra quais os passos a seguir, em
uma linha do tempo, para que determinada tarefa seja executada. Em um diagrama mais detalhado, estes passos
.tr
podem ser chamadas a métodos de classes. Utiliza-se este diagrama para mostrar a arquitetura de um sistema,
design patterns implementados e interações entre classes.
w
w
w
r
.b
m
co
Diagrama 2.6 - exemplo de diagrama de componentes
r
.b
m
co
g.
in
nn
ai
.tr
Uma classe pode ser representada por uma caixa contendo apenas o nome
da classe, ou por uma caixa com atributos e métodos.
r
Diagrama 2.9 - Representação simples de classe
.b
2.3.1 Representando atributos
A representação de atributos de uma classe em UML é feita de forma diferente da que foi definida em Java, pois
m
declara-se primeiramente o nome do atributo e depois o tipo, separando-os pelo símbolo “:” (dois pontos).
co
Diagrama 2.10 - Representação de atributos
g.
in
2.3.2 Representando métodos
A representação de métodos de uma classe em UML é feita de forma diferente da que foi definida em Java, pois
nn
r
.b
m
co
g.
in
nn
ai
.tr
w
w
w
r
.b
m
co
g.
in
nn
ai
Encapsulamento
.tr
Getters e Setters
Modificadores de acesso
w
Objeto this
Encapsulamento de atributos compostos
Acoplamento
w
w
Para uma implementação mais robusta 1 desses atributos, o acesso deve ser mais controlado, ou seja, realizado
mediante métodos que permitam criar regras para atribuição e leitura. Esta prática é chamada encapsulamento de
r
atributos.
.b
Veja algumas vantagens que podem ser obtidas utilizando-se o encapsulamento:
m
• validar valores a serem escritos em cada atributo;
• definir quais e quando os atributos podem ser alterados;
co
• fazer um log das modificações dos atributos;
• oferecer acesso aos atributos através de métodos. Modificações futuras nos
atributos serão facilitadas, pois muitas vezes não será necessário modificar a
g.
assinatura do método que o encapsula.
in
O encapsulamento de atributos pode utilizar métodos padronizados. Contudo, quando existirem métodos
relacionados à lógica de negócio, ou ainda, que facilitem a manipulação de atributos, pode-se utilizar nomes que
não sigam esta convenção.
nn
1
robustez neste caso é um termo técnico que define o grau com o qual o sistema continua a se comportar
normalmente quando submetido a entradas inválidas, defeitos de software e hardware, etc.
Convenções
r
<modificadores> <tipoAtributo> get + <nomeAtributo> ( )
.b
Exemplos
String getNome()
m
long getRg()
co
São métodos que modificam o valor dos atributos. O uso destes métodos, em vez da escrita direta no atributo, é
indicado para protegê-los da escrita ou modificação indevida. É possível estabelecer certas normas e controles
dentro dos métodos Setters.
Convenções
g.
<modificadores> void set + <nomeAtributo> (<tipoAtributo> <nomeParametro>)
in
Exemplos
void setNome(String nome)
nn
3.1.3 Métodos is
ai
Convenções
.tr
boolean is + <nomeAtributo> ()
Exemplos
w
boolean isConnected()
boolean isOk()
w
Veja, a seguir, como encapsular três atributos da classe Produto. Para os atributos marca e descricao serão
utilizadas as formas padronizadas de encapsulamento (getters e setters) , mas para o preço serão usados
w
métodos de encapsulmento que agregam funcionalidade à classe. Exemplo: aumento de preço realizado através
do método aumentarPreco.
r
12. }
.b
13. void setDescricao(String novaDescricao) {
14. descricao = novaDescricao;
15. }
m
16. String getMarca() {
17. return marca;
18. }
co
19. void setMarca(String novaMarca) {
20. marca = novaMarca;
21. }
22. double getPreco() {
23. return preco;
24. }
25. void alterarPreco(double novoPreco) {
26. if (novoPreco > 0) {
g.
in
27. preco = novoPreco;
28. }
29. }
nn
39. }
40. void imprime() {
41. System.out.println("------------------------");
42. System.out.println("Produto : " + descricao);
w
46. }
47.}
w
r
.b
Código 3.2 - TesteProduto.java
m
co
g.
in
nn
ai
.tr
w
w
w
r
11. }
.b
12. int getDia() {
13. return dia;
14. }
15. void setDia(int diaNovo) {
m
16. dia = diaNovo;
17. }
18. int getMes() {
19. return mes;
co
20. }
21. void setMes(int mesNovo) {
22. mes = mesNovo;
23. }
24.
25.
26.
27.
boolean isAnoBissexto() { g.
if (((ano % 4 == 0) && !(ano % 100 == 0)) || (ano % 400 == 0))
return true;
in
28. else
29. return false;
30. }
31. void imprime() {
nn
r
10. System.out.println("O ano " + s + "e bissexto ");
.b
11. }
12.}
m
co
g.
in
Conforme demonstrado, em vez de se modificar os atributos diretamente, é possível fazê-lo através dos métodos
nn
de acesso aos atributos. No entanto, ainda não é possível evitar que desenvolvedores acessem os atributos
diretamente; para isso, é necessário utilizar modificadores de acesso.
ai
.tr
w
w
w
O conceito de modificadores de acesso visa controlar a acessibilidade dos elementos que compõem a classe.
Conseqüentemente, é possível definir por meio de modificadores, que apenas os métodos podem ser chamados,
r
restringindo o acesso direto aos atributos.
.b
3.2.1 Modificador public
O modificador public altera o elemento ao qual é aplicado, permitindo que seja acessado diretamente por qualquer
m
classe. Pode ser aplicado a classes, atributos e métodos.
co
Sintaxe
public <tipo> <identificador>
public <tipoRetorno> <identificadorMetodo> (<parametros>)
public class <nomeClasse>
6. }
7.}
Sintaxe
private <tipo> <identificador>
private <tipoRetorno> <identificadorMetodo> (<parametros>)
r
.b
m
co
g.
in
nn
Ocorre uma restrição de acesso denominada package ou default. Esta restrição de acesso
será abordada em mais detalhes no capítulo dedicado a pacotes. Por enquanto, basta
saber que ela é mais restritiva que public e menos que private.
r
3.2.3 Quando utilizar private ou public?
.b
• Utiliza-se o modificador de acesso public em atributos, quando for necessário que seus valores
possam ser manipulados diretamente por objetos de outras classes.
m
• Utiliza-se o modificador de acesso private para criar variáveis que possam ser acessadas somente
por métodos da própria classe, ou então acessados, via setters e getters, por outras classes.
co
Recomendação
g.
Recomenda-se que os atributos sejam protegidos, portanto eles devem ser private e
acessados via setters. Isso facilita posteriores modificações no código, assim como garante
in
a integridade do código. Haverá algum overhead no encapsulamento mas ele será MÍNIMO.
nn
ai
.tr
w
w
w
1. class Produto {
2. private double preco;
r
3. private String descricao;
.b
4. private String marca;
5. // ... demais métodos permanecem idênticos ao exemplo anterior de Produto
6.}
m
Código 3.6 - Produto.java modificada com encapsulamento de atributos
co
1. class TesteProduto {
2. public static void main(String[] args) {
3. Produto p = new Produto();
4. p.valoresIniciais(9.0, "Produto X", "Marca Y");
5. p.imprime();
6.
7.
8.
// aumento de 10% no preço do produto
p.preco *= 1.1;
g.
in
9. p.descricao = "Nova descricao";
10. p.marca = "Sem-marca";
11. }
12. }
nn
r
.b
1. class TesteData {
2.
m
3. public static void main(String[] args) {
4. Data d = new Data();
5. d.dia = -20;
6. d.mes = 23;
co
7. d.ano = 9;
8. d.imprime();
9. String s = d.isAnoBissexto() ? " " : " nao ";
10. System.out.println("O ano " + s + "e bissexto ");
11. }
12.} g.
Código 3.9 - TesteData.java com tentativa de acesso a atributos privados
in
nn
ai
.tr
w
w
w
Sintaxe
nomeVariavel.nomeAtributo
nomeVariavel.nomeDoMetodo(<parâmetros>)
r
.b
Qualquer instância de classe possui, por default, um objeto implícito denominado this, utilizado para o acesso
aos atributos e métodos da própria instância. Diz-se que o objeto é implícito porque não é declarado
m
explicitamente no código da classe, e sim acrescentado automaticamente.
Sintaxe
this.nomeAtributo
co
this.nomeDoMetodo(<parâmetros>)
O uso do objeto this não é obrigatório. O compilador utiliza implicitamente o objeto this para acessar um
g.
atributo ou um método da própria instância. Por exemplo: é definida uma classe Produto com um atributo de nome
marca; para que instâncias de Produto acessem o valor do atributo marca, utiliza-se explicitamente o objeto this
in
escrevendo-se this.marca, ou simplesmente marca.
nn
Uma situação especial ocorre quando há ambigüidade no nome de variáveis, ou seja, o mesmo nome pode
referenciar uma variável local ou um atributo. Neste caso, a JVM dá preferência a variável local e deve ser
utilizado explicitamente o objeto this caso se deseje referenciar o atributo, como demonstrado a seguir:
ai
8. this.marca = marca;
9. }
10. /*
11. * utilizando this, indica-se que o atributo marca da
w
15.}
r
.b
1. public class TesteProduto {
2. public static void main(String[] args) {
m
3. Produto p1 = new Produto();
4. ProdutoComBug p2 = new ProdutoComBug();
5. p1.setMarca("Brastemp");
6. p2.setMarca("Eletrolux");
co
7. System.out.println("Produto 1 = " + p1.getMarca());
8. System.out.println("Produto 2 = " + p2.getMarca());
9. }
10.}
g.
Código 3.12 - TesteProduto.java
in
nn
ai
Observa-se que na classe ProdutoComBug não é feita a atribuição do parâmetro passado para o atributo. Isto
acontece porque foi feita uma atribuição da variável local para ela mesma, e não para o atributo.
.tr
w
w
w
r
1. public class MauDepartamento {
.b
2.
3. private String nome;
4. private Pessoa[] pessoas;
m
5.
6. public void setNome(String nome) {
7. this.nome = nome;
8. }
co
9. public String getNome() {
10. return nome;
11. }
12. public void setPessoas(Pessoa[] pessoas) {
13. this.pessoas = pessoas;
14. }
15. public Pessoa[] getPessoas() {
16.
17. }
return pessoas;
g.
in
18. public void imprime() {
19. System.out.println("----------------------------------");
20. System.out.println("Departamento: " + this.getNome());
nn
26. }
27. } else
28. System.out.println("Nao ha pessoas neste departamento");
29. System.out.println("----------------------------------");
.tr
30. }
31.}
r
13.
.b
14. public String getRg() {
15. return rg;
16. }
17.
m
18. public void setRg(String rg) {
19. this.rg = rg;
20. }
21.}
co
Código 3.14 - Pessoa.java
g.
Essa implementação apresenta problemas e, portanto, não deve ser utilizada. Veja a seguir:
Nota: Também é possível marcar posições como null e, ao adicionar elementos antes de fazer o
r
redimensionamento, procurar as posições não preenchidas. Para isso, porém, é necessário fazer o controle
.b
dessas posições.
m
E para repetir estas operações mais de uma vez?
É preciso repetir todo o código tantas vezes quanto o número de repetições da operação,e isso é um absurdo!
co
Lembre-se que o encapsulamento não serve apenas para “proteger” ou “controlar” um atributo, mas também para
evitar que um mesmo trecho de código seja repetido muitas vezes, minimizando o esforço de manutenção.
g.
in
nn
ai
.tr
w
w
w
• void addPessoa(Pessoa p)
• Pessoa getPessoa(String rg)
• void removePessoa(Pessoa p)
Há também a opção de retornar um booleano (indicando a real remoção da Pessoa) ou o objeto removido.
r
.b
1. public class Departamento {
2.
3. private String nome;
4. private Pessoa[] pessoas;
m
5. private int numeroPosicoesLivres = 0;
6.
7. public void setNome(String nome) {
co
8. this.nome = nome;
9. }
10.
11. public String getNome() {
12. return nome;
13. }
14.
15. public void setPessoas (Pessoa[] pessoas) {
16. this.pessoas = pessoas;
g.
in
17. }
18.
19. public Pessoa[] getPessoas() {
nn
r
33. Pessoa[] novoArrayPessoas = new Pessoa[pessoas.length + 1];
.b
34. for (int i = 0; i < pessoas.length; i++) {
35. novoArrayPessoas[i] = pessoas[i];
36. }
37. novoArrayPessoas[novoArrayPessoas.length - 1] = pessoa;
m
38. pessoas = novoArrayPessoas;
39. }
40. }
41.
co
42. public Pessoa getPessoa(String rg) {
43. for (int i = 0;
44. (i < pessoas.length - numeroPosicoesLivres); i++) {
45. if (pessoas[i].getRg().equals(rg)) { return pessoas[i]; }
46.
47.
48.
49. }
} g.
// Se a pessoa com o rg passado não foi encontrada retornar null
return null;
in
50.
51. public Pessoa removePessoa(Pessoa p) {
52. String rgPessoa = p.getRg();
nn
53. int i = 0;
54.
55. while (i < pessoas.length - numeroPosicoesLivres) {
56. if (pessoas[i].getRg().equals(rgPessoa)) {
57. // Indicar que a pessoa foi removida trazendo o último elemento
ai
72. }
r
82. // e que não estão sendo impressas posições inválidas,
.b
83. // ou seja, maiores do que o array
84. while ((i < pessoas.length)) {
85. System.out.print("[" + i + "]");
86. if (pessoas[i] != null) {
m
87. System.out.print(pessoas[i].getNome());
88. System.out.println(" " + pessoas[i].getRg());
89. } else {
90. System.out.println("Posicao Livre");
co
91. }
92. i++;
93. }
94. } else {
95. System.out.println("Nao ha pessoas neste departamento");
96.
97.
98. }
} g.
System.out.println("----------------------------------");
in
99.
100.}
nn
r
11. Pessoa[] pessoasDpto = {p1, p2};
12.
.b
13. // Criação do departamento
14. Departamento d = new Departamento();
15. d.setNome("Departamento compras");
m
16. d.setPessoas(pessoasDpto);
17. System.out.println("Departamento recem criado");
18. d.imprime();
19.
co
20. Pessoa novaPessoa = new Pessoa();
21. novaPessoa.setNome("Nova Pessoa da Silva");
22. novaPessoa.setRg("33333");
23. d.addPessoa(novaPessoa);
24. d.imprime();
25.
26.
27.
28.
d.removePessoa(p1);
d.imprime();
g.
in
29. Pessoa p3 = new Pessoa();
30. p3.setNome("Raquel");
31. p3.setRg("33333");
nn
32. d.addPessoa(p3);
33. d.imprime();
34.
35. Pessoa p4 = new Pessoa();
36. p4.setNome("Gustavo");
ai
37. p4.setRg("44444");
38. d.addPessoa(p4);
39. d.imprime();
40.
.tr
48. System.out.println(procurada2.getNome());
49. else
50. System.out.println("Pessoa nao encontrada");
w
51. }
52.}
A classe MauDepartamento é um exemplo de acoplamento forte, pois para ser utilizada em uma aplicação o
r
membro interno array de pessoas deve ser bem conhecido para que se possa gerenciá-lo corretamente. Classes
.b
com um alto grau de acoplamento dificultam a manutenção do software. Se a forma de gerenciamento do array de
pessoas do departamento sofrer uma alteração, todas as aplicações que utilizam MauDepartamento deverão
m
ser modificadas, e em vários pontos.
co
Métodos para gerenciar o array de pessoas (addPessoa, getPessoa, removePessoa) são encontrados na
g.
classe Departamento. Isso facilita sua utilização pois as aplicações que usarem a classe Departamento não
necessitam conhecer seus detalhes internos. Esse é um exemplo de acoplamento fraco, porque se a forma de
in
gerenciamento do array de pessoas do departamento sofrer uma alteração apenas a classe Departamento será
modificada. As aplicações dependentes não sofrem nenhum impacto, considerando-se as mesmas operações de
gerenciamento.
nn
ai
.tr
w
w
w
r
.b
m
co
g.
in
nn
ai
Sobrecarga de métodos
.tr
Exemplos
Sobrecarga com tipos ambíguos
w
Varargs e sobrecarga
w
w
Veja o exemplo: em uma Calculadora existem somas com dois, três ou quantos argumentos forem necessários.
Além disso, há também somas de números de diversos tipos (int, long, float, byte, etc) e, portanto,
r
é necessário implementar diversos métodos que somem os diferentes parâmetros recebidos. Uma possível
.b
solução para este problema consiste em criar diversos métodos que somem os valores, utilizar como prenome
soma e gerar alguma distinção, como demonstrado a seguir:
m
int soma2(int x, int y);
int soma3(int x, int y, int z);
int somaN(int numeros[]);
co
Porém, criar vários nomes para métodos que fazem basicamente a mesma coisa não parece uma abordagem
muito interessante.
Overload = "Sobrecarga"
g.
A solução desse impasse chama-se sobrecarga de métodos e é uma funcionalidade pertinente a Orientação a
in
Objetos e que o Java implementa. Sobrecarregar um método é escrever métodos com nomes iguais, porém, com
quantidade e tipos de parâmetros de entradas diferentes.
nn
Importante
Em Java, é impossível definir métodos sobrecarregados cujo valor de retorno é o único alterado, pois isso
ai
impossibilita o compilador de identificar qual dos métodos deverá ser executado. Supondo que isso fosse
possível, para o exemplo de código a seguir, qual dos métodos
.tr
r
13. multiplica(100,350);
.b
14. }
15.}
m
Código 4.1 - SobrecargaImpossivel.java
Não é possível determinar qual método deveria ser executado; se é o que retorna um long ou o que retorna um
co
int. Por isso, o compilador gera um erro de método duplicado na tentativa de compilar a classe, conforme pode
ser observado na saída gerada pela tentativa de compilação da classe:
g.
in
nn
ai
.tr
w
w
w
r
8. }
.b
9. public float soma(float a, float b) {
10. return a + b;
11. }
m
12. public long soma(int[] numeros) {
13. long resultado = 0;
14. for (int i = 0; i < numeros.length; i++) {
15. resultado += numeros[i];
co
16. }
17. return resultado;
18. }
19.}
g.
Código 4.2 - Calculadora.java
in
1. public class TesteCalculadora {
2. public static void main(String[] args) {
3. Calculadora calc = new Calculadora();
nn
9.}
r
12. this.setDescricao(umaDescricao);
.b
13. }
14. public String getDescricao() {
15. return this.descricao;
16. }
m
17. public void setDescricao(String novaDescricao) {
18. this.descricao = novaDescricao;
19. }
20. public String getMarca() {
co
21. return this.marca;
22. }
23. public void setMarca(String novaMarca) {
24. this.marca = novaMarca;
25. }
26. public double getPreco() {
27.
28. }
return this.preco;
g.
in
29. public void alterarPreco(double novoPreco) {
30. if (novoPreco > 0) {
31. this.preco = novoPreco;
32. }
nn
33. }
34. public void aumentarPreco(double porcentagem) {
35. if (porcentagem > 0) {
36. System.out.println("aumentando o preco em " + porcentagem + " %");
37. porcentagem = 1 + (porcentagem / 100);
ai
45. System.out.println("------------------------");
46. System.out.println("Produto : " + this.getDescricao() + "\n");
47. System.out.println("Marca : " + this.getMarca() + "\n");
w
r
13.}
.b
Código 4.5 - TesteProduto.java
m
co
g.
in
nn
ai
Em algumas situações, são passados parâmetros que são compatíveis com a assinatura de vários métodos.
Nestes casos, é chamado o método que possua a lista mais específica possível em relação aos tipos dos
w
• Utilizar o método, se existir, cuja assinatura corresponde exatamente aos tipos dos parâmetros passados;
• Se não existir, converter os tipos dos parâmetros para o tipo imediatamente superior na hierarquia, até
w
r
11. System.out.println("Soma de double");
12. return d1 + d2;
.b
13. }
14.}
m
co
1. public class TesteCalculadoraAmbigua {
2. public static void main(String[] args) {
3. CalculadoraAmbigua calc = new CalculadoraAmbigua();
4. calc.soma(24, 25); //chama-se soma(long,long)
5. byte a = 24;
6.
7.
8.
9. }
byte b = 25;
calc.soma(a,b); //chama-se soma(byte,byte)
g.
calc.soma((byte)24, (byte)25); //chama-se soma(byte,byte)
in
10. }
Na linha 4 é feita uma chamada passando dois inteiros como parâmetros. Tanto o método soma(long,long)
quanto o método soma(double,double) podem receber os parâmetros, porém long está mais próximo de int
w
Importante
Existem casos nos quais não é possivel determinar o método a ser chamado. Neste caso é
gerado um erro de compilação. Por exemplo, se existirem os métodos soma (int,float) e soma
(float,int), e for feita a chamada soma (15, 30), não existe um método mais específico.
r
.b
m
4.3 Varargs e sobrecarga
co
Quando se utiliza varargs em um método indicando que um ou mais objetos do tipo determinado podem ser
passados como parâmetro, e se declara também um método mais específico, que receba somente um objeto
daquele tipo, pode ocorrer ambigüidade. Varargs são considerados como menos específicos que todos os demais
g.
casos, só sendo utilizados como última opção. Veja o exemplo a seguir, adicionando um método soma com
varargs à classe CalculadoraAmbigua:
in
1. public class CalculadoraAmbiguaVarargs {
nn
7. System.out.println("Soma de long");
8. return lg1 + lg2;
9. }
10. public double soma (double d1, double d2) {
.tr
21. }
22.}
r
.b
m
co
g.
in
nn
ai
.tr
w
w
w
1. What will happen when you attempt to compile and run the following code
r
4. Foo foo = new Foo();
.b
5. short s1 = 123;
6. short s2 = 456;
7. foo.method(s1,s2);
8. }
m
9.
10. public void method(int... params ) {
11. System.out.println("Inside method with varargs");
12. }
co
13.
14. public void method(int a, int b) {
15. System.out.println("Inside method with two int params");
16. }
17.
18.
19.
20.
public void method(int a, long b) {
}
System.out.println("Inside method with
g.
int and long params");
in
21.
22. public void method(long x, int y) {
23. System.out.println("Inside method with long and int params");
24. }
nn
25.}
A) Compilation succeeds and at runtime the message "Inside method with varargs" is written
ai
B) Compilation succeeds and at runtime the message "Inside method with two int params" is written
C) Compile time error because there is no method that accepts two short parameters: method (short, short)
D) Compile time error because there are duplicate methods
.tr
E) Compile time error because there are ambiguous methods : method(int,long) and method(long,int)
w
w
w
r
.b
m
co
g.
in
nn
ai
Construtores
.tr
Declarando construtores
Sobrecarga de construtores
w
r
Sintaxe para invocação de um construtor:
.b
<nomeClasse> <nomeVariavel> = new <nomeClasse>();
m
Toda classe tem, pelo menos, um construtor que o compilador adiciona, quando nenhum for
declarado (“construtor padrão”).
co
Neste exemplo da classe Curso não foi declarado explicitamente nenhum construtor.
7.
8. public void inicializaCurso(String nome, String desc,String cod,int carga) {
9. setNome(nome);
10. setCodigo(cod);
11. setCargaHoraria(carga);
ai
12. setDescricao(desc);
13. }
14. public void imprime() {
.tr
19. }
20. public int getCargaHoraria() {
21. return cargaHoraria;
w
22. }
23. public String getCodigo() {
24. return codigo;
25. }
w
r
38. public void setDescricao(String desc) {
.b
39. this.descricao = desc;
40. }
41. public void setNome(String nome) {
42. this.nome = nome;
m
43. }
44.}
co
1. public class TesteCurso {
2.
3. public static void main(String[] args) {
4.
5.
String nomeCurso = "Curso de tricot";
g.
String descricaoCurso = "Neste curso voce ira aprender tudo sobre tricot";
in
6. String codigo = "ct1";
7. int cargaHoraria = 40;
8. // Chamada ao construtor
nn
Note que não foi declarado explicitamente nenhum construtor na classe Curso, no entanto, foi possível utilizá-lo
.tr
na classe TesteCurso. Isso se deve a adição implícita do construtor default na classe Curso, e pode ser
confirmado utilizando-se o utilitário javap, que é distribuído juntamente com o JDK.
w
javap Curso
w
r
Construtores são rotinas especiais de inicialização, que devem seguir as seguintes regras:
.b
• ter obrigatoriamente o mesmo nome da classe em que são definidos;
• não possuir nenhum valor de retorno (nem mesmo void).
m
Em muitas situações não é desejável que sejam criados objetos sem valores iniciais. Para isso, freqüentemente
declaram-se construtores com parâmetros, que criam “regras” de inicialização de uma classe.
co
A inicialização dos atributos de uma classe poderia ser feita através de um método. No entanto, não há garantias
de que o método será chamado, e tampouco de que será chamado uma única vez.
g.
Na classe Curso, por exemplo, não é recomendável haver objetos criados sem o nome, descrição, código e
in
duração do curso. Diante disso, o método inicializaCurso pode ser substituído por um construtor, conforme
demonstrado a seguir:
nn
6.
7. public Curso(String nome, String descricao, String codigo, int cargaHoraria) {
8. setNome(nome);
.tr
9. setDescricao(descricao);
10. setCodigo(codigo);
11. setCargaHoraria(cargaHoraria);
12. }
w
• o primeiro erro acontece porque não há mais o construtor padrão; assim, sempre que se
declarar um construtor o compilador não adicionará o construtor padrão;
r
.b
m
• o segundo erro é bastante simples, visto que não há mais o método
inicializaCurso.
co
g.
Para eliminar estes erros de compilação é preciso incluir um construtor válido. Neste caso, o único construtor
válido é o construtor que recebe os parâmetros “(String, String, String, int)“ referentes ao conjunto de
in
atributos nome, descrição, código e carga horária do curso.
nn
ai
3.
4. String nomeCurso = "Curso de tricot";
5. String descricaoCurso = "Neste curso voce ira aprender tudo sobre tricot";
6. String codigo = "ct1";
w
10. curso1.imprime();
11. }
12.}
w
.b
m
co
g.
in
nn
ai
.tr
w
w
w
r
6. public void Curso(String nome,String descricao,String codigo,int cargaHoraria)
.b
7. {
8. setNome(nome);
9. setDescricao(descricao);
10. setCodigo(codigo);
m
11. setCargaHoraria(cargaHoraria);
12. }
13. // demais métodos idênticos ao exemplo anterior da classe Curso
14. ...
co
15.}
Código 5.5: Curso.java com construtor declarado incorretamente
r
8. // respectivamente nomeCurso, descricao, codigo, cargaHoraria
9. Curso curso2 = new Curso(cargaHoraria, nomeCurso, descricaoCurso,codigo);
.b
10. }
11.}
Código 5.7 - ExemploConstrutorIncorreto.java
m
co
g.
in
nn
ai
.tr
w
w
w
r
Construtores podem ser definidos de acordo com as regras de negócio de cada entidade. No exemplo da classe
.b
Curso, visto anteriormente, seria possível definir que um Curso também pode ser criado sem a descrição. Para
isso seria necessário definir dois construtores conforme se observa a seguir:
m
1. public class Curso {
2. private String nome;
co
3. private String descricao;
4. private String codigo;
5. private int cargaHoraria;
6. public Curso(String nome, String desc, String cod, int cargaHoraria) {
7. this.setNome(nome);
8.
9.
10.
this.setDescricao(desc);
this.setCodigo(cod);
this.setCargaHoraria(cargaHoraria);
g.
in
11. }
12. public Curso(String nome, String cod, int cargaHoraria) {
13. this.setNome(nome);
14. this.setCodigo(cod);
nn
15. this.setCargaHoraria(cargaHoraria);
16. }
17.
18. // demais métodos idênticos ao exemplo anterior da classe Curso
19. ...
ai
20.}
.tr
r
7. Curso(String nome, String cod, int cargaHoraria) {
.b
8. this.setNome(nome);
9. this.setCodigo(cod);
10. this.setCargaHoraria(cargaHoraria);
11.}
m
Código 5.9: Fragmento da classe Curso.java
co
No entanto, não existe nenhum reaproveitamento de código, e qualquer mudança necessária no construtor com
apenas três parâmetros deverá ser replicada no construtor mais completo e que contém quatro parâmetros.
g.
Na verdade, este é o mesmo problema encontrado na sobrecarga de métodos, e por isso será resolvido da
mesma forma, ou seja, através da utilização do objeto implícito this.
in
Ao chamar um método da própria classe utiliza-se a seguinte sintaxe:
this.nomeDoMetodo (<ZERO ou mais parametros>)
nn
Construtores são invocados de maneira diferente de métodos, portanto não é possível usar a mesma sintaxe. Para
chamar construtores da própria classe utiliza-se :
ai
Importante
É importante ressaltar que a chamada a um construtor da própria classe utilizando a referência implícita this só
w
pode ser feita a partir de outro construtor e deverá sempre ser sua primeira instrução, caso contrário haverá um
erro de compilação.
w
w
r
8. this.setCodigo(cod);
.b
9. this.setCargaHoraria(cargaHoraria);
10.}
m
co
g.
in
nn
ai
.tr
w
w
w
Sintaxe
{ conjunto de instruções }
r
.b
Veja um exemplo de uso, a seguir:
m
2. //declaração de atributos
3. private int atributo = 15;
4. //bloco inicializador
5. {
co
6. System.out.println("Dentro do inicializador de instância");
7. System.out.println("Valor do atributo = " + atributo);
8. }
9. //construtor
10. public ExemploInicializador() {
11.
12.
13.}
}
System.out.println("Dentro do construtor"); g.
in
Código 5.11: ExemploInicializador.java
nn
5.}
r
.b
Nos próximos capítulos serão apresentados novos passos para esta seqüência de inicialização, conforme forem
apresentados os tópicos de herança e o modificador static.
m
co
g.
in
nn
ai
.tr
w
w
w
r
modelo de programação é utilizado.
.b
5.4.1 Removendo um objeto da memória
m
Em Java não há destrutores, porque não é possível forçar a remoção de um objeto da memória, mas isso, na
verdade, é uma facilidade e não uma limitação.
co
A Virtual Machine Java assume a responsabilidade de remover os objetos indesejados da memória valendo-se do
mecanismo “Garbage Collector” (coletor de lixo). Basta que um objeto não seja referenciado por nenhuma variável
g.
do tipo reference e por nenhum outro objeto para que se torne um alvo da coleta de lixo da Virtual Machine.
in
Para descartar um objeto, o programador Java deve, no máximo, atribuir nulo (null) às variáveis reference que
apontam para este objeto, ou atribuir um outro objeto a essas variáveis.
nn
Um objeto não é necessariamente removido da memória pelo Garbage Collector no instante em que perde todas
as referências e se torna “descartável”. A especificação formal de uma Virtual Machine Java permite que o
Garbage Collector decida o melhor momento para a remoção dos objetos descartáveis da memória.
ai
.tr
w
w
w
Importante
Existem duas instruções que o programador pode utilizar para indicar à JVM que é um bom
momento para a coleta de lixo, porém a JVM não tem a obrigação de acatar estas sugestões:
• System.gc()
• Runtime.getRuntime().gc()
r
.b
Mesmo com o auxílio do Garbage Collector seu programa pode levar a JVM a emitir um erro grave
m
de falta de memória (OutOfMemoryError). Por exemplo: grandes arrays de objetos podem manter
muitos objetos inúteis indefinidamente referenciados, e portanto fora da “jurisdição” do Garbage
Collector (que só pode remover os objetos não referenciados). Quando sua aplicação precisar de
co
mais memória obrigará a JVM a negociar com o sistema operacional mas o sistema operacional,por
sua vez, pode se negar a ceder novas páginas de memória para o processo da JVM.
g.
in
Sem dúvida o Garbage Collector pode recuperar quantidades preciosas de memória para nossas aplicações Java
quando o programador libera as referências para os objetos inúteis. Porém a ação do Garbage Collector cobra um
nn
preço, diminuindo o desempenho das aplicações: loops que instanciam e rapidamente descartam objetos são
grandes inimigos do desempenho, pois mais cedo ou mais tarde vão dar trabalho para o Garbage Collector. Este é
o âmago da discussão clássica de utilização das classes String e StringBuffer.
ai
Quando o Garbage Collector decide entrar em funcionamento e encontra objetos “descartáveis” ele procura por
w
um método especial de finalização nestes objetos. Este método especial de finalização chama-se finalize.
w
Quando um objeto disponibiliza o método finalize o Garbage Collector executa-o imediatamente antes da
remoção deste objeto da memória. O programador pode então definir em sua classe um método finalize para
w
estabelecer um conjunto de instruções de finalização, para o Garbage Collector executar nos objetos
“descartáveis” desta classe.
r
6. inteiros = new int[tamanho];
7. }
.b
8.
9. public void finalize() {
10. System.out.println("Removendo objeto " + id + " da memoria");
11. }
m
12.}
co
1. public class TesteListaInteiros {
2.
3.
4.
public static void main(String[] args) {
g.
//Faz a leitura do tamanho da lista de inteiros a ser criada
//Esta valor deve ser passado como parâmetro na execução do código.
in
5. int tamanhoLista = Integer.parseInt(args[0]);
6. for(int i = 0; i < 5; i++) {
7. ListaInteiros lista = new ListaInteiros(i,tamanhoLista);
8. }
nn
9. System.out.println("Finalizando programa");
10. }
11.}
A classe TesteListaInteiros cria várias instâncias de ListaInteiros dentro de um laço e armazena suas
.tr
referências na variável local lista. Como a cada iteração um novo valor é associado à variável lista, o valor
anterior não é mais utilizado e pode ser descartado pelo Garbage Collector.
w
w
w
m
coleta de lixo antes que fosse possivel a criação de novos objetos. Não há como determinar com exatidão o
momento em que irá ocorrer uma coleta de lixo, porém se o sistema precisar alocar mais memória do ele tem
co
disponível, tentará primeiro descartar os objetos não utilizados(coleta de lixo) antes de reclamar de falta de
memória.
g.
A saída gerada não será a mesma em todos os computadores, pois dependerá da configuração de memória
disponível para a JVM além do tamanho dos objetos criados. Com objetos pequenos, pode não ser necessária a
realização da coleta de lixo antes do término do programa. Observe o resultado gerado quando a classe
in
TesteListaInteiros é alterada para gerar listas de inteiros com 10 elemento em vez de 1.000.000.
nn
ai
.tr
Neste caso, como a lista de inteiros era pequena, não ocorreu nenhuma coleta de lixo, pois não foi necessário
w
Você pode invocar diretamente o método finalize, do mesmo modo que poderia fazer com qualquer
método disponível em um objeto. Lembre-se porém que invocar o método finalize diretamente
w
1. What is the output of the following code when compiled and run? Select all the
correct answers.
r
3. {
4. System.out.println("inside instance initializer");
.b
5. }
6.
7. Foo(byte b) {
m
8. System.out.println("inside constructor with byte");
9. }
10.
11. Foo(short s) {
co
12. System.out.println("inside constructor with short");
13. }
14.
15. Foo(char c) {
16. System.out.println("inside constructor with char");
17.
18.
19.
20.
}
Foo(int i) {
System.out.println("inside
g.
constructor with int");
in
21. }
22.
23. public static void main(String[] args) {
nn
r
.b
m
co
g.
in
nn
ai
Modificador static
.tr
Atributos estáticos
Métodos estáticos
w
r
2. public static void main(String[] args) {
.b
3. Curso curso1 = new Curso("Academia Java","AJ",128);
4. System.out.println("O curso " + curso1.getNome()
5. + " tem a carga horaria de " + curso1.getCargaHoraria()
6. + " horas");
m
7. Curso curso2 = new Curso("Academia Web","AW", 124);
8. System.out.println("O curso " + curso2.getNome()
9. + " tem a carga horaria de " + curso2.getCargaHoraria()
10. + " horas");
co
11. }
12.}
Note que cada objeto curso tem os seus próprios valores para os atributos nome e carga horária. Este é o tipo de
.tr
cenário mais comum em sistemas orientados a objetos, mas existem casos nos quais um valor global é desejado.
Esse valor global não é específico de cada objeto, mas pode ser compartilhado entre os objetos e demais
w
elementos de um aplicativo.
w
A classe TesteCurso ilustra um valor global deste tipo. Observe o uso da instrução System.out.println . Até
agora, esta instrução foi utilizada em diversos exemplos sem maiores detalhes, pois foi necessária a introdução de
w
diversos conceitos para o seu correto entendimento. Agora é o momento propício para uma análise mais profunda.
Nos exemplos apresentados até agora, o método println foi utilizado para escrever texto em uma console do
monitor. Para isso, é necessário criar um objeto que represente essa console e invocar o método . Analise o
exemplo da classe TesteCurso2: nada disso foi feito e, no entanto, a mensagem foi escrita corretamente para o
r
monitor. Isso ocorre porque existe um atributo chamado out na classe System, que é inicializado automaticamente
.b
para representar o monitor. Este atributo funciona como uma variável global representando o monitor. A criação
deste tipo de atributos globais é feita utilizando-se o modificador static.
m
Perceba também que não foi necessária a criação de uma instância de System para acessar o atributo out. Essa
é outra característica dos elementos estáticos: eles ficam associados a uma determinada classe, e não a objetos
co
específicos da classe.
g.
in
nn
ai
.tr
w
w
w
*outros modificadores podem ser aplicados juntamente ao modificar static, como por exemplo,
modificadores de acesso public ou private. A ordem dos modificadores não é importante ,portanto pode
r
haver um atributo private static ou um static private.
.b
Outras características importantes de atributos estáticos são:
•
m
um atributo estático fica associado a uma classe e tem um valor único para toda a aplicação;
• atributos estáticos são inicializados com os mesmos valores de inicialização de atributos não
estáticos. Por exemplo:, um atributo do tipo int será inicializado para o valor 0;
co
• os atributos estáticos podem ser acessados diretamente a partir da classe e são alocados na
memória na primeira vez em que a classe for referenciada. Não é preciso, portanto, criar uma
instância;
•
• Sintaxe: NomeClasse.nomeAtributo
g.
o exemplo mais comum de atributos estáticos é o atributo out da classe System, utilizado no
in
System.out.println para impressão de dados na console. Lembre-se de que nunca foi
necessário criar uma instância de objeto da classe System para utilizar seu atributo out e,
nn
Atributos estáticos devem ser utilizados quando for necessário compartilhar um valor entre todas as instâncias da
classe, desta forma a variável será alocada apenas uma vez na memória.
w
• placa: cada Carro tem sua própria placa, que portanto é uma característica da instância;
• velocidade: cada Carro pode se movimentar desenvolvendo sua própria velocidade, o que
w
r
6.
7. public Carro(String fabricante, String placa, int velocidade) {
.b
8. this.fabricante = fabricante;
9. this.placa = placa;
10. this.velocidade = velocidade;
m
11. }
12.
13. public String getFabricante() {
14. return fabricante;
co
15. }
16.
17. public void setFabricante(String fabricante) {
18. this.fabricante = fabricante;
19. }
20.
21. public String getPlaca() {
22. return placa;
g.
23. }
in
24.
25. public void setPlaca(String placa) {
26. this.placa = placa;
nn
27. }
28.
29. public int getVelocidade() {
30. return velocidade;
31. }
ai
32.
33. public void setVelocidade(int velocidade) {
34. this.velocidade = velocidade;
.tr
35. }
36.
37. public void acelerar(int deltaV) {
38. velocidade += deltaV;
w
39. }
40.
41. public boolean ultrapassouLimite() {
42. return velocidade > velocidadeMaximaPermitida;
w
43. }
44.}
w
r
9.
10. c1.acelerar(16);
.b
11. c2.acelerar(30);
12.
13. System.out.print("Carro " + c1.getPlaca() +" ultrapassou o limite ? ");
14. System.out.println(c1.ultrapassouLimite());
m
15. System.out.print("Carro " + c2.getPlaca() +" ultrapassou o limite ? ");
16. System.out.println(c2.ultrapassouLimite());
17.
co
18. Carro.velocidadeMaximaPermitida = 70;
19. System.out.print("\nVelocidade maxima permitida: ");
20. System.out.println(Carro.velocidadeMaximaPermitida);
21.
22. System.out.print("Carro " + c1.getPlaca() +" ultrapassou o limite ? ");
23.
24.
25.
26. }
System.out.println(c1.ultrapassouLimite()); g.
System.out.print("Carro " + c2.getPlaca() +" ultrapassou
System.out.println(c2.ultrapassouLimite());
o limite ? ");
in
27.}
Carro.velocidadeMaximaPermitida: 60
r
.b
Carro c1 Carro c2
fabricante: GM fabricante: Volks
m
placa: IBM9876 placa: SUN3344
velocidade: 50 velocidade: 42
co
Figura 6.1 - representação dos atributos com velocidade máxima de 60
g.
Na primeira vez que a classe Carro for referenciada, o atributo velocidadeMaximaPermitida será criado,
não sendo necessário defini-lo a cada instância de classe.
in
Após invocar o método acelerar nos dois carros e alterar o atributo estático velocidadeMaximaPermitida eis a
nova situação:
nn
Carro.velocidadeMaximaPermitida: 70
.tr
Carro c1 Carro c2
w
velocidade: 66 velocidade: 72
w
Não utilizar atributos estáticos para representar características da instância. Veja como foi criada uma classe
Carro com um atributo estático placa; assim, placa passa a ser um atributo compartilhado por todas as
instâncias da classe Carro.
r
2. private String fabricante;
3. private static String placa;
.b
4. private int velocidade;
5. public static int velocidadeMaximaPermitida = 60;
6. // demais métodos idênticos ao exemplo anterior da classe Carro
7.}
m
Código 6.4 - Carro.java com placa estática
co
Compreenda melhor o efeito deste “compartilhamento” realizando o mesmo teste: TesteCarro
g.
in
nn
ai
.tr
Obs: Perceba que a placa do carro é a mesma para todos os objetos da classe Carro. Quando o valor da
placa é alterado para qualquer uma das instâncias da classe Carro, as demais são afetadas porque a variável
w
Carro.velocidadeMaximaPermitida: 70
Carro.placa: SUN3344
r
Carro c1 Carro c2
.b
fabricante: GM fabricante: Volks
velocidade: 66 velocidade: 72
m
Figura 6.3 - representação dos atributos com placa estática
co
6.2 Métodos estáticos
O modificador static também pode ser aplicado a métodos. De maneira semelhante ao que ocorre para
g.
atributos, o uso do modificador static em métodos faz sua associação à classe em vez de fazê-lo à instância. A
maior parte dos métodos vistos até agora atuam sobre atributos de um objeto, mas este não é sempre o caso:
in
métodos que dependem apenas dos parâmetros recebidos e não atuam sobre atributos do objeto, normalmente
são métodos utilitários e podem ser transformados em métodos estáticos.
nn
Verifica-se exemplos de uso de métodos estáticos em Java, em classes utilitárias como a classe Math. Esta classe
possui diversos métodos para operações matemáticas como cálculo de raiz quadrada, funções trigonométricas,
logaritmos, arredondamento de números, geração de números aleatórios e outras. Veja um exemplo de sua
ai
utilização a seguir:
.tr
7. }
8.}
w
m
Outro exemplo de uso de métodos estáticos em Java ocorre com o método System.gc(), apresentado no
capítulo anterior, que sugere a execução da coleta de lixo pela JVM. O exemplo mais comum de método estático é
co
o main, porque a JVM não instancia um objeto da classe indicada para execução mas apenas chama seu método
main.
static private.
Um possível candidato ao uso de método estáticos é a classe Calculadora, apresentada no capítulo que abordou
ai
sobrecarga. Essa classe possui métodos que dependem apenas dos valores recebidos como parâmetros e não
manipula nenhum atributo, logo não existe a necessidade de criar várias instâncias da classe para executar
.tr
contas. Caso as várias operações dependessem do resultado de operações anteriores, o cenário seria diferente.
r
13. }
.b
14.
15. public static long soma(int[] numeros) {
16. long resultado = 0;
17. for (int i = 0; i < numeros.length; i++) {
m
18. resultado += numeros[i];
19. }
20. return resultado;
co
21. }
22.}
Código 6.6 - CalculadoraStatic.java
Apesar do acesso a atributos e métodos estáticos ser feito a partir do nome da classe e não ser necessária a
criação de objetos, este acesso também pode ser feito a partir de instâncias. Isto pode ser visto no exemplo a
seguir:
w
w
w
r
.b
1. public class TesteLogger {
2. public static void main(String[] args) {
3. Logger.logIt("Logger");
m
4. Logger log1 = new Logger();
5. log1.logIt("log1");
6. Logger log2 = new Logger();
7. log2.logIt("log2");
co
8. Logger.logIt("Logger");
9. }
10.}
g.
Código 6.9 - TesteLogger.java
in
nn
ai
Obs: observe que o valor de qtdLogs está sendo incrementado, mesmo quando se utilizam diferentes instâncias
.tr
de Logger para fazer as chamadas ao método logIt(). Isto acontece porque este atributo é estático e,
portanto, compartilhado por todas as instâncias da classe.
w
w
w
1. Um método estático pode acessar somente membros estáticos porque, como já foi visto, pode ser
chamado sem a necessidade de criação de uma instância da classe. Membros não estáticos são criados
somente na construção da instância.
r
2.
3. private String fabricante;
.b
4. private String placa;
5. private int velocidade;
6. private static int velocidadeMaximaPermitida = 60;
7.
m
8. public Carro(String fabricante, String placa, int velocidade) {
9. this.fabricante = fabricante;
10. this.placa = placa;
co
11. this.velocidade = velocidade;
12. }
13.
14. public static int getVelocidadeMaximaPermitida() {
15. return velocidadeMaximaPermitida;
16.
17.
18.
19.
}
23. }
24.
25. public int getVelocidade() {
26. return velocidade;
27. }
ai
28.
29. public void setVelocidade(int velocidade) {
30. this.velocidade = velocidade;
31. }
.tr
32.
33. // demais getters e setters omitidos
34.}
w
Código 6.10 - Carro.java com métodos estáticos tentando acessar membros não estáticos
w
w
m
métodos estáticos.
co
1. public class Carro {
2.
3. private String fabricante;
4. private String placa;
5. private int velocidade;
6.
7.
8.
private static int velocidadeMaximaPermitida = 60; g.
public Carro(String fabricante, String placa, int velocidade) {
in
9. this.fabricante = fabricante;
10. this.placa = placa;
11. this.velocidade = velocidade;
12. }
nn
13.
14. public static int getVelocidadeMaximaPermitida() {
15. return velocidadeMaximaPermitida;
16. }
17.
ai
21. this.setVelocidade(velocidadeMaximaPermitida);
22. }
23. }
24. public int getVelocidade() {
w
29. }
30. // demais getters e setters omitidos
31.}
w
inicialização na declaração:
private int codigo = 135;
r
inicialização com construtor:
.b
public Cliente(int codigo) {
this.codigo = codigo;
}
m
bloco de inicialização:
co
{
codigo = 135;
}
g.
Para atributos estáticos, há como opções: a inicialização na declaração, que ocorre de maneira idêntica a
inicialização de atributos não estáticos; e a inicialização com blocos de inicialização estáticos.
in
Sintaxe
nn
16. }
17.}
1. na execução do código pela JVM uma classe é utilizada em alguma instrução. Esta utilização pode ser, por
exemplo: uma chamada a um método estático, acesso a um atributo estático ou chamada ao construtor da
classe;.
2. para executar a instrução, a JVM verifica se a classe já se encontra carregada em memória. Caso contrário é
r
necessário encontrar o arquivo .class correspondente e carregá-lo. Após carregar a classe, os seguintes
.b
passos são executados:
m
2.2. execução do bloco de inicialização estático.
co
3. execução da instrução solicitada.
• um bloco de inicialização estático é executado somente uma vez: imediatamente após a primeira
referência à classe, isto é, no seu carregamento;
r
• como o bloco de inicialização estático é executado no carregamento da classe, conseqüentemente
.b
será executado antes da chamada ao construtor da classe;
• dentro de um bloco de inicialização estático são acessados somente atributos e métodos estáticos;
m
• blocos de inicialização estáticos não podem lançar exceções checked (este tópico será abordado
co
posteriomente);
• é possivel criar mais de um bloco de inicialização estático. Neste caso, os blocos serão executadas
na ordem em que aparecem no código da classe. g.
in
nn
ai
.tr
w
w
w
r
.b
m
co
g.
Diagrama 6.1 - representação do modificador static na UML
in
nn
ai
.tr
w
w
w
1. Consider the following piece of code, and select all the statements that are true.
r
7. }
.b
8.
9. public static void main(String[] args) {
10. Foo foo = new Foo();
11. foo.method();
m
12. }
13.}
co
A) The code compiles and displays "Mach5".
B) The code compiles, but throws an exception at runtime, because variable “version” isn't declared as
static.
g.
C) The code fails to compile because you can't make a static reference to a non-static variable.
D) The code will compile and display "Mach5" if “version” is declared to be static.
in
E) The code will compile by removing the static keyword from the declaration of method().
nn
ai
.tr
w
w
w
r
.b
m
co
g.
in
nn
ai
Associação
.tr
Navegabilidade
Restrições
Associação reflexiva
w
Agregação
Composição
w
Dependência
Classe Associativa
Estudo de Caso
r
7.1 Representação de associações na UML
.b
Uma associação é representada na UML com uma reta que liga duas classes. Além disto a UML
permite representar também as seguintes características de uma associação:
m
• nome: Nome da associação;
co
• sentido: Sentido de leitura da associação;
• cardinalidade / multiplicidade: um para um, um para muitos, muitos para muitos, etc;
• papéis: Os papéis da associação, ou seja, qual o papel na associação de cada elemento que
•
pertence a associação; g.
navegabilidade: Se uma das partes pode navegar pela outra parte e vice-versa;
•
in
restrições;
• tipo: associação, agregação e composição.
nn
Nome e Sentido:
ai
.tr
w
w
1 Obrigatoriamente 1
0..1 Zero ou um
1..* Um ou mais
0..* Zero ou mais
r
* Zero ou mais
.b
Obs: Quando os valores para a cardinalidade não estão definidos, fica implícito que o valor da associação é 1.
m
Exemplo de associação 1 para 1:
O diagrama abaixo pode ser lido da seguinte forma: uma Pessoa tem um Endereco, e o Endereco pertence a
co
uma Pessoa.
g.
Diagrama 7.2 - associação 1 para 1
in
O diagrama acima mostra que a classe Pessoa tem um atributo do tipo Endereco, e a classe Endereco tem um
nn
A partir do seguinte diagrama conclui-se que uma Empresa pode ter um ou mais Departamentos, e cada
Departamento pertence a apenas uma Empresa.
.tr
w
No mesmo diagrama verifica-se que, na implementação, a classe Empresa deve ter uma coleção de
w
departamentos.
r
.b
Diagrama 7.4 - associação muitos para muitos
m
Papéis
O nome do papel explica como a classe participa do relacionamento.
co
Este nome pode ser utilizado para geração de código, definindo o nome do atributo que será gerado.
g.
in
Diagrama 7.5 - papéis da associação
nn
O diagrama acima representa uma Empresa que tem um ou mais Departamentos, ou seja, na classe Empresa
há um coletivo de departamentos e na classe Departamento há uma Empresa. Observe o seguinte código:
ai
3. }
Curso.java
public class Curso {
private Estudante[] estudantesInscritos;
}
r
.b
Estudante.java
public class Estudante {
m
private Curso[] cursos;
private Endereco endereco;
}
co
Endereço.java
public class Endereco {
}
private Estudante estudante;
g.
in
Diagrama 7.6 - exemplo de cenário de escola
Observações:
nn
Note que o nome do atributo colocado na classe é igual ao nome utilizado na associação no diagrama UML.
No caso de relacionamentos “um-para-muitos” ou “muitos-para-muitos”, utiliza-se um array para representar a
coleção de objetos; no entanto, outras ferramentas de modelagem UML podem utilizar Collections 2, como por
ai
Muitas vezes é interessante que uma classe tenha referência para outra e vice-versa, mas em outras
circunstâncias não é necessário, como no caso da classe Endereco. Será que realmente é necessário que cada
Endereco tenha uma referência para o Estudante?
w
Por isto existe o conceito de navegabilidade: quando não é definida explicitamente, a navegabilidade é bilateral,
w
isto é, ambos os lados da associação tem uma referência para o outro. Quando não se deseja que a
navegabilidade seja bilateral utiliza-se no diagrama uma seta contínua, indicando qual parte da associação tem
w
navegabilidade:o lado que não tiver seta será considerado sem navegabilidade.
2
Collections são classes que representam estruturas de dados em Java.
Curso.java
public class Curso {
private Estudante[] estudantesInscritos;
r
}
.b
Estudante.java
public class Estudante {
m
private Curso[] cursos;
private Endereco endereco;
}
co
Endereço.java
public class Endereco {
}
g.
Diagrama 7.7 - exemplo de cenário de escola modificado
in
A grande vantagem desta abordagem com navegabilidade unilateral é que a classe Endereco se torna mais
reutilizável. No exemplo a seguir com outro cenário de aplicativo, é necessária a classe Endereco, mas não a
nn
Estudante.
ai
Empresa.java
public class Empresa {
private Endereco endereco;
.tr
Endereço.java
w
Pode-se utilizar a mesma classe Endereco, nos dois sistemas, o que não seria possivel se Endereco tivesse
uma associação bidirecional com Estudante.
As restrições são representadas por chaves e devem ser separadas por vírgulas, no caso da existência de mais
de uma restrição:
r
.b
m
Diagrama 7.9 - restrição de pagamento
Ordenação
co
Quando há um relacionamento em que pelo menos uma das partes da associação tem multiplicidade maior que
um, é possível definir se os elementos devem ser mantidos em ordem ou não. Para que a ordenação seja levada
em consideração utiliza-se o símbolo {ordered}, que é uma restrição. Quando a ordenação não é necessária
simplesmente omite-se esse símbolo. g.
in
nn
Uma associação reflexiva é definida quando uma classe contém uma ou mais instâncias de objetos da própria
classe.
.tr
r
Neste exemplo, a Turma é o todo, enquanto os Estudantes e o
.b
Instrutor são partes do todo.
m
Diagrama 7.12 - agregação
co
A leitura do diagrama apresentado traz as seguintes informações:
• a classe Turma tem referência para todos os Estudantes inscritos na Turma;
•
• a Turma tem referência para
g.
cada Estudante tem referência para a Turma na qual está matriculado;
o Instrutor que irá ministrá-la;
•
in
cada Instrutor tem uma referência para a sua Turma;
• há informações sobre a cardinalidade de cada associação: cada Turma possui zero
ou mais Estudantes, e cada Estudante pertence apenas a uma Turma. Da mesma
nn
forma, cada Turma tem um Instrutor, e cada Instrutor é responsável por uma
Turma.
ai
Estudantes e o Instrutor. Visto de outra forma, os Estudantes e o Instrutor fazem parte da turma.
w
A principal diferença entre uma agregação e uma associação está em que a agregação é um
relacionamento de um todo com suas partes. No exemplo, os estudantes são parte de uma
w
turma (agregação), mas não faz sentido dizer que uma pessoa é parte do endereço ou vice-
versa (associação). Na dúvida,utilize a associação.
w
r
associação que representa o todo.
.b
Diagrama 7.13 - composição
m
A leitura do diagrama apresentado traz as seguintes informações:
• a classe Escola tem referência para todos os Departamentos da Escola;
co
• o Departamento tem uma referência para a Escola a qual pertence;
• o Instrutor tem referência para o seu TimeCard;
• cada TimeCard tem referência para o Instrutor o qual pertence;
•
•
•
g.
cada Escola tem um ou mais Departamentos, mas cada Departamento pertence a apenas uma Escola;
cada Instrutor tem um TimeCard, e cada TimeCard pertence a apenas um Instrutor;
se o objeto da classe Escola for destruído, todos os Departamentos devem ser excluídos também. O
in
mesmo acontece com o Instrutor e seu TimeCard, ou seja, o Departamento não faz sentido sem a Escola,
e o TimeCard não faz sentido sem o Instrutor.
nn
No exemplo utilizado, a classe GeradorRelatorios depende da classe Turma, que é recebida como
r
parâmetro no método imprimirTurma; portanto, se a classe Turma for removida a GeradorRelatorios não
.b
irá mais funcionar, ou então, alterações realizadas na Turma podem afetar a GeradorRelatorios.
m
co
Diagrama 7.14 - dependência
g.
in
nn
ai
.tr
w
w
w
r
.b
m
co
Diagrama 7.15 - classe associativa
Estudante.java
class Estudante {
private Matricula[] matriculas;
g.
in
}
nn
Turma.java
class Turma {
private Matricula[] matriculas;
ai
}
.tr
Matricula.java
class Matricula {
private Estudante estudante;
w
r
• um escritório pode ser representado por um endereço e um CNPJ;
.b
• cada pessoa pode ser representada por um nome, um rg e um endereço.
Nota: Cada uma destas entidades (Empresa, Departamento, Pessoa, Endereco, Escritorio) têm muitas
m
outras características que podem ser utilizadas para representá-las; no entanto, serão utilizadas somente aquelas
consideradas essenciais para o entendimento do assunto.
co
Veja as representações em UML e em código Java necessárias para atender os requisitos identificados acima.
Para efeito de simplificação do código gerado, todos os métodos de encapsulamento (getters e setters), bem como
os construtores foram eliminados do código e do diagrama. g.
in
nn
ai
.tr
r
1. class Departamento {
.b
2. private String nome;
3. private Pessoa pessoas[];
4.}
m
Código 7.4 - Departamento.java
co
1. class Escritorio {
2. private Endereco endereco;
3.
4.}
private String cnpj; g.
in
Código 7.5 - Escritorio.java
nn
1. class Pessoa {
2. private Endereco endereco;
3. private String nome;
ai
1. class Endereco {
2. private String rua;
3. private int numero;
w
4.}
w
Objetivo:
Praticar o desenvolvimento de diagramas de classes.
Tabela de atividades:
Atividade OK
r
1. Criar um diagrama de classes para modelar um sistema de compras que atenda os seguintes
.b
requisitos:
O usuário pode visualizar um catálogo com todos os produtos disponíveis.
m
O Catálogo deve exibir as seguintes informações do produto:
• Nome
• Descrição
co
• Preço
• Imagem
• Código (com letras e números)
g.
O usuário pode adicionar ou remover produtos de sua lista de compras.
O usuário pode alterar a quantidade de um determinado produto de sua lista.
in
O valor de venda deverá ser recalculado à medida que forem feitas alterações na lista de compras..
nn
ai
.tr
w
w
w
r
.b
m
co
g.
in
nn
ai
Herança
.tr
Exemplos
Herança e modificador private
w
Modificador protected
Referência implícita super
w
Construtores X Herança
Sobrescrita de métodos
Modificador final
class Pessoa {}
A princípio, a classe Pessoa parece uma classe vazia, sem funcionalidade e sem atributos, ou seja, uma classe
r
inútil. Todavia, já foi visto que o compilador adiciona o construtor padrão, portanto, mesmo sem nenhum atributo
.b
ou método já é possível criar uma instância da classe Pessoa.
Além disto, o compilador também garante que todas as classes contenham um mínimo de funcionalidades,
m
definidas na classe Object. Por isto, a classe Object é chamada de superclasse de todas as classes, porque,
com certeza, todas são derivadas dela.
co
Utilizando-se novamente o utilitário javap observe que, na verdade, a declaração da classe Pessoa é feita da
seguinte forma:
g.
in
nn
ai
.tr
A palavra chave extends faz com que a classe Pessoa herde todos os atributos e métodos definidos na classe
Object, ou seja, possua todos os métodos e atributos da superclasse .
w
Este é o conceito de herança, simplificadamente considerado como a capacidade que uma classe tem de herdar
w
atributos e métodos de outras classes. Assim como o relacionamento de associação também é conhecido como
relacionamento "tem um(a)", a herança entre classes também é denominada relacionamento do tipo "é um(a)",
w
Importante:
Java implementa somente herança simples, o que significa que cada classe pode estender
somente uma classe (em C++ é possível herdar várias classes ao mesmo tempo e essa
r
capacidade é denominada herança múltipla).
.b
8.1 Representação de herança na UML
m
A herança é representada na UML (Unified Modeling Language) da seguinte forma:
co
g.
in
Diagrama 8.1 - representação de herança
nn
A seta contínua indica que uma classe estende outra. O exemplo mostra que a classe Pessoa estende a classe
Object, herdando todos os seus métodos e atributos.
ai
Não é necessário que a classe filha esteja abaixo da classe pai, porque freqüentemente os diagramas contém um
grande número de classes, e nem sempre é possível manter essa forma.
.tr
w
r
5. }
.b
6.}
m
1. public class CalculadoraPlus extends Calculadora {
2. public double multiplicacao(int num1, int num2) {
3. double resultado = num1 * num2;
4. return resultado;
co
5. }
6.}
r
.b
m
co
g.
in
nn
ai
.tr
w
r
11. this.nome = nome;
12. }
.b
13. public String getNome() {
14. return nome;
15. }
16. public void setRg(String rg) {
m
17. this.rg = rg;
18. }
19. public String getRg() {
co
20. return rg;
21. }
22. public void imprime() {
23. System.out.println("Nome: " + this.getNome());
24. System.out.println("RG: " + this.getRg());
25.
26.
27. }
28.}
g.
System.out.print("Endereco: " + this.getEndereco().getRua());
System.out.println(" , " + this.getEndereco().getNumero());
in
Código 8.4 - Pessoa.java
nn
5. this.setRua(rua);
6. this.setNumero(numero);
7. }
8. public int getNumero() {
.tr
9. return numero;
10. }
11. public String getRua() {
12. return rua;
w
13. }
14. public void setNumero(int numero) {
15. this.numero = numero;
w
16. }
17. public void setRua(String rua) {
18. this.rua = rua;
w
19. }
20.}
r
6.
7. public void setCarteiraProfissional(long carteira) {
.b
8. this.carteiraProfissional = carteira;
9. }
10.
11. public long getCarteiraProfissional() {
m
12. return this.carteiraProfissional;
13. }
14.
co
15. public void setSalario(long salario) {
16. this.salario = salario;
17. }
18.
19. public double getSalario() {
20.
21. }
22.
return this.salario;
Para compreender melhor os conceitos de herança, foi criada a classe TesteFuncionario, que utilizará
métodos e atributos herdados implicitamente pela classe Funcionario.
w
w
w
r
11. // atribuindo as características herdadas da classe Pessoa
.b
12. f.setNome("Pedro da Silva");
13. f.setEndereco(end1);
14. f.setRg("1234545");
15.
m
16. // atribuindo as características específicas de funcionários
17. f.setSalario(10000);
18. f.setCarteiraProfissional(123434L);
19.
co
20. // Chamando um método definido na classe Pessoa que, por sua vez,
21. // "conhece" apenas os atributos da classe Pessoa
22. f.imprime();
23. }
24.}
g.
Código 8.7 - TesteFuncionario.java
in
nn
ai
.tr
w
w
w
r
.b
1. public class Funcionario extends Pessoa {
2.
3. private long carteiraProfissional;
m
4. private double salario;
5. private String dataAdmissao;
6.
co
7. public void setCarteiraProfissional(long carteira) {
8. this.carteiraProfissional = carteira;
9. }
10. public long getCarteiraProfissional() {
11. return carteiraProfissional;
12. }
13. public void setSalario(double salario) {
14.
15. }
this.salario = salario;
g.
in
16. public double getSalario() {
17. return salario;
18. }
nn
24. }
25. public void impressaoRecibo(int dia, int mes, int ano) {
26. System.out.println("\n\n------- Recibo pagamento ------");
27. System.out.print(" Eu, " + nome + ", portador do RG de numero: ");
.tr
31. System.out.println("\n-----------------------------------\n\n");
32. }
33.}
w
m
Em determinadas situações é interessante que alguns atributos estejam disponíveis para as classes filhas. Nestas
condições, é possivel utilizar o modificador protected em atributos ou métodos. Agora a classe Pessoa será
co
alterada fazendo com que seus atributos sejam declarados com o modificador protected, ao invés de
private, permitindo assim que sejam acessados a partir da classe Funcionario.
9. return endereco;
10. }
11. public void setNome(String nome) {
12. this.nome = nome;
13. }
ai
28. }
29.}
r
.b
m
Representação UML do modificador protected
co
g.
Diagrama 8.4 - modificador protected
in
nn
ai
.tr
w
w
w
r
super para acessar atributos da superclasse Pessoa.
.b
1. public class Funcionario extends Pessoa {
m
2.
3. private long carteiraProfissional;
4. private double salario;
5. private String dataAdmissao;
co
6.
7. public void setCarteiraProfissional(long carteiraProfissional) {
8. this.carteiraProfissional = carteiraProfissional;
9. }
10. public long getCarteiraProfissional() {
11.
12. }
return carteiraProfissional;
18. }
19. public void setDataAdmissao(String dataAdmissao) {
20. this.dataAdmissao = dataAdmissao;
21. }
22. public String getDataAdmissao() {
ai
33.}
w
r
11. f.setNome("Pedro da Silva");
.b
12. f.setEndereco(end1);
13. f.setRg("1234545");
14.
15. // atribuindo as características específicas de funcionários
m
16. f.setSalario(10000);
17. f.setCarteiraProfissional(123434L);
18. f.impressaoRecibo(16,4,2005);
19. }
co
20.}
g.
in
nn
ai
.tr
w
w
w
Além disso, toda classe deve chamar o construtor da superclasse dentro de seu próprio construtor, pois isso é
necessário para que seja feita a inicialização dos elementos herdados. Esta chamada ao construtor da
superclasse deve ser a primeira instrução do construtor da classe. Caso esta chamada não seja feita
r
explicitamente no código da classe, o compilador irá acrescentar uma chamada implícita ao construtor sem
.b
parâmetros da superclasse.
m
Sintaxe para chamar o construtor da superclasse:
co
super(<ZERO ou mais parametros>)
A seguinte instrução é incluída implicitamente pelo compilador Java em construtores que não fazem chamadas
explicitas a outros construtores:
super();
g.
in
Se a superclasse não possuir o construtor default (construtor padrão), isto é, se tiver apenas construtores com
nn
Para entender melhor o que isto significa, veja o que acontece quando se adiciona um construtor que receba
ai
todos os parâmetros de inicialização da classe Pessoa e é feita a recompilação das classes Funcionario e
Pessoa.
.tr
w
w
w
r
12. this.endereco = endereco;
13. }
.b
14. public Endereco getEndereco() {
15. return endereco;
16. }
17.
m
18. public void setNome(String nome) {
19. this.nome = nome;
20. }
co
21. public String getNome() {
22. return nome;
23. }
24. public void setRg(String rg) {
25. this.rg = rg;
26. }
27. public String getRg() {
28.
29. }
return rg;
g.
in
30. public void imprime() {
31. System.out.println("Nome: " + nome);
32. System.out.println("RG: " + rg);
nn
O que ocorreu foi que o compilador adicionou o seguinte fragmento de código, representando o construtor default
r
na classe Funcionario:
.b
Funcionario() {
m
super();
}
co
Ainda no mesmo exemplo, a classe Pessoa não possui um construtor default porque foi declarado um construtor
com três parâmetros da seguinte forma:
8. }
9. //demais métodos idênticos ao exemplo anterior da classe Funcionario
10.}
w
r
.b
O compilador garante que as regras de construção de objetos definidas na classe pai serão seguidas nas
classes filhas quando ele adiciona uma chamada ao construtor padrão da superclasse, obrigando a classe filha a
chamar um construtor válido da superclasse.
m
• É importante ressaltar que a chamada ao construtor da superclasse deverá
co
sempre ser a primeira instrução do construtor!
• Como ambas devem ser a primeira instrução do construtor, não é permitido fazer
uma chamada ao this() e outra ao super(), ou vice-versa.
r
5. a inicialização dos atributos via declaração e o código do bloco de inicialização da superclasse são
.b
executados na ordem em que aparecem;
6. o código do construtor da superclasse é executado;
m
7. a inicialização dos atributos via declaração e o código do bloco de inicialização da classe são executados
na ordem em que aparecem;
8. o código do construtor da classe é executado.
co
Esta seqüência pode ser vista na execução das classes de exemplo a seguir:
7. System.out.println("Construtor da superclasse");
8. }
9.}
5. }
6.
7. ExemploSubClasse() {
w
8. System.out.println("Construtor da subclasse");
9. }
10.}
w
r
.b
m
co
g.
in
nn
ai
.tr
w
w
w
ExemploSubclasse@3554
r
.b
ExemploSubclasse@3554
m
Cada um dos atributos da superclasse é campo = 0
co
criado e inicializado para seu valor padrão campoSuper = 0
ExemploSubclasse@3554
A inicialização dos atributos via
declaração e o código do bloco de
g. campo = 0
in
inicialização da superclasse são campoSuper = 25
executados na ordem em que aparecem
ExemploSubclasse@3554
nn
executado
As seguintes regras devem ser respeitadas para sobrescrever um método de uma superclasse:
•
r
o novo método deve ter exatamente o mesmo nome daquele que quer se sobrescrever, caso
.b
contrário será criado apenas um novo método, ao invés de sobrescrevê-lo;
• o método deverá ter a mesma lista de parâmetros, caso contrário será uma sobrecarga de método
(overload) e não uma sobrescrita;
m
• o tipo do valor de retorno do método deverá ser o mesmo do método que está se sobrescrevendo,
caso contrário ocorrerá um erro de compilação. A partir do Java 5 foi introduzido a covariância de
co
valores de retorno, que permite que o tipo retornado seja também uma subclasse do tipo retornado
pelo método sobrescrito. A covariância será abordada mais adiante;
• não se pode lançar exceções que não estejam declaradas no método, mas apenas as mesmas
•
g.
exceções declaradas na assinatura do método ou que sejam subclasses daquela declarada (este
tópico será discutido mais adiante, no capítulo de Tratamento de Erros);
um método static não pode sobrescrever um método não static e vice-versa;
in
• um método não deve ter um modificador de acesso mais restritivo que o método sobrescrito.
nn
ai
.tr
w
w
w
r
11.
.b
12. public void imprime() {
13. System.out.println("Nome: " + nome);
14. System.out.println("RG: " + rg);
15. System.out.print("Endereco: " + endereco.getRua());
m
16. System.out.println(" , " + endereco.getNumero());
17. }
18.
19. //demais métodos idênticos ao exemplo anterior da classe Pessoa
co
20.
21.}
6.
7. public Funcionario(Endereco e, String nome, String rg,
8. long carteira, double salario, String dataAdmissao) {
9. super(e, nome, rg);
10. this.setCarteiraProfissional(carteira);
ai
11. this.setSalario(salario);
12. this.setDataAdmissao(dataAdmissao);
13. }
14.
.tr
22.
23. //demais métodos idênticos ao exemplo anterior de Funcionario
24.}
w
r
11. //Imprimindo uma linha em branco
12. System.out.println();
.b
13.
14. // Criando o funcionário f e imprimindo seus dados
15. Funcionario f = new Funcionario(end1, "Pedro da Silva", "1234545",
m
16. 123434L, 10000, "16/04/2001");
17. f.imprime();
18. }
19.}
co
Código 8.19 - TesteSobrescrita.java
g.
in
nn
ai
.tr
Observe que o intuito da sobrescrita feita na classe Funcionario, foi adequar o método imprime, herdado de
w
Pessoa, para que também imprima os valores dos atributos do funcionário. Note também, que para evitar
duplicação de código nas duas classes, no método sobrescrito foi feita a chamada ao método herdado original,
w
super.imprime();
A sobrescrita de métodos na UML é representada repetindo o método na classe filha que herda o
método.
r
.b
m
Diagrama 8.5 - sobrescrita de métodos
co
8.7.1 Sobrescrita do método toString()
Para entender melhor como funciona a sobrescrita, veja como sobrescrever um método
g.
que todas as classes herdam da classe Object. Será sobrescrito o método
toString(), que tem como objetivo criar uma representação do objeto como uma
in
String. Esta representação pode ser utilizada para imprimir o objeto na console, para
salvá-lo em um arquivo texto, etc. No método toString()são escolhidos os atributos
nn
<nomeDaClasse>@hashCode
.tr
Agora ele será reescrito na classe Pessoa, fazendo com que retorne uma String contendo o nome, RG e
w
r
12.
13. public Endereco getEndereco() { return endereco; }
.b
14. public String getNome() { return nome; }
15. public String getRg() { return rg; }
16.
17. public void setEndereco(Endereco endereco) {
m
18. this.endereco = endereco;
19. }
20. public void setNome(String nome) {
co
21. this.nome = nome;
22. }
23. public void setRg(String rg) {
24. this.rg=rg;
25. }
26. public void imprime() {
27.
28.
29.
System.out.println("Nome: " + nome);
System.out.println("RG: " + rg);
System.out.print("Endereco: " + endereco.getRua());
g.
in
30. System.out.println(" , " + endereco.getNumero());
31. }
32.
nn
38. }
39.}
Código 8.20 - Pessoa.java com sobrescrita do método toString
.tr
Para entender melhor este exemplo, observe o resultado gerado pela execução da classe
TestePessoaToString apresentada abaixo.
w
r
Código 8.21 - TestePessoaToString.java
.b
m
co
g.
in
nn
ai
.tr
w
w
w
r
.b
1. public final class ContaCorrente {
2. //Corpo da classe
m
3.}
co
1. public class ContaCorrenteEspecial extends ContaCorrente{
2.
3.}
g.
in
Código 8.23 - ContaCorrenteEspecial.java
nn
ai
.tr
w
w
r
7. public final void saque(double valor) {
8. if (saldo > valor)
.b
9. saldo -= valor;
10. else
11. System.out.println("Saldo insuficiente");
m
12. }
13.}
co
Na próxima classe ocorre a tentativa de alterar o método saque, mudando seu comportamento através da
sobrescrita.
Se o atributo for do tipo primitivo, depois de feita a inicialização, não é possível mais alterar o seu conteúdo. Veja o
r
exemplo da classe Pedido, a seguir:
.b
m
1. public class Pedido {
2.
3. //declaração de constantes
4. public final static int NOVO = 1;
co
5. public final static int APROVADO = 2;
6. public final static int CANCELADO = 3;
7.
8. //declaração de atributos
9. private final int codigo;
10.
11.
12.
13.
private int status;
m
Note também a declaração de constantes estáticas na classe para definir possíveis valores para o atributo status.
co
Este tipo de uso, onde combinam-se os modificadores final e static para definir constantes é bem comum. A
classe utilitária Math define um atributo final static para a constante PI.
g.
Se o atributo for do tipo reference, já se sabe que a variável armazena uma referência para o objeto e, por isto,
não é possível alterar tal referência. No entanto, pode-se chamar métodos e atributos do objeto referenciado,
alterando-se seu estado.
in
Veja o que ocorre a seguir para a classe OrdemDeCompra: nesta classe foi criada um atributo da classe Cliente
com o modificador final, e apesar de não ser possivel associar uma ordem de compra a um novo cliente, é
nn
3.
4. public OrdemDeCompra (Cliente cliente) {
5. this.cliente = cliente;
6. }
w
7.}
r
.b
Código 8.28 - Cliente.java
m
3. public static void main(String[] args) {
4. Cliente cliente = new Cliente("Antonio Pereira");
5. OrdemDeCompra ordem = new OrdemDeCompra(cliente);
6.
co
7. //é possivel alterar os dados do cliente
8. ordem.cliente.setNome("Marcos Pereira");
9.
10. //não é possivel atribuir um novo cliente ao pedido
11.
12.
13.
14.}
}
Cliente cliente2 = new Cliente("Ana Silva");
ordem.cliente = cliente2;
g.
in
Código 8.29 - TesteOrdemDeCompra.java
nn
ai
.tr
w
Observe que ocorreu um erro na tentativa de atribuição de um novo Cliente ao atributo cliente da classe
w
TesteOrdemDeCompra.
w
r
7. public void move() {
.b
8. System.out.println("moves on wheels");
9. }
10.}
11.public class Car extends VehicleOnWheels {
m
12. public void move() {
13. System.out.println("move on 4 wheels");
14. }
15.
co
16. public static void main(String[] args) {
17. Car car = new Car();
18. //insert code here
19. }
20.} g.
in
Which instruction can replace the comment if you want to access de ancestor method move in Vehicle so it prints
out the String "move":
nn
A) car.super().super().move();
B) car.parent().parent().move();
C) car.move();
ai
r
10.
.b
11.public class Sub extends Super {
12. private int value = 15;
13.
14. public int getValue() {
m
15. return value;
16. }
17.
18. public static void main(String[] args) {
co
19. Sub sub = new Sub();
20. }
21.}
1. class Qualquer {
2. int a;
3.
4. Qualquer (int a) {
5. this.a = a;
6. }
7.
8. void setA(int a) {
r
9. this.a = a;
.b
10. }
11.
12. public String toString() {
13. return "" + a;
m
14. }
15.}
16.
17. class CJFoo {
co
18. public static void main(String a[]) {
19. Qualquer d1 = new Qualquer(1);
20. Qualquer d2 = new Qualquer(2);
21. amethod(d1, d2);
22. System.out.println("d1 is : " + d1 + " / d2 is : " + d2);
23.
24.
25.
}
A) Doesn’t compile
ai
B) Doesn’t run
C) d1 is: 100 / d2 is: 100
.tr
D) d1 is: 2 / d2 is: 2
E) d1 is 100 / d2 is 2
w
w
w
r
9: }
.b
10:
11: public static void main(String[] args) {
12: Bar b = new Bar();
13: }
m
14: }
co
A) Compilation error at line 2.
B) Compilation error at line 7.
C) No compilation error, but runtime exception at line 2.
D) No compilation error, but runtime exception at line 7.
6. System.out.print(primeNumbers[i]+", ");
7. }
8. }
9.}
.tr
C) 1, 2, 3, 5,
D) 1, 2, 3, 7,
w
1. class Point {
2. private int x, y;
3.
4. public Point(int x, int y) {
5. this.x = x;
6. this.y = y;
7. }
8.}
r
9. class Shape {
.b
10. public final void drawLine(Point start, Point end) {
11. //code to draw line
12. }
13.}
m
14.class Triangle extends Shape{
15. private Point a, b, c;
16.
17. public Triangle(Point a, Point b, Point c) {
co
18. this.a = a;
19. this.b = b;
20. this.c = c;
21. }
22.
23.
24.
25.
public void draw() {
drawLine(a,b);
drawLine(b,c);
g.
in
26. drawLine(c,a);
27. }
28.}
nn
38.}
w
A) The code will not compile because Shape is not also declared as final.
B) The code will not compile because you cannot invoke a final method from a subclass.
w
1. class Ace {
2. Case c = new Case();
3.}
4.
5. class Base {
6.}
7.
8. class Case extends Ace {
r
9. Base b = new Base();
10.}
.b
m
A) A Base is a Case.
B) An Ace has a Base.
C) A Case is an Ace.
co
D) An Ace is a Case.
E) A Case has a Base.
g.
8. What is the result when you compile and run the following code?
in
1. class Animal {
2. private String name;
3. Animal(String name) {
4. System.out.println("Inside Animal's constructor");
nn
5. this.name = name;
6. }
7.}
8.
9. class Dog extends Animal {
ai
10. Dog() {
11. System.out.println("Inside dog's constructor");
12. }
.tr
13.
14. public static void main(String[] args) {
15. Dog d = new Dog();
16. }
w
17.}
D) Runtime error
r
.b
m
co
g.
in
nn
ai
Enumerações
.tr
r
1. public class Cliente {
2.
.b
3. public final static int PESSOA_JURIDICA = 1;
4. public final static int PESSOA_FISICA = 2;
5. private String nome;
6. private String email;
m
7. private int tipo;
8.
9. public Cliente(String nome, String email, int tipo) {
co
10. this.nome = nome;
11. this.email = email;
12. this.tipo = tipo;
13. }
14.
15.
16.
17.
18.
public String getNome() {
}
return nome;
g.
in
19. public void setNome(String nome) {
20. this.nome = nome;
21. }
nn
22.
23. public String getEmail() {
24. return email;
25. }
26.
ai
38.}
r
.b
m
co
g.
Nesta abordagem não há como restringir o valor que será passado pelo usuário no construtor da classe Cliente,
ou seja, se o usuário passar um valor inteiro inválido, como por exemplo 7 não haverá erro de compilação. Não há,
também, como obrigá-lo a utilizar a constante estática ao invés do número inteiro. Os trechos de código a seguir
in
ilustram estas possibilidades:
nn
Note também que trabalhar com o tipo inteiro pode ser inconveniente em vários casos, exigindo um tratamento
.tr
especial do código, como já apresentado. Ao imprimir o tipo do cliente na console, não há certeza se o tipo é
pessoa física ou jurídica pois o valor impresso foi o número 2.
w
Para resolver todos estes problemas, foi criado no Java5 um novo tipo de dado denominado enumeração (enum).
Este novo tipo garante o uso de valores pertencentes a um conjunto pré-definido, ou seja, garante o uso de tipos
w
r
.b
O exemplo abaixo declara uma enumeração com dois valores somente: PESSOA_JURIDICA e PESSOA_FISICA.
Note que a propriedade / atributo que irá receber um valor da enumeração na classe, neste caso o atributo tipo,
deve ser declarado com o mesmo nome da enumeração, indicando assim que somente valores da enumeração
m
serão válidos.
co
1. public class Cliente {
2.
3. public enum TipoCliente { PESSOA_FISICA, PESSOA_JURIDICA };
4. private TipoCliente tipo;
5.
6.
7.
8.
private String nome;
private String email;
g.
public Cliente(String nome, String email, TipoCliente tipo) {
in
9. this.nome = nome;
10. this.email = email;
11. this.tipo = tipo;
nn
12. }
13. public String getNome() {
14. return nome;
15. }
16. public void setNome(String nome) {
ai
21. }
22. public void setEmail(String email) {
23. this.email = email;
24. }
w
31.}
Cliente.TipoCliente.PESSOA_FISICA
O exemplo abaixo instancia um objeto da classe Cliente passando um dos valores da enumeração ao
construtor.
r
.b
1. public class TesteClienteEnumeracao {
2. public static void main(String[] args) {
3. Cliente c = new Cliente("Maria", "maria@gc.com.br",
m
4. Cliente.TipoCliente.PESSOA_FISICA);
5. System.out.println("tipo de cliente criado = " + c.getTipo());
6. }
7.}
co
Código 9.4 - TesteClienteEnumeracao.java
g.
in
nn
No exemplo anterior a enumeração foi criada dentro da classe Cliente, porque representa um tipo específico desta
ai
classe. Podem ser criadas enums em seu próprio arquivo .java, quando se deseja uma melhor reutilização. Isso
pode ser visto a seguir, definindo um tipo para representar os meses do ano.
.tr
r
10. case DEZEMBRO:
.b
11. case JANEIRO:
12. case FEVEREIRO:
13. case JULHO:
14. return true;
m
15. default:
16. return false;
17. }
co
18. }
19.}
g.
in
nn
ai
.tr
w
w
w
r
3. Cliente c = new Cliente("Maria", "maria@gc.com.br",
4. Cliente.TipoCliente.PESSOA_FISICA);
.b
5. System.out.println("Tipos de cliente:");
6. for (Cliente.TipoCliente tipo : Cliente.TipoCliente.values()) {
7. System.out.println(tipo);
8. }
m
9. }
10.}
co
Código 9.7 - TesteImpressaoEnum.java
g.
in
nn
ai
.tr
w
w
w
No exemplo a seguir a enumeração tem três atributos: dias, horaInicio e horaTermino. Foi definido um
construtor que inicializa estes atributos. Repare que cada elemento da enumeração foi construído passando
r
valores para este construtor.
.b
1. public enum Periodo{
2. DIURNO_INTEGRAL ("Seg a Sex", 10, 18),
3. DIURNO_MATUTINO ("Seg a Sex", 8,12),
m
4. NOTURNO ("Seg a Sex", 19, 23);
5.
6. private String dias;
7. private int horaInicio;
co
8. private int horaTermino;
9.
10. Periodo (String dias, int horaInicio, int horaTermino){
11. this.dias = dias;
12. this.horaInicio = horaInicio;
13.
14.
15.
}
this.horaTermino = horaTermino;
r
6. System.out.println("\t- Hora termino:"
.b
7. + t.getPeriodo().getHoraTermino());
8. }
9.}
m
Código 9.10 - TesteTurma.java
co
g.
in
nn
ai
.tr
w
w
w
r
.b
m
co
g.
in
Diagrama 9.1 - enumeração Periodo
nn
ai
.tr
w
w
w
1:enum Animal {
2: DOG("walk"), BIRD("fly"), FISH("swim");
3: String movement;
4: Animal(String s) {
5: movement = s;
6: }
7:}
r
8:
9:public class TestEnum {
.b
10: static Animal animal;
11: public static void main(String[] args) {
12: System.out.println(animal.DOG.movement + " " + animal.FISH.movement);
m
13: }
14:}
What is the result of compiling and running the code? (choose 1 answer)
co
A) walk swim
B) Multiple compilation errors
C) Compilation fails due to an error on line 2
D) Compilation fails due to an error on line 3
E) Compilation fails due to an error on line 4
g.
in
F) Compilation fails due to an error on line 12
1. enum FooEnum {
2. FOO_VALUE
3.}
4.class Foo {
ai
5. enum BarEnum {
6. BAR_VALUE
7. }
.tr
8. void method() {
9. enum BazEnum {
10. BAZ_VALUE
11. }
w
12. }
13.}
w
10
r
.b
m
co
g.
in
nn
ai
Modificador abstract
w
Interfaces
w
w
Um destes mecanismos é a herança, que foi estudada no capítulo anterior. Através da herança, é possível
reaproveitar atributos e métodos escritos para superclasses nas subclasses. Nos exemplos de herança vistos até
r
agora, a superclasse contém um código completo e pronto para ser utilizado, independente da existência de
.b
subclasses: a classe Pessoa, por exemplo, pode ser utilizada em vários sistemas de software, mas em outros
sistemas, talvez fosse necessário utilizar uma classe mais específica, como Funcionário.
m
Existem cenários, porém, nos quais apenas parte das funcionalidades são descritas na superclasse. Neste caso
ela funciona como um modelo que deve ser completado pela subclasse. Em outros cenários, é necessário
co
somente criar um contrato de alto nível para interação entre aplicativos, definindo apenas nomes de métodos,
listas de parâmetros e valores de retorno desejados, mas sem especificar os detalhes da implementação. Para
suportar estes cenários utiliza-se elementos denominados classes abstratas e interfaces, que serão descritos a
seguir. g.
in
10.1 Modificador abstract
O modificador abstract pode ser utilizado para classes e métodos.
nn
*o modificador abstract pode ser combinado com outros modificadores, em qualquer ordem
w
w
r
.b
1. public abstract class FormatadorRecibo {
2.
3. private String nomeEmpresa;
4.
m
5. public void setNomeEmpresa(String nomeEmpresa) {
6. this.nomeEmpresa = nomeEmpresa;
7. }
co
8.
9. public String getNomeEmpresa() {
10. return this.nomeEmpresa;
11. }
12.
13. public String gerarRecibo(String nomeCliente, double valor,
14.
15.
16. }
String data, String motivo) {
return "método em construção...";
g.
in
17.}
Na classe TesteRecibo tentou-se criar uma instância da classe FormatadorRecibo que resultou em um erro
na compilação, indicando que a classe é abstrata e não pode ser instanciada.
ai
8.}
.b
subclasse que a estenda, ou seja, a classe FormatadorRecibo serve como um padrão para outras classes.
m
Veja o exemplo da classe FormatadorModeloA, que estende a classe FormatadorRecibo e sobrescreve seu
método gerarRecibo:
co
1. public class FormatadorModeloA extends FormatadorRecibo {
2.
3. public String gerarRecibo(String nomeCliente, double valor,
4.
5.
6.
7.
String data, String motivo) {
11.
12. }
13.}
4.
5. FormatadorModeloA fA = new FormatadorModeloA();
6. fA.setNomeEmpresa("Rain microsystems do Brasil");
7. String texto = fA.gerarRecibo("William Gates", 2000, "11/04/2004",
w
8. "Aluguel de Servidores");
9. System.out.println(texto);
10. }
w
11.}
.b
uma classe abstrata serve para acomodar métodos abstratos, como será visto a seguir.
m
O desenvolvedor de software freqüentemente se depara com a máxima universal: “existe mais de uma forma de
resolver um problema”. Muitas vezes uma rotina de software pode ser escrita de várias formas para atingir um
co
mesmo resultado.
• utilizando racionalmente a memória em um ambiente restrito;
• ou, minimizando as operações de I/O em um ambiente de resposta rápida;
•
•
g.
ou, visando a simplicidade e a clareza do código fonte para manutenções freqüentes;
ou, utilizando bibliotecas ou APIs para integrar com outros sistemas corporativos;
in
• etc.
nn
A orientação a objetos facilita a convivência com esses dilemas do desenvolvimento de software com o conceito
de métodos abstratos.
ai
.tr
w
w
w
Um método abstrato define um ponto de diversificação, indicando que mais de uma solução técnica pode ser
adotada.
Podem existir métodos abstratos somente dentro de classes abstratas. Isso fará com que estes métodos tenham
r
de ser obrigatoriamente implementados nas subclasses não-abstratas. Um método abstrato tem a seguinte
.b
semântica:
• declara-se apenas a sua estrutura (tipo de retorno, nome, parâmetros de entrada e exceções)
m
incluindo o modificador abstract;
• não se declara corpo de instruções;
co
• finaliza-se a declaração com ponto e virgula (;).
Métodos abstratos são utilizados para definir contratos de comportamento, garantindo que serão implementados
nas classes filhas.
ai
Importante
.tr
r
9. public String getNomeEmpresa() {
10. return this.nomeEmpresa;
.b
11. }
12.
13. public abstract String gerarRecibo(String nomeCliente, double valor,
14. String data, String motivo);
m
15.
16.}
co
Código 10.5 - FormatadorRecibo com método abstrato
Agora, todas as subclasses não-abstratas serão obrigadas a implementar o método gerarRecibo. Na classe
g.
FormatadorModeloB, a seguir, não está implementado o método gerarRecibo e, por isso,ocorrerá um erro de
compilação.
in
1. public class FormatadorModeloB extends FormatadorRecibo {
2.
3. private String simboloMoeda;
4.
nn
10.}
r
3. private String simboloMoeda;
4.
.b
5. public String getSimboloMoeda() {
6. return simboloMoeda;
7. }
m
8.
9. public void setSimboloMoeda(String simboloMoeda) {
10. this.simboloMoeda = simboloMoeda;
11. }
co
12.
13. public String gerarRecibo(String nomeCliente, double valor,
14. String data, String motivo) {
15. return "Recebemos de " + nomeCliente +
16. " o valor de " + this.getSimboloMoeda() + " " + valor +
17.
18.
19. }
20.}
" referente a " + motivo + ". \n" +
super.getNomeEmpresa() + ", " + data;
g.
in
Código 10.7 - FormatadorModeloB.java corrigido
nn
6. fB.setSimboloMoeda("Euro$");
7. String texto = fB.gerarRecibo("Serafim da Silva", 4, "16/09/2004",
8. "cafe com bolinhos");
w
9. System.out.println(texto);
10. }
11.}
w
.b
Classes abstratas
m
Classes abstratas são representadas na UML com seus nomes escritos em itálico, como se vê no
exemplo da classe FormatadorRecibo .
co
Métodos abstratos
Métodos abstratos também são representados escritos em itálico.
g.
in
nn
ai
.tr
w
w
w
r
2.
3. private String nomeEmpresa;
.b
4.
5. public void setNomeEmpresa(String nomeEmpresa) {
6. this.nomeEmpresa = nomeEmpresa;
7. }
m
8.
9. public String getNomeEmpresa() {
10. return this.nomeEmpresa;
co
11. }
12.
13. public abstract String gerarRecibo(String nomeCliente, double valor,
14. String data, String motivo);
15.
16.} g.
Código 10.9 - FormatadorRecibo.java sem o uso do modificador abstract na declaração da classe
in
nn
ai
.tr
w
w
w
r
5. public void setNomeEmpresa(String nomeEmpresa) {
6. this.nomeEmpresa = nomeEmpresa;
.b
7. }
8.
9. public String getNomeEmpresa() {
m
10. return this.nomeEmpresa;
11. }
12.
13. public abstract String gerarRecibo(String nomeCliente, double valor,
co
14. String data, String motivo) {}
15.
16.}
r
5. String data, String motivo) {
.b
6.
7. return "Nós da " + super.getNomeEmpresa() +
8. " recebemos de " + nomeCliente +
9. " em " + data +
m
10. " o valor de R$ " + valor +
11. " referente a " + motivo;
12. }
13.}
co
Código 10.11 - FormatadorModeloC.java
g.
in
nn
ai
.tr
w
w
w
r
.b
2) Para que serve a interface USB?
Através de um padrão estabelecido diversos dispositivos podem ser conectados via USB. Na prática, o USB define
m
uma forma de conectar e expandir a capacidade de um computador. De um lado, há a indústria que definiu um
padrão e o publicou; de outro, fabricantes que estudaram os padrões e desenvolveram dispositivos que podem se
comunicar via USB.
co
g.
in
nn
ai
.tr
De uma forma geral, as interfaces definem um contrato para comunicação entre dois elementos ( usuário e
sistema operacional, computador e periféricos, etc).
w
w
Um exemplo comum é a comunicação entre um aplicativo Java e um banco de dados. No aplicativo existem
classes responsáveis por interagir com o banco, e esta comunicação é feita através de um driver que contém os
comandos e funcionalidades oferecidas pelo banco ao aplicativo. Apesar de cada banco poder oferecer
funcionalidades diferentes, existe um conjunto mínimo comum a todos os bancos: um exemplo de funcionalidade
r
comum é abrir uma conexão.
.b
No entanto, como cada banco de dados possui o seu próprio driver com um conjunto diferente de comandos, o
m
código necessário para abrir uma conexão é diferente para cada banco. Desta forma para cada banco de dados
será necessário escrever um código diferente no aplicativo, de acordo com as características do driver utilizado.
co
Se for estabelecido um contrato de comunicação, ou seja, se for estabelecido um conjunto de comandos comuns
através da definição de uma interface, pode-se aproveitar o mesmo código para comunicação com bancos
diferentes.
g.
Percebendo isso, o JCP (Java Community Process) desenvolveu a API JDBC (Java DataBase Connectivity), que é
um dos exemplos de uso de interfaces.
in
Nela são definidos todos os comportamentos de infra-estrutura que devem ser implementados pelos fabricantes
nn
• fechar a conexão.
.tr
Com esta especificação em mãos, cada fabricante deverá implementar as suas classes (seguir suas regras),
usando as interfaces que compõem o JDBC. Isso garante ao desenvolvedor de aplicações uma certeza das
funcionalidades que estarão disponíveis.
w
w
w
A modelagem com interfaces proporciona uma grande flexibilidade para os sistemas porque através de seu uso é
g.
possível separar totalmente a especificação da implementação, e obter soluções que podem facilmente trabalhar
com implementações diferentes da mesma interface.
in
10.2.2 Definindo uma Interface
nn
Em Java, uma interface define um contrato de comunicação entre classes. Esta comunicação ocorre através de
chamadas de métodos, então é natural imaginar que uma interface defina quais métodos devem estar presentes
em uma determinada classe. É possível definir dois elementos na comunicação entre classes: uma classe cliente,
ai
que faz as chamadas de métodos, e uma classe de implementação, que recebe as chamadas de métodos e
executa o código correspondente. Do ponto de vista da classe cliente, a interface deve definir quais os possiveis
.tr
métodos a serem chamados, e do ponto de vista da classe de implementação, a interface deve definir quais
métodos devem ser implementados.
w
//definição de constantes
//definição de métodos abstratos
w
Assim como uma classe concreta que tem como superclasse uma classe abstrata é obrigada a sobrescrever os
métodos abstratos herdados, uma classe que implementa uma interface é obrigada a criar implementações para
r
os métodos da interface.
.b
Veja a seguir algumas características importantes dos membros de uma interface:
m
Métodos
Uma interface pode conter apenas métodos abstratos. No entanto, não é necessário utilizar o modificador
co
abstract, visto que, todos os métodos definidos em uma interface são abstratos por padrão.
Todos os métodos de uma interface são públicos mesmo que não seja utilizado o modificador de acesso public.
g.
Os únicos modificadores permitidos para os métodos definidos nas interfaces são: public e abstract.
in
Atributos
Uma interface pode conter apenas atributos públicos, e explicitamente inicializados, não havendo nenhuma
nn
Todos os atributos de uma interface são final (constantes) e static por default, mesmo quando não
ai
explicitamente declarados.
Não é possível criar instâncias de interfaces seguindo o mesmo conceito de classes abstratas.
r
interface Trasportavel foi projetada para um sistema de logística.
.b
Uma prática é usar, quando possível, um adjetivo como nome de uma interface – como, entre
outras: Transportavel, Tributavel, Vendavel, Rastreavel, Perecivel.
m
10.2.3 Implementando uma Interface
co
Para implementar uma interface em uma classe utiliza-se a instrução implements, sendo obrigatório
implementar todos os métodos definidos pela interface, e respeitar a assinatura dos métodos.
r
12. this.fabricante = fabricante;
13. this.material = material;
.b
14. this.altura = alt;
15. this.largura = larg;
16. this.profundidade = prof;
m
17. this.peso = peso;
18. }
19.
20. // Implementação obrigatória dos métodos da interface
co
21.
22. public double getVolume() {
23. double vol = this.altura * this.largura * this.profundidade;
24. return vol;
25. }
26.
27. public double getPeso() {
28.
29. }
return this.peso;
g.
in
30.
31. public int getEmpilhamentoMaximo() {
32. return 2;
nn
33. }
34.
35. // getters & setters para demais atributos
36.
37. public double getAltura() { return altura; }
ai
É possível implementar mais do que uma interface em cada classe. A interface Perecivel define métodos para
capturar a temperatura de conservação e a quantidade de dias de validade. A classe AlimentoBase deverá ser
adaptada às interfaces Transportavel e Perecivel. A subclasse Alimento será empregada com um
adaptador, para assumir as características das interfaces.
r
.b
1. public interface Perecivel {
2. public int getTemperaturaDeConservacao();
3. public int getDiasDeValidade();
m
4.}
co
1. public abstract class AlimentoBase {
2.
3. private String nome;
4. private int temperatura;
5. private double volumeCm3;
6. private int pesoGramas;
g.
in
7.
8. public AlimentoBase(String nome, int temperatura, int gramas, double cm3) {
9. this.nome = nome;
10. this.temperatura = temperatura;
nn
23.}
r
12.
13. public double getVolume() {
.b
14. return super.getVolumeCm3();
15. }
16.
17. public double getPeso() {
m
18. return super.getPesoGramas()/1000;
19. }
20.
co
21. public int getEmpilhamentoMaximo() {
22. return this.empilhamentoMaximo;
23. }
24.
25. public int getTemperaturaDeConservacao() {
26.
27. }
28.
return super.getTemperatura();
Criou-se a classe TesteInterface para testar a utilização das classe Mobilia e Alimento e, em
conseqüência, para testar a utilização das interfaces Transportavel e Perecivel.
.tr
w
w
w
r
12.
.b
13. Alimento junk = new Alimento("hamburguer", -2, 350, 260d, 6, 30);
14. System.out.println("Alimento: " + junk.getNome());
15. System.out.println("volume: " + junk.getVolume()
16. + Transportavel.UNIDADE_VOLUME);
m
17. System.out.println("peso: "+junk.getPeso()+ Transportavel.UNIDADE_PESO);
18. System.out.println("empilhamento max: " + junk.getEmpilhamentoMaximo());
19. System.out.println("conservacao: " + junk.getTemperaturaDeConservacao()
20. + "C");
co
21. System.out.println("dias de validade: " + junk.getDiasDeValidade());
22. }
23.}
g.
Código 10.17 - TesteInterface.java
in
nn
ai
.tr
w
Uma classe abstrata pode implementar uma interface sem implementar todos os seus
métodos, pois já foi visto que uma classe abstrata pode possuir métodos abstratos. Os
w
métodos da interface não implementados pela classe abstrata serão considerados como
abstratos. Qualquer classe não-abstrata que estenda a classe abstrata terá que implementar
w
Quando se quer especificar uma nova categoria de classes, que é um subconjunto das classes definidas pela
super-interface, utiliza-se herança de interface.
r
.b
No exemplo utilizado, tendo em vista um sistema de logística, será criada a interface Inflamavel, a partir de
Transportavel,. Desta forma, fica garantido que todas as classes que implementarem a interface Inflamavel
m
obrigatoriamente irão implementar a interface Transportavel, assumindo as características de transporte.
co
1. public interface Inflamavel extends Transportavel {
2. public int getTemperaturaMaxima();
3.}
r
11. this.temperaturaDeTransporte = tempTransporte;
.b
12. }
13.
14. public int getTemperaturaMaxima() {
15. return (int) (temperaturaDeTransporte * 1.4d);
m
16. }
17.
18. public double getVolume() { return 3785; } // 1 galao = 3.785 L
19.
co
20. public double getPeso() {
21. double pesoGramas = getVolume() * densidade;
22. return pesoGramas / 1000;
23. }
24.
25. public int getEmpilhamentoMaximo() { return 3; }
26.
27. public String getNomeCombustivel() { return nomeCombustivel; }
g.
in
28. public double getDensidade() { return densidade; }
29. public int getTemperaturaDeTransporte() { return temperaturaDeTransporte; }
30.
31.}
nn
• a primeira forma é com a figura de um círculo com o respectivo nome abaixo deste símbolo. Nesse caso
representa-se a implementação de interface com uma linha ligando a classe, mas não é possível a
representação de atributos e métodos;
r
.b
m
co
Diagrama 10.2 - representação simplificada de interfaces
•
g.
a segunda maneira, mais usual, é através do estereótipo <<interface>>. Estereótipos são textos
contendo uma ou mais palavras entre << e >> e representam um mecanismo de extensão da linguagem
in
UML. Nesse caso, a representação da implementação deve ser feita utilizando uma seta pontilhada
ligando a classe e a interface.
nn
ai
.tr
w
w
w
Herança de interfaces é representada com o mesmo símbolo utilizado para representação de herança de classes
concretas, isto é, com uma seta contínua partindo da interface pai até a sub-interfaces.
r
.b
m
co
g.
in
Diagrama 10.4 - herança entre interfaces
nn
ai
.tr
w
w
w
É uma boa prática de modelagem de classes trabalhar sempre no sentido do mais abstrato para mais concreto, do
genérico para o específico e assim por diante. Para organizar melhor essa prática, veja alguns recursos:
r
• classes final - definição completa de um objeto.
.b
Diagrama de abstração / concretização
m
Classe final
co
- Classe concreta que,não
pode ser estendida
Classe abstrata - Maior nível de
- Parte abstrata especialização
- Parte completa - Definição completa
g.
in
Interface Classe concr eta
nn
r
1. public class Mobilia implements Transportavel {
.b
2.
3. private String tipo, fabricante, material;
4. private double altura, largura, profundidade, peso;
5.
m
6. public Mobilia(String tipo, String fabricante, String material,
7. double alt, double larg, double prof, double peso ) {
8. this.tipo = tipo;
9. this.fabricante = fabricante;
co
10. this.material = material;
11. this.altura = alt;
12. this.largura = larg;
13. this.profundidade = prof;
14.
15. }
16.
this.peso = peso;
37. }
38.}
w
r
1. public class Alimento extends AlimentoBase implements Transportavel, Perecivel {
.b
2.
3. private int empilhamentoMaximo;
4. private int diasDeValidade;
5.
m
6. public Alimento(String nome, int temperatura, int gramas,
7. double cm3, int empilhamento, int validade) {
8. super(nome, temperatura, gramas, cm3);
9. this.empilhamentoMaximo = empilhamento;
co
10. this.diasDeValidade = validade;
11. }
12. public double getVolume() {
13. return super.getVolumeCm3();
14. }
15. public double getPeso() {
16.
17. }
return super.getPesoGramas()/1000;
g.
in
18. public int getEmpilhamentoMaximo() {
19. return this.empilhamentoMaximo;
20. }
21. public int getTemperaturaDeConservacao() {
nn
27. }
28.}
.tr
r
3. Perecivel perecivel = new Perecivel();
4. }
.b
5.}
m
co
g.
in
nn
ai
.tr
w
w
w
r
2. public int getTemperaturaDeConservacao() {}
.b
3. public int getDiasDeValidade();
4.}
m
co
g.
in
nn
ai
.tr
w
w
w
r
D) A class may implement many interfaces.
.b
2. What is the result of compiling the following classes:
m
1. class Foo {
co
2. void foo() {
3. System.out.println("Inside foo method");
4. }
5. }
6.
7. abstract class Bar extends Foo {
8.
9. }
10.
abstract void bar();
g.
in
11.abstract class Baz extends Bar {
12.}
13.
nn
11
r
.b
m
co
g.
in
nn
ai
Polimorfismo
.tr
Cast de objetos
w
Polimorfismo
Tipos de retorno covariantes e polimorfismo
w
Acoplamento e polimorfismo
w
r
Antes de apresentar o conceito de polimorfismo é importante entender como ocorre em Java a conversão de tipos
.b
entre objetos ou cast de objetos.
m
Em algumas condições, é necessário mudar a forma de operar e "visualizar" um objeto. Nestas situações
co
emprega-se as operações de cast para trabalhar com o objeto, utilizando parte ou todos seus métodos e atributos.
A operação de cast de objetos é semelhante à operação de cast de tipos primitivos, mas com uma diferença
fundamental: os objetos por trás das variáveis não são copiados ou truncados; no máximo, as características e
funcionalidades estarão apenas ocultas, podendo ser restituídas posteriormente.
ai
Para entender estas operações visualise a estrutura hierárquica das seguintes classes:
.tr
w
w
w
Vale reforçar que cast não representa uma mudança estrutural do objeto, mas tão somente uma mudança no tipo
de variável reference que está apontando para o objeto na memória. Imagine por exemplo que um objeto em
memória seja como uma televisão e a referência para o objeto representa o controle remoto. Mesmo que uma TV
tenha 70 funções diferentes, só é possivel acionar aquelas que estão disponíveis no controle remoto. Utilizando-se
um controle genérico que serve para vários tipos de aparelhos,há um conjunto restrito de botões mas caso seja
r
utilizado um controle específico para o tipo e marca da TV acessa-se todas suas funções. O cast de objetos
.b
consiste na troca do controle remoto utilizado para acessar um objeto.
As classes Pessoa, Cliente e Funcionario apresentadas a seguir serão utilizadas nos exemplos de cast.
m
1. public class Pessoa {
co
2. private String nome;
3. private long rg;
4. public String getNome() {
5. return nome;
6. }
7. public void setNome(String nome) {
8.
9. }
this.nome = nome;
g.
in
10. public long getRg() {
11. return rg;
12. }
13. public void setRg(long rg) {
nn
3.
4. public String getCpf() {
5. return cpf;
6. }
w
7.
8. public void setCpf(String cpf) {
9. this.cpf = cpf;
w
10. }
11.}
w
r
11. }
12.
.b
13. public double getSalario() {
14. return salario;
15. }
16.
m
17. public void setSalario(double salario) {
18. this.salario = salario;
19. }
co
20.
21. public double calculaPagamento() {
22. return salario;
23. }
24.}
g.
Código 11.3 - Funcionario.java
in
nn
ai
.tr
w
w
w
Portanto, é possível realizar a operação de widening, visualizando um objeto da classe Cliente como Pessoa ou
Object, mas o objeto não perderá definitivamente suas características de Cliente. Seguindo a analogia
apresentada anteriormente, é possível utilizar um controle remoto de Pessoa e Object para acessar um objeto
r
do tipo Cliente, que continua sendo um Cliente independente do controle utilizado.
.b
1. public class TesteCastUp {
2.
m
3. public static void main(String[] args) {
4.
5. // O objeto c foi declarado e instanciado como Cliente
co
6. Cliente c = new Cliente();
7.
8. // Casting UP explicito do objeto da classe Cliente para classe Pessoa
9. Pessoa p = (Pessoa) c;
10.
11.
12.
13.
14.
// Casting UP do objeto da classe Cliente para classe Pessoa
Pessoa p2 = c;
23.}
Ao visualizar um Cliente como uma Pessoa perde-se a capacidade de manipular os métodos getCpf e
setCpf. mas ainda é possível trabalhar com os getters e setters para nome e rg.
w
Apesar da variável de manipulação ser do tipo Pessoa o objeto continua sendo um Cliente,
w
Ao visualizar uma pessoa como um Object torna-se possível manipular apenas os membros (atributos e
métodos) definidos na classe Object, como toString().
A generalização é uma operação mais previsível do que a especialização, porque a análise da hierarquia de
classes permite saber se a operação é ou não possível; na especialização, ao contrário, a operação irá depender
do tipo real do objeto, que, por sua vez, depende da forma como ele foi criado. O tipo real do objeto é definido pelo
r
construtor que foi chamado quando ele foi instanciado.
.b
Então, se um objeto é criado e declarado como Cliente, e sofre um cast up para Pessoa é possível fazer o cast
m
down para voltar a visualizá-lo como Cliente, porque o tipo real do objeto é Cliente. No entanto, se um
objeto é criado e declarado como Pessoa, não é possível fazer o cast down para transformá-lo em Cliente. Se
co
o tipo real é Pessoa, significa que o objeto não tem os atributos e métodos específicos de Cliente, que no
exemplo são cpf e seus respectivos getters e setters. Voltando à analogia anterior, não adianta tentar usar os
botões do controle remoto para funções não suportadas no modelo de TV utilizado.
g.
Ratificando, todo Cliente é uma Pessoa (possui todos os atributos e métodos de Pessoa), mas nem toda
Pessoa é um Cliente e por isso toda operação de cast down deverá ser feita sempre de forma explícita. Esta
in
obrigatoriedade significa que o compilador não pode garantir que os tipos sejam válidos, e o programador está
assumindo a responsabilidade. Caso a conversão seja inválida será gerado um erro em tempo de execução.
nn
8.
9. // Cast DOWN de Pessoa para Cliente
10. Cliente c2 = (Cliente) p;
11.
w
18.}
.b
O operador instanceof é utilizado, antes da operação de cast down, para verificar se o objeto referenciado por
uma variável é compatível com uma determinada classe ou interface. Com isso evita-se o problema
m
ClassCastException verificado no exemplo anterior.
co
1. public class TesteOperadorInstanceof {
2.
3. public static void main(String[] args) {
4. Pessoa p = new Pessoa();
5.
6.
7.
8.
if (p instanceof Cliente) {
Cliente c = (Cliente) p; // cast down seguro
System.out.println("CPF: " + c.getCpf());
} else if (p instanceof Funcionario) {
g.
in
9. Funcionario f = (Funcionario) p; // cast down seguro
10. System.out.println("Salario: " + f.getSalario());
11. } else {
12. System.out.println("Nao eh Cliente nem Funcionario");
nn
13. }
14. }
15.}
ai
Polimorfismo é uma palavra de origem grega que significa muitas formas. Essa palavra é usada para nomear um
poderoso recurso da Programação Orientada a Objetos (POO) que é utilizado da seguinte forma:
• define-se um tipo base (classe ou interface) e cria-se classes derivadas, por herança ou por
implementação de interface, e assim obtêm-se várias formas para um tipo base;
r
.b
• utiliza-se uma declaração de variável de um tipo-base para manipular (via cast up) um objeto de
qualquer um de seus tipos derivados.
m
co
Onde uma superclasse é esperada utiliza-se uma instância de uma subclasse.
Onde uma interface é esperada utiliza-se uma instância de uma classe implementadora.
g.
Essa abordagem otimiza a escrita de código, permitindo que um aplicativo cliente trabalhe com tipos mais
in
genéricos, em vez de precisar tratar cada tipo específico, quando o comportamento desejado é o mesmo para
ambos. Veja a seguir como isso acontece com as classes Contabilidade, FuncionarioCLT e
nn
7. }
8.}
w
r
1. public class ContabilidadeSemPolimorfismo {
.b
2.
3. public void gerarDemonstrativo(FuncionarioCLT func) {
4. System.out.println("O funcionario: " + func.getNome());
m
5. System.out.println("Recebeu o pagamento de "+func.calculaPagamento());
6. }
7.
8. public void gerarDemonstrativo(FuncionarioPJ func) {
co
9. System.out.println("O funcionario: " + func.getNome());
10. System.out.println("Recebeu o pagamento de "+func.calculaPagamento());
11. }
12.}
g.
Código 11.9 - ContabilidadeSemPolimorfismo.java
in
Perceba que o código dos dois métodos gerarDemonstrativo é idêntico. Um dos princípios da boa
programação orientada a objetos é que a repetição de código deve ser evitada mas como evitar a repetição do
código apresentado acima se os parâmetros recebidos são de tipos diferentes? Apesar dos tipos recebidos serem
nn
diferentes, ambos são subclasses de Funcionario, e mais importante, o código utiliza apenas métodos definidos
na superclasse. Assim,neste cenário é possivel obter redução do código utilizando o polimorfismo e, para isso
basta definir um único método que recebe como parâmetro a superclasse Funcionario, e eliminar os métodos
ai
7.}
Mas se a referência é para Funcionário, não vai ser chamado o método definido na superclasse em vez dos
métodos sobrescritos nas subclasses? Para responder esta pergunta basta recordar a analogia do controle
Esta capacidade de chamar o método desejado a partir do tipo real do objeto em tempo de execução é chamada
vínculo dinâmico (dynamic binding ou late binding).
r
O exemplo a seguir ilustra este comportamento:
.b
1. public class TesteDynamicBinding {
2.
m
3. public static void main(String[] args) {
4. Funcionario f = new FuncionarioCLT();
5. f.calculaPagamento();
co
6. f = new FuncionarioPJ();
7. f.calculaPagamento();
8. }
9.}
g.
Código 11.11 - TesteDynamicBinding
in
nn
ai
.tr
coleções heterogêneas
A classe RelatorioPessoas apresentada a seguir será utilizada nos próximos exemplos de polimorfismo:
w
r
13. }
.b
14. return quantidadeImpressa;
15. }
16.}
m
Código 11.12 - RelatorioPessoas.java
co
Considerando a hierarquia de classes Pessoa, Cliente e Funcionário: onde uma Pessoa é esperada pode
ser utilizado um Cliente ou um Funcionário.
11. c.setRg(272829303132L);
12. c.setCpf("6544566-54");
13.
14. Funcionario f = new Funcionario();
.tr
19.
20. RelatorioPessoas relatorio = new RelatorioPessoas();
21. relatorio.imprime(p); // Pessoa
w
25. }
26.}
r
.b
m
co
11.2.2 Coleções Heterogêneas
17. f.setCarteiraProfissional("86554877899");
18.
19. Pessoa[] pessoas = new Pessoa[3];
20. pessoas[0] = f; // Funcionário
w
27.}
r
.b
m
11.3 Tipos de retorno covariantes e polimorfismo
co
Até a versão 1.4 da plataforma Java, a sobrescrita de métodos era válida somente se o nome do método, os tipos
dos parâmetros e o tipo de retorno fossem mantidos, Então, imagine o seguinte cenário: há uma interface
chamada BaseDAO que é a super-interface de todas as classes de acesso a camada de persistência; essa
g.
interface define um método que retorna um objeto dado a primary key do objeto, ou seja, o índice no banco de
dados. Veja no exemplo abaixo:
in
1. public interface BaseDAO {
2. public Object getByPrimaryKey(Object o);
nn
3.}
Cada implementação desta interface retorna um objeto de um tipo diferente, no entanto, pela definição de
sobrescrita retorna-se somente objetos do mesmo tipo definido na interface, como pode ser observado a seguir:
.tr
6.}
r
6. }
7.}
.b
Código 11.17 - TesteClienteDAOSemCovariancia.java
m
Com a introdução dos valores de retorno covariantes, é possível alterar o tipo de retorno de um método
sobrescrito, desde que o novo tipo de retorno seja derivado (subclasse ou implementação da interface) do tipo
co
definido no método que está sendo sobrescrito, facilitando assim a tarefa do usuário que utiliza os métodos
sobrescritos. O mesmo exemplo seria implementado com covariância da seguinte forma:
Na classe ClienteDAO altera-se o tipo de retorno do método getByPrimaryKey, indicando que o método
ai
retorna um Cliente ao invés de um Object. Isto só é possível porque Cliente é uma subclasse de Object.
.tr
A facilidade pode ser observada na classe que utiliza o método sobrescrito. Perceba que no código abaixo não foi
necessário fazer casting de Object para Cliente, porque o método já retorna um objeto do tipo Cliente.
w
6.}
r
.b
m
co
g.
in
Diagrama 11.2 - Acoplamento entre a classe Contabilidade e implementações de Funcionario sem o
nn
uso de polimorfismo
ai
.tr
w
w
w
Diagrama 11.3 - Acoplamento entre a classe Contabilidade e Funcionario após o uso de polimorfismo
Um dos ganhos obtidos com o baixo acoplamento é a facilidade na manutenção do sistema. Em sistemas com alto
acoplamento, alterações nas classes provocam um efeito cascata de alteração em múltiplas classes. No primeiro
exemplo se fosse alterado o nome da classe FuncionarioPJ para FuncionarioTerceirizado seria
r
necessário alterar também a classe Contabilidade. Já no segundo exemplo, somente a classe
.b
FuncionarioPJ seria alterada.
m
co
g.
in
nn
ai
.tr
w
w
w
1. Assume that Triangle and Circle are both subclasses of class Shape.
Given the declarations:
Shape shape = new Shape();
Triangle triangle = new Triangle();
Circle circle = new Circle();
r
.b
Which statement best describes the result of attempting to compile and execute the following statement:
shape=triangle;
m
A) Compiles and definitely legal at runtime
B) Does not compile
co
C) Compiles and but may be illegal at runtime
2. Given:
1: class Animal {
g.
in
2: String getName() { return "animal"; }
3: Animal getType() { return this; }
4: }
5: class Mammal extends Animal {
nn
12
r
.b
m
co
g.
in
nn
ai
Pacotes
.tr
Diagrama de componentes
w
É importante entender, desde o ínicio, que a estrutura de pacotes está baseada na estrutura de diretórios. No
r
entanto, para se referir a pacotes dentro do código não se utiliza uma barra, mas um ponto separando os
.b
diretórios.
Todas as APIs do Java estão organizadas em pacotes, como se verifica na documentação Javadoc:
m
Lista de classes do pacote selecionado; ou
co
todas, se forem selecionadas All Classes
Lista de pacotes do Java SE
g.
in
nn
ai
.tr
w
w
w
r
.b
m
co
g.
Figura 12.2 - estrutura interna do arquivo rt.jar
in
nn
ai
.tr
w
w
Figura 12.3 - estrutura interna do arquivo rt.jar navegando para o pacote java
r
package nomeCompletoDoPacote;
.b
A instrução package deverá sempre ser a primeira instrução da classe, e o nome do pacote será a estrutura de
diretórios onde se encontra a classe, mas lembrando que deverá ser utilizado ponto como separador em vez da
m
barra. É importante destacar que a estrutura de diretórios não consiste no caminho absoluto utilizado desde o
diretório raiz do sistema de arquivos, mas sim de um caminho relativo. O início deste caminho relativo corresponde
co
ao classpath configurado para a aplicação, que normamente é o diretório corrente.
Todas as classes de um mesmo pacote devem conter a mesma instrução package como primeira instrução do
arquivo no qual a classe foi declarada. g.
Convenciona-se declarar nomes de pacotes utilizando uma URL invertida: normalmente a URL do “proprietário” da
in
classe como, por exemplo, sua empresa ou a empresa para quem você está desenvolvendo.Aqui será utilizada a
URL da trainning.com.br que será declarada como br.com.trainning.
nn
1. package br.com.trainning.exemplos;
.tr
2.
3. public class ExemploPacote {
4. public static void main(String[] args) {
5. System.out.println("Esta classe esta dentro de um pacote!!");
w
6. }
7. }
Suponha que esteja desenvolvendo um projeto no subdiretório projeto1 localizado na pasta projetos:
dentro dele, será criada a estrutura de diretórios do pacote, (br/com/trainning)e, neste caso, a compilação e
também a execução, como será visto no tópico seguinte, deverão ser feitas a partir da raiz da estrutura de
pacotes, isto é, do diretório projeto1.
r
.b
c:\projetos\projeto1> javac br\com\trainning\exemplos\*.java
m
ou então,
co
c:\projetos\projeto1> javac br\com\trainning\exemplos\ExemploPacote.java
r
.b
m
co
g.
in
nn
Para corrigir este erro basta executar a classe do diretório correto, ou seja, do diretório raiz da aplicação.
ai
.tr
w
w
w
r
da classe no código
.b
Sintaxe para importação de classes:
import nomeCompletoDaClasse;
m
<declaração de classe ou interface>
ou
co
import nomePacote.*;
<declaração de classe ou interface>
A classe Date do pacote java.util será utilizada nos exemplos com o intuito de aprender a importar classes
que não estejam no mesmo diretório, isto é, que não estejam no mesmo pacote. Esta classe representa datas em
ai
Java.
.tr
Quando se constrói um objeto da classe Date utilizando o construtor padrão, o objeto é inicializado para o valor
da data atual do sistema operacional.
w
Observe a seguir que apenas a classe Date do pacote java.util foi importada, e dentro do método main
foi criada uma instância da classe e impressa na console:
w
w
r
Código 12.2 - TesteImport.java
.b
m
co
g.
Também é possível importar todas as classes de um pacote, utilizando * no lugar do nome da classe. No exemplo
in
abaixo é novamente utilizada a classe Date mas agora importando todas as classes do pacote.
nn
1. package br.com.trainning.testes;
2.
3. import java.util.*;
4.
5. public class TesteImportPacote {
ai
6.
7. public static void main(String[] args) {
8. Date d = new Date();
.tr
9. System.out.println(d);
10. }
11.}
w
.b
não quando um import é realizado.
m
IMPORTANTE
• Para aumentar a legibilidade do código, é conveniente declarar explicitamente o
co
import individual das classes;
• Além das classes do próprio pacote, as únicas classes que podem ser utilizadas
diretamente no código sem a necessidade de importação são aquelas do pacote
g.
java.lang. Neste pacote estão as classes consideradas fundamentais da
linguagem, como a classe System e String, entre outras.
in
Outra opção para trabalhar com classes de outros pacotes é usar o nome completo da classe como pode ser visto
a seguir. Neste caso não é necessário utilizar a instrução import.
nn
1. package br.com.trainning.testes;
2.
ai
Modificador public
Definem-se como public:
• classes;
• construtores;.
• métodos;
r
• atributos.
.b
Os elementos que estiverem com o modificador de acesso public poderão ser acessados por outros métodos e
construtores de quaisquer classes (inclusive as que estão em outros pacotes).
m
Modificador private
Definem-se como private:
co
• construtores;
• métodos;
• atributos.
g.
Os elementos que estiverem com o modificador de acesso private não poderão ser acessados externamente
in
por nenhuma instância de outras classes e, portanto,, poderão ser acessados exclusivamente pelos elementos da
própria classe.
nn
Modificador protected
Definem-se como protected:
ai
• construtores;
•
.tr
métodos;
• atributos.
w
Os elementos demarcados como protected, serão acessíveis pelos métodos e construtores de suas classes
filhas e todas as classes que estiverem no mesmo pacote.
w
w
r
.b
Os elementos demarcados por este modificador podem ser acessados, por método ou construtores da classes
definidas no mesmo pacote.
m
Tabela 12.1 - Modificadores de acesso e o comportamento entre pacotes:
co
Acessível Modificador Acesso permitido
Permite acesso a qualquer classe
public
protected
g.
Qualquer classe do mesmo pacote ou qualquer
sub-classe pode acessar o metodo ou atributo
protected.
in
Permite acesso a qualquer classe do mesmo
não especificado pacote
nn
Não se pode fazer o import das duas classes explicitamente, porque o compilador detecta a ambigüidade,
gerando uma mensagem de erro, conforme demonstra o código abaixo e a saída da compilação:
r
.b
1. package br.com.trainning.erros;
2.
m
3. import java.util.Date;
4. import java.sql.Date;
5.
6. public class ExemploAmbiguo {
co
7.
8. }
1. package br.com.trainning.erros;
2.
3. import java.util.*;
4. import java.sql.*;
5.
6. public class ExemploImportAmbiguo {
7.
r
8. public static void main(String[] args) {
.b
9. Date data = new Date();
10. System.out.println("java.util.Date = " + data);
11. // Operações que utilizam classes do pacote java.sql
12. }
m
13.}
co
g.
in
nn
ai
.tr
É possivel resolver a ambigüidade utilizando o nome completo das classes, em vez de importá-las como
demonstrado a seguir:
w
w
w
r
13. System.out.println("java.util.Date = " + data);
.b
14. System.out.println("java.sql.Date = " + dataSQL);
15. }
16.}
m
Código 12.7 - ExemploSemImport.java
co
g.
in
nn
Outra solução para a ambigüidade, é fazer um import explícito somente de uma das classes e o import do
pacote inteiro da outra. No exemplo utilizado, ao fazer um import explícito para a classe java.util.Date e um
import do pacote inteiro java.sql.*, pode se usar a classe Date sem utilizar o nome totalmente qualificado;
ai
o compilador assumirá que a classe Date que se quer utilizar é a do pacote java.util, visto que foi feito um
import explícito. Se for necessário utilizar também a classe java.sql.Date o nome completo deve ser
.tr
utilizado.
w
w
w
r
.b
Código 12.8 - ExemploImportCorreto.java
Se nenhuma das instruções de import for explícita ou ambas forem explícitas, ocorrerá um erro de compilação.
m
co
12.2.3 Trabalhando com classes que estão em pacotes diferentes
Como foi visto anteriormente, a estrutura de diretórios foi criada para melhor organização das classes; logo não
adiantaria criar apenas um pacote e colocar todas as classes de um projeto dentro dele.
g.
No entanto, muitas vezes uma classe utiliza outra: a classe Pessoa utiliza a classe Endereco mas elas não
in
estarão necessariamente no mesmo pacote.
nn
Para permitir que a classe Pessoa possa acessar a classe Endereco é preciso seguir os seguintes passos:
1. colocar cada um dos arquivos das classes em seus respectivos pacotes
2. adicionar a instrução package em cada uma delas, respeitando a estrutura de diretórios
3. colocar a instrução de import adequada na classe Pessoa para que a JVM possa carregar a classe
ai
Veja como fazer isso colocando as classes Pessoa e Endereco em pacotes diferentes.
w
Obs: Os métodos da classe Pessoa foram omitidos no exemplo, pois são irrelevantes neste contexto.
w
w
r
Código 12.9 - Pessoa.java
.b
m
1. package br.com.trainning.util;
2.
3. public class Endereco {
co
4.
5. private String rua;
6. private int numero;
7. // Os métodos da classe foram omitidos
8. }
g.
Código 12.10 - Endereco.java
in
nn
ai
.tr
w
w
w
1. package br.com.trainning.exemplos.erros; 2.
r
7. }
8. }
.b
Código 12.11 - ExemploFaltaImport.java
m
co
g.
in
nn
ai
.tr
w
w
w
r
.b
3. Utilizar import antes da instrução package.
m
1. import java.util.Date;
2. package br.com.trainning.exemplos.erros;
3.
co
4. public class ExemploImportAntes {
5. public static void main(String[] args){
6. Date d = new Date();
7. System.out.println(d);
8. }
9. } g.
Código 12.12 - ExemploImportAntes.java
in
nn
ai
.tr
w
w
w
Observe a classe Endereco declarada com o modificador padrão, ou seja, permitindo acesso somente a classes
do mesmo pacote.
1. package br.com.trainning.util;
2. class Endereco {
r
3.
4. private String rua;
.b
5. private int numero;
6.//Os métodos da classe foram omitidos
7. }
m
Código 12.13 - Endereco.java com modificador default
A classe Pessoa (Código 12.9) utiliza a classe Endereco, mas a classe Endereco não oferece acesso
co
para classes definidas em outros pacotes.
g.
in
nn
ai
.tr
w
w
w
5. As classes estão nos diretórios corretos, mas a instrução package não foi colocada.
Nos exemplos a seguir omitiu-se a instrução package das classes Pessoa e Endereco.
1. import br.com.trainning.util.Endereco;
r
2.
.b
3. public class Pessoa {
4.
5. private Endereco endereco;
6. private String nome;
m
7. private String rg;
8. // Os métodos da classe foram omitidos
9. }
co
Código 12.14 - Pessoa.java sem a instrução package
A utilização de métodos estáticos até a versão 1.4 da plataforma Java exige que a chamada ao método seja
prefixada com o nome da classe.
1. package br.com.trainning.util;
2.
3. public class Logger {
r
4. public static final int DEBUG = 0;
5. public static final int ERROR = 1;
.b
6. public static void log (String msg, int level){
7. System.out.println("["+level+"]"+msg);
8. }
m
9. }
co
1. package br.com.trainning.testes; 2.
3. import br.com.trainning.util.Logger;
4.
g.
in
5. public class TesteSemStaticImport {
6. public static void main(String[] args){
7. Logger.log("Facil, facil....", Logger.DEBUG);
8. }
nn
9. }
Em muitas situações é necessário definir constantes e métodos que deverão ser utilizadas em várias classes
muito frequentemente. Para que o código não se torne muito redundante e repetitivo, os programadores
.tr
começaram a colocar as constantes e métodos estáticos em uma interface ao invés de colocar em uma classe.
Desta forma, ao invés de importar a classe quando necessário, implementa-se a interface.
w
Esta não é considerada uma boa prática, e foi definida como Constant Interface Antipattern no livro Effective
3
Java.
w
w
3
Este livro, escrito por Joshua Bloch, é considerado uma das melhores referências para boas práticas na
linguagem Java.
A sintaxe é ligeiramente diferente, visto que é necessário adicionar a palavra static na declaração de import.
r
import static nomeCompletoDaClasse.nomeMembro;
.b
ou
import static nomeCompletoDaClasse.*;
m
Usando importação estática podem ser utilizados os métodos e atributos estáticos sem a necessidade de utilizar o
nome da classe.
co
1. package br.com.trainning.testes;
2.
3. import static br.com.trainning.util.Logger.*;
4.
5. public class TesteComStaticImport {
g.
in
6. public static void main(String[] args) {
7. log("Facil, facil....", DEBUG);
8. }
9. }
nn
• Devemos utilizar importação estática quando estamos fazendo uso intenso de métodos estáticos de
uma determinada classe;
w
• Devemos utilizar importação estática para substituir o uso do Constant Interface Anti-pattern;
w
• Não devemos utilizar importação estática para todos os métodos estáticos, pois isto pode diminuir a
legibilidade e consequentemente a manutenibilidade do código, já que não saberemos em qual classe
w
Um pacote é representado na UML pelo símbolo de pasta cujo nome pode ser colocado na guia superior esquerda
ou no meio do desenho.
r
.b
m
Diagrama 12.1 - representando pacotes em UML
co
g.
in
Diagrama 12.2 - representação alternativa de pacotes em UML
nn
ai
.tr
w
w
w
r
.b
m
co
Diagrama 12.3 - dependências entre classes
g.
Em determinadas situações não é necessário especificar a dependência entre classes, mas somente a
dependência entre os pacotes, como se observa no seguinte diagrama:
in
nn
ai
.tr
w
w
w
• Iniciar os pacotes com o domínio da internet invertido mais o nome da empresa proprietária ou
desenvolvedora, como por exemplo: br.com.trainning, com.mysql, br.com.seudominio.
• Não utilizar pacotes iniciados com java, javax, ou domínios de outras empresas.
r
.b
• Ao criar pacotes utilizar apenas letras minúsculas e apenas nomes com uma palavra, de acordo com
a convenção de nomenclatura da Sun,
m
• Evitar a dependência circular entre pacotes como pode ser visto no diagrama abaixo:
co
g.
in
Diagrama 12.5 - dependência circular entre pacotes
Neste caso específico, o ideal é que o pacote de business dependa do pacote que contém as classes de
modelo, mas que a recíproca não seja verdadeira:
nn
ai
.tr
• Pacotes devem conter um conjunto de classes de uma mesma “família”. Por exemplo: o pacote
w
possível, misturar classes muito diferentes em um mesmo pacote. Não faz sentido colocar a classe
Logger dentro do pacote br.com.trainning.model, ou br.com.trainning.business.
r
.b
m
co
g.
in
nn
Caso seja necessário, pode haver mais subdivisões referentes a módulos de um mesmo projeto, ou qualquer
divisão que faça sentido no cenário da empresa, projeto e equipe de desenvolvimento.
.tr
w
w
w
r
Existe, entre outros utilitários do JDK, um denominado jar, que é responsável pela criação, descompactação e
.b
atualização dos arquivos com terminação *.jar.
Para obter a ajuda desse utilitário basta digitar jar na linha de comando e todas as opções serão exibidas.
m
co
g.
in
nn
ai
.tr
w
w
w
Para mais infomações, consulte a documentação dos utilitários distribuída juntamente com o JDK que pode ser
encontrada em: %JAVA_HOME%\docs\tooldocs\tools.html
r
.b
jar –cvf nomeDoJar.jar raizDoPacote
m
v: Verbose (imprimir na console todas as operações que estão sendo executadas)
f: File (nome do arquivo a ser criado)
co
No exemplo, há a seguinte estrutura de diretórios:
g.
in
nn
ai
A partir do diretório projeto1, o utilitário jar será executado, passando os seguintes parâmetros:
jar –cvf testeJAR.jar br
w
w
w
co
Examine o conteúdo do arquivo testeJAR.jar, abrindo-o com algum utilitário de descompactação.
g.
in
nn
ai
.tr
w
Note que foi criado um arquivo MANIFEST.MF no diretório META-INF; ele será visto mais adiante no tópico que
explicará a execução de arquivos JAR.
w
r
A extração dos arquivos do JAR gerado no exemplo anterior pode ser feita com o seguinte comando:
.b
jar –xvf testeJAR.jar
m
co
g.
in
nn
ai
.tr
w
w
w
r
Created-By: Criador do arquivo
.b
Main-Class: Nome totalmente qualificado da classe que queremos executar
Para transformar o JAR de exemplo em um arquivo executável deve ser criado um arquivo MANIFEST.MF
m
conforme o código de exemplo apresentado a seguir:
co
Manifest-Version: 1.0
Created-By: trainning
Main-Class: br.com.trainning.testes.TesteImport
g.
Código 12.19 - MANIFEST.MF
in
Sempre que o utilitário jar gera ou altera um arquivo, um MANIFEST.MF é criado e adicionado automaticamente
ao diretório META-INF. Para adicionar um arquivo MANIFEST.MF específico, basta utilizar o parâmetro m (indica
nn
que será feita a inclusão de um Manifest.MF) e indicar o local onde o arquivo pode ser encontrado.
A adição de um arquivo manifest.MF no exemplo apresentado pode ser feita com o seguinte comando:
ai
r
.b
m
co
12.6.4 Execução de um JAR
g.
Para executar um arquivo JAR (que tenha definido corretamente o MANIFEST.MF) usa-se o utilitário java,
passando –jar como argumento, assim como o nome do arquivo que se quer executar.
in
Sintaxe:
java –jar nomeJar.jar
nn
Como esperado, o método main da classe TesteImport foi executado, imprimindo a data do sistema na
console.
w
w
No exemplo utilizado:
CLASSPATH = C:\Projetos\projeto1\testeJARExecutavel.jar
Veja a criação de uma classe chamada TesteJar, que utiliza a classe Endereco contida no
r
testeJARExecutavel.jar. Esta classe será colocada em um diretório isolado, neste caso, na raiz do sistema
.b
de arquivos:
m
1. package br.com.trainning.testes;
2.
3. import br.com.trainning.util.Endereco;
co
4.
5. public class TesteJar {
6.
7. public static void main(String[] args) {
8.
9.
10.
11.}
}
Endereco e = new Endereco("Av. Paulista", 1000);
System.out.println(e);
g.
in
Código 12.20 - TesteJar.java
nn
ai
.tr
w
w
w
r
.b
m
co
g.
in
nn
ai
.tr
IMPORTANTE
Quando se utiliza o argumento classpath, fica estabelecido que o compilador ou o utilitário
em questão deverá procurar por arquivos somente no(s) diretório(s) indicados.
w
Para executar a classe TesteJar também será necessário indicar ao utilitário java onde
w
encontrar o JAR que contém a classe Endereço, assim como foi feito para a compilação,
w
co
que o utilitário java também deverá procurar as classes no diretório corrente, pois é lá que se
encontra a classe TesteJar.
g.
Isso é feito concatenando o ponto ( . ) no CLASSPATH; no Windows, a concatenação é feita utilizando ponto e
in
virgula (;) e no Linux utilizando dois pontos ( : ) .
nn
ai
.tr
w
w
w
r
.b
Diagrama 12.8 - exemplo de componente
m
Os artefatos podem, ou não, ser exibidos dentro de um componente.
co
g.
in
nn
Existem alguns estereótipos freqüentemente utilizados para componentes. Abaixo são apresentados alguns deles:
.tr
r
.b
Diagrama 12.13 - componente EJB session bean
m
Dependência entre componentes
co
A dependência entre componentes é representada na UML com a mesma seta utilizada para representar a
dependência entre classes e pacotes, isto é, uma seta pontilhada ligando os componentes como pode ser visto no
seguinte diagrama:
g.
in
nn
Desta forma, fica indicado que para a AplicacaoWeb funcionar é necessário ter a biblioteca dateutils.jar.
ai
.tr
w
w
w
r
.b
2. Which of the modifiers of member methods and member variables allows access to all subclasses?
(select 2 answers)
m
A) public
B) private
co
C) protected
D) No modifier(default)
1. package aj2.cap12.cert;
2.
g.
in
3. public class Util {
4. public static final int MY_CONSTANT = 2;
5. public static int doCalculation(int param) {
nn
1. import aj2.cap12.cert.Util.*;
ai
2. import java.lang.System.out;
3. public class UtilClient {
4. public static void main(String[] args) {
5. out.println(doCalculation(MY_CONSTANT));
.tr
6. }
7. }
w
B) 6
C) 9
w
D) Compilation fails.
E) An exception is thrown at runtime.
13
r
.b
m
co
g.
in
nn
ai
Tratamento de erros
.tr
Exceptions
Tratamento de Exceções
w
r
• um arquivo de configuração pode ter sido removido acidentalmente;
.b
• o usuário pode ter digitado um valor não aceitável.
m
Não tratar os erros oriundos dessas situações previsíveis é considerada uma falha no desenvolvimento do
software, portanto, é necessário “preparar algumas respostas” para essas situações.
co
[ ] A mensagem de erro deve ser impressa na console.
[ ] A mensagem de erro deve ser impressa em uma área visível pelo usuário
(Interface Gráfica, Aplicações Web, etc..).
g.
[ ] Um e-mail deve ser enviado para o administrador do banco de dados.
[ ] A operação deve ser logada em arquivo.
in
[ ] Os dados do usuário devem ser temporariamente salvos em arquivo.
[ ] Outras.________________________________________________________.
nn
Não existe uma única resposta para esta pergunta, pois ela simplesmente dependerá da situação. O que não
pode ser feito é ignorar o erro e continuar o processamento normal do programa, como se nada tivesse
acontecido.
ai
.tr
Infelizmente verifica-se que é muito comum não se dar atenção ao tratamento de erros em
aplicações. Por isso, é importante reforçar aqui a necessidade do planejamento no
tratamento de erros.
w
w
w
Java define um conjunto de classes que representam erros. Como existe uma grande diversidade de erros
possíveis, existem muitos tipos nativos da linguagem, cada um tratando um conjunto específico de problemas.
Tais exceções usualmente são definidas dentro de cada um dos pacotes, podendo ser subdivididas em diversos
r
conjuntos.
.b
O pacote java.io, por exemplo, possui uma família de classes de exceção derivadas de IOException e o
pacote java.sql contém uma família derivada de SQLException.
m
Através da extensibilidade oferecida pela linguagem, é possível criar tipos próprios (classes) de exceções e, para
co
isso basta estender a classe java.lang.Exception.
Cada método pode lançar uma ou mais exceções, ou seja, em vez de retornar um valor (ou simplesmente
g.
terminar a execução normalmente e retornar para o ponto em que o método foi chamado), o método sinaliza o
código chamador, informando que aconteceu algo inesperado. O mecanismo de tratamento de erros em Java
permite reconhecer esta sinalização e executar alguma ação de tratamento do erro ocorrido.
in
Conheça agora a hierarquia de classes de exceções em Java e o funcionamento detalhado do mecanismo de
nn
Embora o tratamento de erros esteja associado à idéia de Exceptions, existe uma super classe de Exception
denominada Throwable que, como diz o próprio nome, representa algo que pode ser lançado, interrompendo a
.tr
execução de um método.
w
As principais subclasses de Throwable, que definem a sua própria hierarquia são java.lang.Error e
java.lang.Exception.
r
.b
13.1.2 A classe Error
A classe Error representa condições anormais graves e geralmente irrecuperáveis. Geralmente, os aplicativos não
m
precisam se preocupar com o tratamento destes erros.
co
As principais subclasses de java.lang.Error encontradas no pacote java.lang são:
• ExceptionInInitializerError: lançado pela JVM para reportar falha de inicialização de variável
•
g.
static ou falha em um bloco de inicialização (não confundir com construtor);
StackOverflowError: lançado pela JVM para reportar o uso excessivo da pilha de métodos devido
a alguma recursividade muito profunda (normalmente infinita);
in
• NoClassDefFoundError: lançado pela JVM para reportar falha de localização de um arquivo .class
(normalmente trata-se de um problema na composição do classpath);
nn
• AssertionError: lançado pela JVM para reportar uma condição de assertiva falsa (veja detalhes
mais adiante).
ai
.tr
w
w
w
r
tratamento obrigatório. Normalmente representam erros do próprio programador, que podem ser evitados
.b
com código robusto. São subclasses de RuntimeException.
m
co
g.
in
nn
Todas as exceções derivadas da classe RuntimeException são chamadas de unchecked exceptions, porque,
ao contrário das subclasses de Exception, o compilador não verifica se foi criado um código de tratamento para
elas.
r
.b
m
co
g.
in
nn
através de uma variável que não aponta para nenhum objeto, isto é, tem o valor null;
• ArrayIndexOutOfBoundsException: ocorre quando se acessa uma posição que não existe em
w
um array;
• ArithmeticException: operações matemáticas podem gerar erros. Exemplo: dividir qualquer
w
1. package br.com.trainning.exemplos;
2.
3. public class ExemploRuntimeException {
4. public static void main(String[] args) {
5. int i = 23;
6. int j = 0;
7. double res = i / j;
r
8. System.out.println(i + " / " + j + " = " + res);
.b
9.
10. }
11.}
m
Código 13.1 - ExemploRuntimeException.java
co
g.
in
nn
ai
.tr
w
w
w
r
.b
m
co
Diagrama 13.4 - arquitetura de exceptions
g.
A criação da exception é feita criando uma instância da classe desejada utilizando a instrução new.
in
Exception e = new Exception();
nn
O método avisa a possibilidade de lançamento de uma Exception utilizando a instrução throws em sua
ai
declaração.
public void metodoQueLançaException() throws Exception { }
.tr
Para capturar uma exceção, isto é, programar uma operação que seja executada caso a exceção ocorra, utiliza-se
w
as instruções try-catch.
try {
obj.metodoQueLancaException();
w
r
ou
.b
throw variavelDoTipoException;
m
Veja a seguir a criação e lançamento de uma exceção: a classe ExemploLancaRuntimeException lança uma
exceção se não receber nenhum parâmetro de entrada no método main, caso contrário, imprime o parâmetro
recebido.
co
1. package br.com.trainning.exemplos;
2.
3. public class ExemploLancaRuntimeException {
4.
5.
public static void main(String[] args) {
if(args.length > 0) {
g.
in
6. System.out.println("Valor do parametro recebido = " + args[0]);
7. } else {
8. throw new RuntimeException();
9. }
nn
10. }
11.}
Perceba que foi lançada uma exceção do tipo java.lang.RuntimeException e uma mensagem de erro foi
w
impressa na console. Este formato de mensagem que foi impressa é denominado stackTrace.
r
.b
No exemplo utilizado foi lançada uma RuntimeException, que é uma unchecked exception, e isso significa que
o compilador ignora a exceção, sem verificar se existe um tratamento, ou mesmo se o método em que ela ocorre
sinaliza a possibilidade de lançamento da exceção ao código chamador. A seguir, a classe foi alterada para lançar
m
uma checked exception.
co
1. package br.com.trainning.exemplos.erros;
2.
3. public class ExemploLancaException {
4. public static void main(String[] args) {
5.
6.
7.
8.
if(args.length > 0) { g.
System.out.println("Valor do parametro recebido = " + args[0]);
} else {
throw new Exception();
in
9. }
10. }
11.}
nn
Como neste exemplo a exceção é checked o compilador acusa um erro, porque não foi feita a sinalização no
w
método da possibilidade de lançamento da exceção. Esta sinalização é feita através da instrução throws na
declaração do método. Veja como isso é feito a seguir.
r
exceptions> {
.b
// Instruções
}
m
Sintaxe para declaração da <lista de exceptions>
tipoException, ..., tipoException
co
No próximo exemplo é utilizada a classe File que faz parte do pacote java.io. Esta classe é utilizada para
manipulação de arquivos e diretórios. Seu construtor recebe uma String representando o nome de um arquivo,
e o método createNewFile() é utilizado para criá-lo. No entanto, analisando-se o javadoc da classe File e o
g.
método que será usado, percebe-se que uma IOException poderá ser lançada: esta é uma checked exception
e, como já foi visto, é preciso tratar o erro.
in
nn
ai
.tr
w
w
w
r
.b
Tente utilizar o método sem se preocupar em tratar o erro e compilar a classe para entender melhor o problema.
m
1. package br.com.trainning.util.erros;
2.
co
3. import java.io.File;
4.
5. public class CriadorArquivos {
6.
7. public void criarArquivo(String name) {
8.
9.
10.
// com o nome name
File f = new File(name);
g.
// Criação de um objeto da classe File representando o arquivo
in
11. // Tentativa de criar um arquivo em disco
12. f.createNewFile();
13. }
14 }
nn
r
.b
Veja, primeiramente, como indicar que a exceção deverá ser relançada com a utilização da instrução throws.
m
1. package br.com.trainning.util;
2.
co
3. import java.io.File;
4. import java.io.IOException;
5.
6. public class CriadorArquivos {
7.
8.
9.
10.
11.
g.
public void criarArquivo(String name) throws IOException {
// Criação de um objeto da classe File representando o arquivo
// com o nome name
File f = new File(name);
in
12. // Tentativa de criar um arquivo em disco
13. f.createNewFile();
14. }
nn
15.}
Neste caso, está se “jogando a batata quente” para o método que chamou, ou seja, se o erro acontecer, ele não
será tratado, mas simplesmente relançado para a classe que chama este método que, por sua vez, irá enfrentar o
.tr
1. package br.com.trainning.util;
2.
3. public class TesteCriadorArquivosSemTratamentoErro {
4.
w
8. }
9.}
co
g.
in
nn
ai
.tr
w
w
w
r
Sintaxe para bloco de tratamento de exceções
.b
try {
// instruções que podem gerar/lançar exceções
m
} catch (TipoException e) {
// código que deverá ser executado caso uma Exception seja lançada
}
co
Qualquer tipo de Throwable pode ser capturado utilizando-se a instrução catch, incluindo todos os tipos de
Error, Exception e RuntimeException.
g.
Veja o que ocorre se qualquer parte do código dentro do bloco try lançar uma exceção da classe especificada,
in
ou derivada da classe especificada na instrução catch,:
Se o programa não lançar uma exceção dentro do bloco try, então o código dentro do bloco da instrução catch
ai
No entanto, se o código dentro do bloco try gerar uma exceção diferente da especificada na cláusula catch,
então o método é finalizado imediatamente. Com sorte, o chamador terá uma cláusula catch capaz de capturar o
w
r
11. } catch (IOException e) {
.b
12. System.out.println("---------- Tratamento do Erro ---------");
13. System.out
14. .println("Nao foi possível criar o arquivo! "+e.getMessage());
15. System.out.println("--------------------------------------");
m
16. }
17. }
18.}
co
Código 13.7 - TesteCriadorArquivos.java com tratamento de exceções
g.
Agora a compilação e execução da classe serão bem sucedidas, e caso aconteça uma IOException na criação
do arquivo, todas as mensagens do bloco catch serão impressas.
in
Voltando ao exemplo anterior ExemploRuntimeException (Código 13.1), observe como alterar o código
para que a mensagem de erro gerada pela JVM não seja impressa, e para que se apresente uma mensagem de
nn
erro adequada.
1. package br.com.trainning.exemplos.exceptions;
ai
2.
3. public class ExemploRuntimeException {
4.
5. public static void main(String[] args) {
.tr
6. int i = 23;
7. int j = 0;
8. try {
w
9. double res = i / j;
10. // Impressão do resultado da divisão
11. System.out.println(i + " / " + j + " = " + res);
w
16.}
.b
exceções.
m
Método printStackTrace( )
Este método é bastante útil para os desenvolvedores ou responsáveis pela manutenção do sistema porque dá
co
uma visão detalhada do erro, indicando qual o método que originou a exceção, assim como, cada um dos
métodos que a chamaram, além de mostrar a localização da linha responsável pelo lançamento da exceção. Essa
informação é impressa na console e é chamada de stack trace (histórico de passagens) da exceção.
Método getMessage( ) g.
Este método retorna apenas a mensagem da exceção que é definida no momento da construção da Exception,
in
utilizando o construtor que recebe uma String como parâmetro.
Normalmente a stack trace da exceção não é impressa para o usuário, porque esta informação é útil apenas para
nn
o desenvolvedor / administrador. O método getMessage, por sua vez, não é muito útil para fazer depuração de
aplicações.
ai
r
12. System.out.println(i + " / " + j + " = " + res);
13. } catch (ArithmeticException e) {
.b
14. System.out.println("---------- Tratamento do Erro ---------");
15. System.out.println("Mensagem de erro para o usuario:");
16. System.out.println("Divisao invalida! " + e.getMessage());
m
17. System.out.println("\nSTACK TRACE:");
18. e.printStackTrace();
19. System.out.println("--------------------------------------");
20. }
co
21. }
22.}
Código 13.9 - ExemploMetodosThrowable.java
g.
in
nn
ai
.tr
w
w
w
Quando um código lança uma exceção, ele interrompe o fluxo de execução. Contudo, isso é um problema quando
o código utiliza recursos que precisam ser descartados, como por exemplo, conexões com o banco de dados.
Para evitar repetição de código na estrutura try / catch, existe a estrutura finally, que será executada
r
sempre, independentemente do método ter gerado ou não uma exceção.
.b
Sintaxe para utilização da instrução finally:
m
try {
// instruções que podem gerar/lançar exceções
} catch (TipoException e) {
co
// código que deverá ser executado caso uma exceção seja lançada
}
finally {
}
// liberação de recursos, log, etc.
Quando o código lança uma exceção que não é capturada por nenhuma estrutura catch:
w
r
11. // Impressão do resultado da divisão
.b
12. System.out.println(i + " / " + j + " = " + res);
13. }
14.
15. catch (ArithmeticException e) {
m
16. System.out.println("---------- Tratamento do Erro ---------");
17. System.out.println("Mensagem de erro para o usuario:");
18. System.out.println("Divisao invalida! " + e.getMessage());
19. System.out.println("\nSTACK TRACE:");
co
20. e.printStackTrace();
21. System.out.println("--------------------------------------");
22. }
23.
24.
25.
26.
27. }
finally {
}
System.out.println("Passou pelo finally");
g.
in
28.}
.b
Importante
m
A estrutura de tratamento de exceções pode ser constituída por uma instrução try e uma
instrução finally, sem a presença da instrução catch. Veja o exemplo de código a seguir:
co
1. public class ExemploTryFinally {
2.
3.
4.
public static void main(String[] args) {
int i = 23;
int j = 0;
g.
in
5.
6. try {
7. double res = i / j;
8. // Impressão do resultado da divisão
nn
14.}
Em muitos casos cada tipo de erro terá um tratamento diferente. Para conseguir este tratamento diferenciado são
utilizadas diversas instruções catch, uma para cada exceção.
r
// instruções que podem gerar/lançar exceptions
.b
}
catch (TipoDaExcecao1 e) {
// logs, codigo
m
}
catch (TipoDaExcecao e) {
// logs, codigo
co
}
finally { //opcional
// liberacao de recursos, log, etc...
} g.
Pode-se colocar quantas instruções catch forem necessárias, contudo existem duas condições para organizá-las:
in
• não é permitido colocar instruções catch para checked exceptions que não têm possibilidade de
nn
r
11. double res = i / j;
.b
12. System.out.println(i + " / " + j + " = " + res);
13. } catch (ArithmeticException e) {
14. System.out.println("---------- Tratamento do Erro ---------");
15. System.out.println("Mensagem de erro para o usuario:");
m
16. System.out.println("Divisao invalida! " + e.getMessage());
17. System.out.println("\nSTACK TRACE:");
18. e.printStackTrace();
19. System.out.println("--------------------------------------");
co
20. } catch (NumberFormatException e) {
21. System.out.println("---------- Tratamento do Erro ---------");
22. System.out.println("Mensagem de erro para o usuario:");
23. System.out.println("Dados invalidos! " + e.getMessage());
24.
25.
26.
27.
System.out.println("\nSTACK TRACE:");
e.printStackTrace();
g.
System.out.println("--------------------------------------");
} catch (ArrayIndexOutOfBoundsException e) {
in
28. System.out.println("----------Tratamento do Erro ---------");
29. System.out.println("Mensagem de erro para o usuario:");
30. System.out.println("Digite dois parametros no main! "+e.getMessage());
nn
36. }
37. }
38.}
.tr
co
superclasse de todas. Com isso, não haveria preocupação com o tipo de exceção que está sendo lançada por
cada método. No entanto, esta prática não é recomendada, porque qualquer que seja o erro, a mesma ação será
executada.
g.
Outra opção, disponível a partir do Java 7, quando o tratamento de várias exceções deve ser o mesmo, é utilizar o
recurso denominado multi-catch.
in
Sintaxe para multi-catch
nn
try {
// instruções que podem gerar/lançar exceptions
}
catch (TipoDaExcecao1 | TipoDaExcecao2 | TipoDaExcecao3 e) {
ai
// logs, codigo
}
.tr
finally { //opcional
// liberacao de recursos, log, etc...
}
w
w
w
r
12 } catch (ArithmeticException | NumberFormatException |
13 ArrayIndexOutOfBoundsException e ) {
.b
14 System.out.println("---------- Tratamento do Erro ---------");
15 System.out.println("Ocorreu o seguinte erro:"
16 + e.getClass().getName());
17 System.out.println("Com a seguinte mensagem : " + e.getMessage());
m
18 System.out.println("\nSTACK TRACE:");
19 e.printStackTrace();
20 System.out.println("--------------------------------------");
co
21 } finally {
22 System.out.println("Passou pelo finally");
23 }
24 }
25 }
g.
Código 13.13 - ExemploMultiCatch.java
in
nn
ai
.tr
w
w
w
Por exemplo: espera-se que o usuário entre com uma idade válida, mas recebe-se o número 1000. Neste caso
pode ser criada uma exceção DadosInvalidosException que será lançada sempre que um usuário fornecer
dados inválidos.
r
.b
É uma boa prática tentar sempre reutilizar as próprias exceções fornecidas pela linguagem. Apenas em casos nos
quais nenhuma das exceções pré-existentes é aplicável deve ser criada uma exceção customizada.
m
Lembre sempre que a classe Exception é uma classe como outra qualquer, portanto filha de Object. Dessa
co
maneira, fica fácil entender que para criar uma nova classe de Exception basta apenas estendê-la, como seria
feito com qualquer outra.
1. package br.com.trainning.exceptions;
2.
g.
in
3. public class NumeroInvalidoException extends Exception {
4.
5. }
nn
Uma funcionalidade útil na criação de exceções customizadas é permitir que o código de tratamento de erros
ai
recupere uma mensagem de erro descritiva da exceção. Isto pode ser feito através do método getMessage(),
herdado de Throwable, mas como inicializar a mensagem para o valor desejado? A única forma de fazer isso é
.tr
passar a mensagem por meio do construtor da superclasse, à classe Exception definindo um construtor na
classe que deriva Exception, e na primeira linha passar a mensagem de erro fazendo a chamada ao
w
r
.b
Veja que na classe Endereco apresentada a seguir, será validado o número da rua que deverá ser maior do que
zero, caso contrário o método lançará uma Exception chamada NumeroInvalidoException.
m
1. package br.com.trainning.model;
co
2.
3. import br.com.trainning.exceptions.NumeroInvalidoException;
4.
5. public class Endereco {
6. private String rua;
7.
8.
9.
10.
private int numero; g.
// Como o construtor chama o método setNumero é necessário indicar na
// assinatura que ele pode lançar uma exception NumeroInvalidoException
public Endereco(String rua, int numero) throws NumeroInvalidoException {
in
11. this.setRua(rua);
12. this.setNumero(numero);
13. }
nn
19. }
20. }
21. public void setRua(String rua) {
22. this.rua = rua;
.tr
23. }
24. public String getRua() {
25. return rua;
26. }
w
30.}
1. package br.com.trainning.model;
2.
3. import br.com.trainning.exceptions.NumeroInvalidoException;
4.
5. public class TesteEnderecoRelancandoErro {
r
6.
7. public static void main(String[] args) throws NumeroInvalidoException {
.b
8. Endereco e = new Endereco("Av. Jabaquara", 0);
9. }
10.}
m
Código 13.17 - TesteEnderecoRelancandoErro.java
co
1. package br.com.trainning.model;
2.
3. import br.com.trainning.exceptions.NumeroInvalidoException;
4.
5. public class TesteEnderecoTratandoErro {
6.
7.
8.
public static void main(String[] args) {
try {
g.
in
9. Endereco e = new Endereco("Av. Jabaquara", 0);
10. } catch (NumeroInvalidoException e) {
11. e.printStackTrace();
nn
12. }
13. }
14.}
Não se deve incluir em uma classe de exceção nenhuma rotina de tratamento de erros pois:
r
• Uma mesma classe de exceção pode ser aproveitada em vários projetos, cada um
.b
com sua política de tratamento de erros
m
1. package br.com.trainning.exceptions;
2.
3. public class GlobalcodeException extends Exception {
co
4.
5. public GlobalcodeException(String mensagem, Exception e) {
6. super(mensagem, e);
7. e.printStackTrace();
8.
9.
10.
11.
}
this.print();
// Envia e-mail para o responsável
15.
16. public void print() {
17. System.out.println("============== Tratando Erro ===============");
18. System.out.println(getMessage());
19. System.out.println("PrintStackTrace: ");
ai
20.
21. // Obtivemos a exceção causadora do problema passada como parametro
22. // no construtor
.tr
23. printStackTrace();
24. System.out.println("============ Fim do Tratamento =============");
25. }
26.}
w
1. Capturar uma exceção que não pode ser lançada no bloco try
1. package br.com.trainning.exemplos.erros;
2.
3. import java.io.IOException;
4.
5. public class ExemploCatchInvalido {
6. public static void main(String[] args) {
r
7. try {
8. System.out.println("Dentro do bloco try");
.b
9. } catch (IOException e) {
10. e.printStackTrace();
11. }
m
12. }
13 }
Código 13.20 - ExemploCatchInvalido.java
co
g.
in
nn
ai
.tr
w
w
w
1. package br.com.trainning.exemplos.erros;
2.
r
3. public class ExemploMultiplasExceptions {
4. public static void main(String[] args) {
.b
5. int i = 23;
6. int j = 0;
7. try {
8. double res = i / j;
m
9. // Impressão do resultado da divisão
10. System.out.println(i + " / " + j + " = " + res);
11. } catch (RuntimeException e) {
co
12. System.out.println("Capturei uma RuntimeException");
13. } catch (ArithmeticException e) {
14. System.out.println("Divisao invalida! ");
15. }
16. }
17.} g.
Código 13.21 - ExemploMultiplasExceptions.java com captura de exceções inválida
in
nn
ai
.tr
w
w
w
r
• lançar subclasses das checked exceptions declaradas no método original;
.b
• lançar quaisquer runtime exceptions
m
1. package br.com.trainning.teste;
2.
3. public interface Teste {
co
4.
5. public void metodoA() throws GlobalcodeException, java.text.ParseException;
6.}
g.
Código 13.22 - Teste.java
A seguinte implementação não é válida porque gera um erro de compilação: ela altera a assinatura do método
in
definido na interface Teste, lançando uma IOException em vez de GlobalcodeException, ou
ParseException.
nn
1. package br.com.trainning.teste;
2.
3. import java.io.*;
ai
4.
5. public class ClasseImplementadora implements Teste {
6.
.tr
co
1. package br.com.trainning.teste;
2.
3. public class OutraClasseImplementadora implements Teste {
4.
5.
6.
7. }
System.out.println("outro metodoA");
g.
public void metodoA() throws java.text.ParseException, NullPointerException {
in
8.}
Veja outra implementação válida, porque lança uma subclasse de uma das exceções declaradas no método
original.
ai
1. package br.com.trainning.exceptions;
2.
.tr
6. }
7.}
r
.b
m
co
g.
in
nn
ai
.tr
w
w
w
1 package
r
br.com.trainning.exemplos.exceptions; 2
.b
3 import java.io.FileWriter;
4 import java.io.IOException;
5
6 public class ExemploTryTradicional {
m
7
8 public static void main(String[] args) {
9 String nomeArquivo = "arquivo1.txt";
10 FileWriter out = null;
co
11 try {
12 out = new FileWriter(nomeArquivo);
13 //escrita no arquivo
14 out.write("texto qualquer");
15 } catch (IOException ex) {
16
17
18
//tratamento do erro
} finally {
if (out != null) {
g.
in
19 try {
20 out.close();
21 } catch (IOException ex) {
22 //tratamento do erro ao fechar o fluxo
nn
23 }
24 }
25 }
26 }
27 }
ai
erro;
• Caso seja feito o relançamento da exceção dentro do bloco catch e dentro do bloco finally, a exceção
w
ambos;
A partir do Java 1.7 a instrução try foi aperfeiçoada com o suporte à liberação automática de recursos. Esse
recurso é denominado try-with-resources.
r
}
.b
catch (TipoDaExcecao e) {
// tratamento de erros
m
}
A declaração de recursos consiste na declaração e inicialização dos objetos que devem ser fechados ao término
co
da execução do código. Vários objetos podem ser declarados, separados por ; (ponto e vírgula). Veja o mesmo
exemplo do código anterior utilizando a nova sintaxe:
1 package
br.com.trainning.exemplos.exceptions; 2
3 import java.io.FileWriter;
g.
in
4 import java.io.IOException;
5
6 public class ExemploTryWithResources {
nn
16 }
Importante
Todas as classes que representam recursos que podem ser “fechados” automaticamente pelo
r
try-with-resources apresentam uma característica em comum. Todos implementam a interface
.b
java.lang.AutoCloseable, criada no Java 1.7. Um objeto que implementa essa interface
tem seu método close chamado ao final do bloco try, independente do lançamento de exceção,
m
eliminando a necessidade da chamada no bloco finally. Ou seja, o método close() é sempre chamado antes de
entrar nos blocos de tratamento de exceção.
co
g.
in
nn
ai
.tr
w
w
w
Uma assertiva é uma expressão booleana que deve retornar true somente se o seu código estiver funcionando
corretamente. Se a expressão retornar false um AssertionError é gerado, interrompendo o programa.
r
.b
As expressões de assertivas podem ser habilitadas ou desabilitadas via linha de comando, por isso não devem ser
utilizadas expressões envolvendo métodos que modificam o estado dos objetos.
m
Sintaxe para o uso de assertivas:
co
assert exprCondicional;
ou
assert exprCondicional : mensagem;
g.
Na segunda opção, caso a expressão condicional seja falsa, é gerado um AssertionError contendo a
in
mensagem de erro definida em mensagem. A mensagem não necessita ser uma String podendo ter qualquer
valor primitivo ou de objetos.
nn
Para exemplificar o uso de assertivas imagine um cenário bancário com as classes apresentadas no diagrama a
seguir:
ai
.tr
w
w
w
Veja a seguir o código para a enumeração TipoConta e a classe Banco. Observe que esta primeira versão da
classe Banco apresenta um erro no código, pois faltou a criação da instância de ContaPoupanca. Este erro será
percebido ao executar um código de teste com assertivas habilitadas.
r
.b
1. package br.com.trainning.teste;
2.
3. public enum TipoConta {
4. CONTA_CORRENTE, CONTA_POUPANCA
m
5. }
co
1. package br.com.trainning.teste;
2.
3. public class Banco {
4.
5.
6.
public Conta abrirConta(TipoConta tipo) {
Conta conta = null;
if(tipo==TipoConta.CONTA_CORRENTE) {
g.
in
7. conta = new ContaCorrente();
8. }
9. assert conta != null : "a conta nao foi criada!!";
10. return conta;
nn
11. }
12.}
1. package br.com.trainning.teste;
2.
.tr
10. }
11.}
w
r
javac –source 1.4 br/com/trainning/*.java
.b
Para ignorar assertivas, compilar com a diretiva de compilação –source 1.3:
• javac –source 1.3 br/com/trainning/*.java
m
13.6.2 Execução com assertivas
Para executar uma classe com assertivas utiliza-se as diretivas apresentadas na Tabela 13.1:
co
Tabela 13.1 - diretivas de linha de comando para assertivas
g.
in
nn
ai
As diretivas –ea e –da - são na verdade uma abreviação para –enableassertions e –disableassertions- ,
sendo que as duas formas são válidas. As diretivas de execução podem ser usadas em combinação
.tr
Neste exemplo habilita-se assertivas em geral, mas elas são desabilitadas para o pacote br.com e seus
subpacotes.
w
Após entender como habilitar a compilação e execução de códigos com assertivas é possivel executar e avaliar o
código de exemplo da classe TesteBanco.
m
é apresentado o código da classe Banco, com o erro corrigido.
co
1. package br.com.trainning.teste;
2.
3. public class Banco {
4. public Conta abrirConta(TipoConta tipo) {
5. Conta conta = null;
6.
7.
8.
9.
if(tipo==TipoConta.CONTA_CORRENTE) {
conta = new ContaCorrente();
} else {
conta = new ContaPoupanca();
g.
in
10. }
11. assert conta != null : "a conta nao foi criada!!";
12. return conta;
nn
13. }
14.}
1. import java.io.IOException;
r
2.
.b
3. public class Super {
4.
5. void method() throws IOException {
6. System.out.println("Inside Super");
m
7. throw new IOException();
8. }
9.
10. public static void main(String[] args) {
co
11. Base base = new Base();
12. try {
13. base.method();
14. } catch (IOException e) {
15.
16.
17.
18.}
}
}
System.out.println("Inside catch"); g.
in
19.
20. class Base extends Super {
21. void method() {
22. System.out.println("Inside Base");
nn
23. }
24. }
ai
A) Prints:
Inside Base
.tr
B) Prints:
Inside Super
C) Prints:
w
Inside Super
Inside catch
w
1. import java.io.IOException;
2.
3. public class SuperFoo {
4.
5. public void methodOne(int a, int b) throws IOException{
6. //method implementation
7. }
8.
9. public void methodTwo(float a, float b) {
r
10. //method implementation
.b
11. }
12. }
13.
14. public class Foo extends SuperFoo{
m
15.
16. }
co
Which of the following are legal method declarations to add to the class Foo? Assume that each method is the only
one being added, and all the necessary imports and return statements are done.
r
7.}
.b
m
You want to make this method throw an IOException if the method everythinkOk() returns a value of
false. Which changes achieve this? (Choose 2 answers)
co
A) Add at line 2: IOException exception;
B) Add at line 4: throw exception;
C) Add at line 4: throw new IOException();
D) Add at line 6: throw new IOException();
g.
E) Modify the method declaration to indicate that IOException might be thrown.
in
4. Which of the following statements are true? (Choose 3 answers)
nn
r
14
.b
m
co
g.
in
nn
ai
.tr
Diagramas de Seqüência
w
r
• ilustrar e validar a lógica que está sendo implementada relativa à determinada funcionalidade;
.b
• identificar a necessidade da criação de novos métodos nas classes, que não foram previstos no
diagrama de classes original;
•
m
identificar quais classes serão as mais complexas;
• documentar fluxos de negócios de uma empresa;
• documentar fluxos de operações em sistemas legados;
co
• Identificar gargalos na modelagem de um aplicativo.
•
g.
desenvolvedores , como base para a implementação do código;
in
• analistas e arquitetos,para melhoria da modelagem;
• analistas de processo, para um melhor entendimento do negócio.
nn
.
ai
.tr
w
w
w
14.1.1 Objetos
Nos diagramas de seqüência, objetos são representados por caixas contendo o nome (opcional) e a classe do
objeto.
r
.b
m
co
Diagrama 14.1 - representação de objetos no diagrama de seqüência
g.
Os objetos são mostrados no topo do diagrama e o tempo é representado de cima para baixo, através de uma
linha tracejada chamada linha de vida do objeto.
in
nn
ai
.tr
w
w
w
Mensagens são representadas por setas que conectam uma linha de vida a outra. As mensagens representam as
interações entre os objetos, ou seja, métodos que um objeto chama em outro. Acima da seta escreve-se o nome
da mensagem que (em nível de projeto ou implementação) será o nome do método implementado pelo objeto
receptor da mensagem, e opcionalmente, é possível numerar as mensagens.
r
A localização da mensagem na linha de vida, representa o momento em que ela ocorre dentro da seqüência.
.b
Mensagens que se localizam mais acima do diagrama são chamadas mais cedo do que as que se localizam mais
abaixo no diagrama. Alem disso, durante o processamento da mensagem representa-se uma caixa de ativação na
m
linha de vida do objeto.
co
• mensagem de criação de objetos (estereótipo <<create>>);
• mensagem de invocação de método
• mensagem de valor retornado por uma invocação a método.
g.
in
nn
ai
m
Mensagens síncronas são aquelas nas quais o objeto que envia a mensagem aguarda o término do seu
processamento. O diagrama acima corresponde ao seguinte código:
co
conta.imprimeExtrato();
Também é possível representar a passagem de parâmetros nas mensagens como se vê no seguinte diagrama:
g.
in
nn
Mensagens assíncronas são aquelas nas quais o objeto que envia a mensagem não aguarda o término do seu
processamento. Observe acima que a seta é representada aberta.
m
Mensagens de retorno representam os valores retornados pelos métodos em seu processamento. São
representadas por setas pontilhadas. Acima da seta é indicado o nome de um elemento que representa o valor de
retorno do método. Mensagens de retorno são elementos opcionais do diagrama de seqüência e seu uso vai
co
depender do nível de detalhe desejado para o diagrama. Algumas boas práticas relacionadas ao uso de
mensagens de retorno são:
• não modelar um valor de retorno quando é óbvio o que está sendo retornado;
• g.
modelar um valor de retorno quando precisar referenciá-lo em outro lugar.
in
nn
ai
.tr
co
Na modelagem da troca de mensagens entre objetos, existem situações nas quais uma mensagem pode precisar
que uma determinada condição seja atendida para ser enviada. Em UML 1.4 estas condições são chamadas
g.
condições de guarda, sendo representadas por uma expressão booleana entre colchetes antes da mensagem.
in
nn
ai
.tr
if ( carrinho != null) {
w
carrinho.addItem(item);
}
w
m
O diagrama acima corresponde ao seguinte código:
co
if ( carrinho == null) {
carrinho = new CarrinhoCompras();
} else {
carrinho.addItem(item);
}
g.
in
14.1.3 Fragmentos
nn
A partir da versão 2.0 foram criados novos elementos na UML, que permitem o agrupamento de diversas
mensagens em fluxos mais complexos. Estes elementos são denominados fragmentos e consistem em uma caixa
que engloba as mensagens desejadas. Existem diversos tipos de fragmentos, de acordo com a operação que se
ai
deseja representar.
.tr
co
O diagrama a seguir representa uma chamada condicional if/else utilizando fragmentos.
g.
in
nn
ai
.tr
w
r
.b
m
Diagrama 14.13 - chamadas dentro de um laço
co
g.
in
nn
ai
.tr
w
w
w
Para complementar o estudo de UML, acompanhe a seguir, um estudo de caso para um sistema de reservas de
passagens aéreas e os respectivos diagramas.
r
• o sistema permite a um passageiro a compra de passagens aéreas;
.b
• o processo de compras é dividido em duas etapas: na primeira, é criada uma reserva para um
determinado vôo que tem um prazo de validade; na segunda etapa é feita a confirmação da reserva e é
gerado um bilhete para o passageiro;
m
• o sistema aceita três formas de pagamento: cartão de crédito, desconto em carteira ( para funcionários da
empresa aérea) e programa de milhagem;
co
• o passageiro pode acumular milhas voadas se fizer parte do programa de milhagem e neste caso ele
possui um cartão fidelidade;
• os assentos em uma aeronave possuem preços diferenciados de acordo com a categoria a que
pertençam: primeira classe, classe econômica ou outras. g.
Interpretando essas definições será modelado um diagrama de classes (estático) e os principais diagramas de
in
seqüência (dinâmico).
nn
ai
.tr
w
w
w
Ao se aproximar da implementação, foram criadas classes auxiliares tais como controladores do fluxo do
aplicativo e telas e classes de integração com outros sistemas ou com banco de dados. Nesse exemplo também
ai
foram criadas classes para interface com o usuário (ReservasView) e para acesso ao banco de dados
(ReservasDAO).
.tr
w
w
w
Objetivo:
Praticar o desenvolvimento de diagramas de seqüência.
Tabela de atividades:
Atividade OK
r
1. Criar um diagrama de seqüência para o seguinte fluxo:
.b
• O usuário entra na página e com isso aciona a classe CatalogoProdutosView.jsp
• O usuário seleciona um produto para adicionar no carrinho de compras.
m
co
g.
in
nn
ai
.tr
•
w
r
.b
m
• Quando o usuário clica no botão recalcular o valor é recalculado.
co
Imagine que as classes CatalogoProdutosView.jsp e CarrinhoComprasView.jsp são classes
como outras quaisquer, por exemplo CatalogoProdutoGUI e CarrinhoComprasGUI.
g.
in
nn
ai
.tr
w
w
w
r
.b
m
co
g.
in
nn
ai
.tr
r
Explicação:
.b
Lembrar que, nas classes, os atributos são inicializados automaticamente, entretanto, nos blocos de códigos a
inicialização deve ser explícita. Neste caso, b é um atributo local do método method, que não foi inicializado.
m
A letra D está incorreta porque pode se atribuir um valor literal inteiro positivo a uma variável char, desde que
co
tenha um tamanho compatível (valor menor que 16 bits, ou seja, menor que 65535).
A letra E refere-se ao fato de terem sido utilizados varargs na assinatura do método main. Tanto um array de
g.
Strings ( String[] ) como varargs ( String... ) poderiam ser utilizados.
in
CAPÍTULO 4 - Sobrecarga de métodos
Questão 1
nn
Resposta: B
Explicação:
A chamada ao método method é feita passando-se duas variáveis short como parâmetros. Como não existe
ai
nenhum método com a assinatura exatamente igual aos tipos de parâmetros passados, ocorre a promoção dos
tipos até encontrar-se um método que seja aplicável. Os tipos são promovidos para int e encontra-se o método
desejado.
.tr
Caso não existisse o método method(int,int), ocorreria um erro de compilação reclamando que os métodos
method(int, long) e method (long, int) são ambígüos.
w
CAPÍTULO 5 - Construtores
w
Questão 1
w
Resposta: D, E
Explicação:
Qual dos construtores é escolhido?
r
Métodos estáticos não conseguem acessar atributos não estáticos.
.b
CAPÍTULO 8 Herança
m
Questão 1
Resposta: D
co
Explicação:
Não é possível acessar métodos sobrescritos que estejam em uma hierarquia superior a da superclasse.
Questão 2
Resposta: C
g.
in
Explicação:
Basta lembrar a seqüência de inicialização de objetos em Java: ao chamar-se o construtor de Sub, é alocado
nn
espaço em memória e são criados os atributos da classe; em seguida estes atributos são inicializados para seu
valor padrão.
No exemplo é criado o atributo value e inicializado para 0 e na seqüência é chamado o construtor da superclasse
que invoca o método getValue. Como este método foi sobrescrito, o método da subclasse é invocado retornando
ai
o valor atual do atributo value (0). Ao retornar da chamada ao construtor o atributo é inicializado de acordo com
sua declaração, para o valor 15.
.tr
Questão 3
w
Resposta: E
Explicação:
w
Por outro lado, quando se atribui que p2=p1, a variável p2, que foi criada apenas para o método, tem sua
referência alterada. Ela não aponta mais para a variável d2, mas para a referência de d1!
Ora, ao sair do método a variável p2 deixa de existir, mostrando que a alteração de referência executada em
amethod de p2 não tem mais validade. Contudo, como a variável p1 foi alterada, conseqüentemente a variável
r
d1 também o será. As referências permaneceram as mesmas, mas a estrutura foi modificada!
.b
Questão 4
m
Resposta: B
Explicação:
co
Métodos estáticos não podem sobrescrever métodos não estáticos e vice-versa.
Questão 5
Resposta: D
g.
in
Explicação:
Variáveis e atributos finais são constantes. Entretanto, neste caso, os elementos internos do objeto
primeNumbers (array é um objeto) estão sendo modificados. Ora, essa alteração não modifica nenhuma
nn
Questão 6
.tr
Resposta: C
Explicação:
w
Os métodos finais não podem ser sobrescritos, mas podem ser utilizados normalmente em quaisquer outras
classes em que sejam visiveis. O final também não tem função de limitador de acesso.
w
w
Questão 7
Resposta: C, E
Explicação:
Questão 8
Resposta: C
Explicação:
r
Lembrar que :
.b
1. Construtores padrão (sem argumentos) são construídos apenas quando não existem construtores dentro da
classe, caso contrário, não são implementados.
m
2. Todos os construtores fazem chamada, de maneira implícita, ao construtor da superclasse, na 1a linha
super().
co
CAPÍTULO 9 - Enumerações
Questão 1
Resposta: A
Explicação:
g.
in
Compila e executa normalmente imprimindo os valores “walk swim”.
nn
Enumerações podem ter atributos, métodos e construtores, por isso as alternativas B, D e E são falsas.
Quando uma enumeração possui um construtor, ele deve ser invocado na declaração dos tipos conforme é feito
na linha 2 do código. Por isso a opção C é falsa.
ai
Os valores de uma enumeração são acessados como atributos estáticos. Estes atributos podem ser acessados a
.tr
partir do nome da classe ou de uma instância da classe. Por isso é possivel acessar os valores de uma
enumeração a partir de uma variável como é feito no código com a variável animal e a alternativa F é falsa.
w
Questão 2
w
Resposta: D
Explicação:
w
Enumerações são um tipo especial de classe e classes não podem ser declaradas internamente em um método,
portanto a linha 5 gera um erro na compilação.
r
A alternativa A é incorreta pois variáveis são consideradas como static, final e public quando criadas em
.b
interfaces
A alternativa C é incorreta pois uma classe pode estender outra classe e implementar interfaces ao mesmo tempo.
m
Questão 2
Resposta: D
Explicação:
co
Classes abstratas não podem ser instanciadas e são criadas para serem herdadas. Não existe nenhuma restrição
de herança para classes abstratas, nem é necessário que elas possuam métodos abstratos.
CAPÍTULO 11 - Polimorfismo
Questão 1
g.
in
Resposta: A
Explicação:
Lembrar das regras de cast de objetos:
nn
1. Toda subclasse pode ser atribuida para a superclasse sem a necessidade de cast explícito. (todo
Triangle é um Shape).
2. Uma referência de superclasse pode ser atribuída para uma referência de subclasse, porém é necessário
ai
No exemplo é feita uma atribuição de referência de subclasse para referência de superclasse, o que é sempre
válido de acordo com a regra número 1.
w
w
Questão 2
Resposta: A, C e D
w
Explicação:
As alternativas A, C e D estão corretas. A e D são exemplos de retorno covariante, porque as classes Mammal e
Dog são subclasses da classe Animal.
CAPÍTULO 12 - Pacotes
Questão 1
Resposta: B
Explicação:
r
Métodos sem nenhum modificador de acesso são do tipo padrão, acessiveis apenas dentro do mesmo pacote.
.b
Lembre-se que classes declaradas no mesmo arquivo pertencem ao mesmo pacote, mas esta não é a resposta
mais apropriada. Outras classes e subclasses só terão acesso aos membros padrão se estiverem no mesmo
pacote.
m
Questão 2
co
Resposta: A,C
Explicação:
Membros private são invisíveis aos elementos externo e membros sem modificador (padrão) são visiveis apenas
g.
aos elementos do mesmo pacote, portanto as alternativas B e D estão incorretas.
in
Questão 3
Resposta: D
nn
Explicação:
Para utilizar membros estáticos diretamente em uma classe a declaração de import deve iniciar com “import
static”
ai
Questão 1
Resposta: D
w
Explicação:
No método main é chamado o método method da classe Base. Este método é uma sobrescrita válida do método
w
herdado de Super. Lembre-se que na sobrescrita não é obrigatório o lançamento das exceções declaradas pelo
método original. Ocorre um erro de compilação na instrução catch (IOException e) porque a exceção
w
IOException não é lançada pelo método method da subclasse. Lembre-se que o compilador não permite a
presença de uma instrução de captura de uma checked exception que nunca é lançada no bloco try.
r
A alternativa C é uma sobrescrita inválida pois altera o tipo de retorno do método original.
.b
Métodos sobrescritos podem lançar as mesmas exceções, ou subclasses das exceções declaradas no método
original ou subconjuntos delas, ou ainda exceções do tipo RuntimeException. Desta maneira as alternativas D
m
e E estão corretas, porque ArithmeticException é uma RuntimeException e FileNotFoundException é
uma subclasse de IOException.
co
Questão 3
Resposta: C,E
g.
in
Explicação:
Quando o método everythingOk retorna false é executado o bloco de código dentro da condicional if. Para
nn
lançar IOException é necessário utilizar a instrução throw passando uma instância da exceção (alternativa C) e
sinalizar na assinatura do método que existe a possibilidade de lançamento desta exceção (alternativa E).
ai
Outra opção seria declarar e criar uma IOException para depois lançá-la na linha desejada. Perceba que a
alternativa A declara a variável exception, mas não cria uma instância de IOException. Por isso não é
possível lançar a variável não inicializada (alternativa D).
.tr
Questão 4
w
Resposta: A,B,C
Explicação:
w
Segundo a especificação de Java, métodos abstratos só podem ter os modificadores de acesso public e
protected. Note que ausência de modificadores de acesso é permitida, portanto também é possivel criar
w
Classes abstratas podem ter métodos estáticos, mas um método não pode ser ao mesmo tempo abstrato e
estático. Por isso a alternativa B está correta.
Métodos private ficam invisíveis para as subclasses que tecnicamente não são capazes de sobrescrevê-los. Um
método abstrato deve ficar visível (protected, public) para as subclasses que irão sobrescrevê-los. Logo, a
r
alternativa C também está correta.
.b
Adicionalmente, os métodos abstratos podem lançar exceções, como qualquer método.
m
co
15.2.1 Modelagem de Classes e Relacionamentos
Consultar Catálogo
r
.b
m
co
g.
in
nn
ai
.tr
w
w
w
r
.b
m
co
g.
in
nn
ai
.tr
w
w
w
r
.b
m
co
g.
in
nn
ai
.tr
w
w
w
r
.b
m
co
g.
in
nn
ai
Javadoc
.tr
Doclets
Tags padrão do Javadoc
w
r
.b
m
co
g.
in
nn
ai
.tr
Os comentários cujo conteúdo é processado pelo utilitário são aqueles contidos na notação especial /** e */,
denominados comentários para javadoc. Este tipo de comentário pode conter qualquer texto desejado para a
descrição das classes, métodos e atributos. Além disso, podem ser utilizados elementos denominados tags, que
w
terão um processamento especial pelo utilitário Javadoc, e que são precedidos pelo símbolo @.
w
Um Doclet é um programa escrito em Java utilizando a API Doclet, que especifica o conteúdo e a formatação que
deve ser gerada na utilização do utilitário Javadoc. Doclets podem ser escritos para gerar qualquer tipo de saída:
HTML,XML, RTF, PDF, e vários outros formatos.
A Sun provê um Doclet padrão para geração de documentação de APIs em formato HTML.
r
.b
Doclets também podem ser utilizados para executar tarefas especiais não relacionadas a produção de
documentação de APIs como por exemplo: é possível criar um Doclet que checa se todos os membros de uma
classe têm comentários.
m
co
16.2 Tags padrão do Javadoc
Cada tag a ser processado por um doclet tem sua funcionalidade implementada em um tipo de classe Java
especial. Cada uma destas classes é denominada taglet. Podem ser criados tags customizados escrevendo
g.
taglets, mas comumente são utilizados os próprios tags padrão do utilitário Javadoc.
in
Veja a descrição dos tags mais utilizados:
nn
@author nome
Adiciona uma seção "Author" com o nome especificado.
Cada comentário javadoc pode conter mais do que um autor em cada comentário.
ai
@deprecated textoExplicativo
.tr
Adiciona um comentário indicando que esta API não deverá mais ser utilizada, mas continuará funcionando.
Esta tag é válida para comentários : overview, pacotes, classes, interfaces, construtores, métodos e atributos.
w
O texto colocado nesta tag deve conter, pelo menos, a indicação de a partir de qual versão esta API foi
considerada deprecated, e o que deve ser utilizado em substituição ao que está deprecated.
w
Nesse caso, utiliza-se a tag {@link} que apontará para a API que substitui a que está deprecated.
w
Exemplo:
r
.b
@return descrição
Adiciona uma seção "Returns" no javadoc gerado. Este texto deve ser utilizado para descrever o valor de retorno
m
de um método.
Esta tag pode ser utilizada apenas em comentários de métodos.
co
@see referência
Adiciona uma seção "See Also" com um link ou texto. Um comentário javadoc pode conter um ou mais @see tags.
g.
Esta tag pode ser utilizada em overview, pacotes, classes, interfaces, construtores, métodos e atributos.
Se for colocado apenas um texto e não um link, coloca-se a referência entre aspas duplas.
in
Fazendo referência a atributos da mesma classe:
nn
• @see #atributo
• @see #metodo(Tipo, Tipo,...)
• @see #metodo(Tipo param1, Tipo param2,...)
ai
• @see Class#atributo
• @see Class#metodo(Type, Type,...)
w
• @see package.Class#atributo
• @see package.Class#metodo(Tipo, Tipo,...)
@since textoExplicativo
Esta tag serve para indicar desde qual versão da API este método, atributo ou classe está disponível.
Esta tag é válida em qualquer comentário: overview, pacote, classe, interface, construtor, método ou atributo.
r
.b
@throws nomeClasse descrição
Esta tag é sinônima da tag @exception.
nomeClasse indica o nome da exceção que pode ser lançada por este método.
m
Esta tag é válida apenas para métodos ou construtores.
co
Esta tag é copiada para métodos sobrescritos em subclasses somente quando a exceção está explicitamente
declarada no método sobrescrito.
g.
Veja algumas utilizações de tags para a classe Livro e a exceção CategoriaInvalidaException:
in
nn
ai
.tr
w
w
w
import
br.com.trainning.exception.CategoriaInvalidaException; /**
* Esta classe representa um Livro. Exemplo:
*
* <pre>
* Livro l = new Livro();
* l.setNome("A História da trainning");
* </pre>
r
*
*@author trainning
.b
* @version 1.0
* @see www.trainning.com.br
*/
m
public class Livro {
co
private int categoria;
private String ISBN;
private String autor;
private String editora;
public static final int ROMANCE = 1;
public static final int HISTORIA = 2;
public static final int INFORMATICA = 3;
g.
in
/**
* @param nome
* Nome do livro
* @param autor
nn
* Autor do livro
* @param editora
* Editora do livro
* @param ISBN
* ISBN do livro
ai
*/
public Livro(String nome, String autor, String editora, String ISBN) {
this.nome = nome;
.tr
this.autor = autor;
this.editora = editora;
this.ISBN = ISBN;
}
w
/**
* @param nome
* Nome do livro
w
*/
public void setNome(String nome) {
this.nome = nome;
w
/**
* @param categoria
/**
r
* @param ISBN
* ISBN do livro
.b
*/
public void setISBN(String ISBN) {
this.ISBN = ISBN;
}
m
/**
* @param autor
co
* Autor do livro
*/
public void setAutor(String autor) {
this.autor = autor;
}
/**
* @param editora
* Editora do livro
g.
in
*/
public void setEditora(String editora) {
this.editora = editora;
nn
/**
* @return nome do livro
*/
ai
/**
* @return categoria do livro
*/
public int getCategoria() {
w
return categoria;
}
w
/**
* @return ISBN do livro
*/
w
/**
/**
* @return editora do livro
*/
public String getEditora() {
return editora;
}
r
}
.b
Codigo 16.1 - Livro.java
m
co
g.
in
nn
ai
.tr
w
w
w
/**
* Esta classe representa a exceção gerada quando se tenta designar uma
* categoria inválida a um objeto.
*
* Exemplo:
*
* <pre>
* Livro l = new Livro();
* l.setCategoria(0125);
r
* </pre>
.b
*
* Caso não exista a categoria 0125 será lançada a exceção
* CategoriaInvalidaException
*
m
*@author trainning
* @version 1.0
* @see www.trainning.com.br
*/
co
public class CategoriaInvalidaException extends Exception {
}
} g.
Código 16.2 - CategoriaInvalidaException.java
in
package br.com.trainning.teste;
nn
import
br.com.trainning.model.Livro; /**
* Esta é a classe criada para testar o funcionamento
* da classe Livro.
ai
*
*
* @author trainning
.tr
* @version 1.0
*
*/
public class TesteLivro {
w
"1234-56789"); System.out.println(l.getNome());
}
}
w
• javadoc br.com.trainning.model
r
É possível também indicar onde se deseja que o javadoc seja gerado:
.b
• javadoc -d docs br.com.trainning.model
m
Desta maneira os htmls serão colocados no diretório docs.
co
E ainda é possível gerar documentação para um conjunto de subpacotes.
Veja como gerar os documentos javadoc para todos os subpacotes do pacote br.com.trainning, que são
br.com.trainning.model , br.com.trainning.test e br.com.trainning.exception e salvá-los
no diretório docs: g.
• javadoc -d docs -subpackages br.com.trainning
in
nn
ai
.tr
w
w
w
g.
in
nn
ai
.tr
w
w
w