Académique Documents
Professionnel Documents
Culture Documents
Orientação a Objetos
JavaRN - http://javarn.dev.java.net
J2EEBrasil - http://www.j2eebrasil.com.br
1
Sumário
1. Introdução 4
6. Variáveis 11
6.1 Declaração de Variáveis 12
6.2 Variáveis que Referenciam Objetos 13
8. Separadores 16
9. Operadores 17
9.1 Operadores Unários 17
9.2 Operadores Aritméticos 19
9.3 Operadores de Deslocamento 20
9.4 Operadores de Comparação 21
9.5 Operadores Bit-a-Bit 22
9.6 Operadores Lógicos de Curto-Circuito 22
9.7 Operador de Atribuição 24
11. Vetores/Arrays 30
11.1 Vetores/Arrays Multidimensionais 33
11.2 Estrutura for Aprimorada 34
2
12.8 Sobrecarga de Métodos 46
14. Encapsulamento 52
15. Herança 54
15.1 Como Definir o Relacionamento de Herança 56
15.2 Exemplo de Implementação de Herança em Java 61
16. Polimorfismo 65
16.1 Redefinição de Métodos 68
18. Interfaces 73
18.1 Breve Comparação entre Classe Abstrata e Interface 79
19. Modificadores 79
19.1 Private 79
19.2 Public 80
19.3 Protected 80
19.4 Package 80
19.5 Static 80
19.6 Final 82
19.7 Abstract 82
19.8 Transient 82
19.9 Synchronized 82
3
1. Introdução
Java é uma linguagem de programação que começou a ser desenvolvida pela Sun
Microsystems[1] por volta de 1991. Possui uma estrutura bastante parecida com as
linguagens C e C++.
Java é orientada a objetos, portanto, inclui os benefícios de reutilização de
código, extensibilidade e aplicações dinâmicas. Sintetizando, programar em Java
significar criar classes (formadas por variáveis e métodos) que encapsulam
funcionalidades em objetos reutilizáveis e que podem ser carregados dinamicamente.
Uma outra característica bastante importante dessa linguagem é o fato dela ser portável,
ou seja, uma aplicação implementada em Java pode ser executada em diversos sistemas
operacionais.
Java é uma linguagem que é primeiramente compilada e em seguida
interpretada. A Figura 1 apresenta um esquema de compilação e execução de um
programa escrito nela. O primeiro passo é a construção de um arquivo com o código
fonte, que deve ter como extensão .java. No caso do exemplo da Figura 1, o arquivo foi
chamado de HelloWorld.java. Em seguida, o código fonte é compilado e um programa
fonte em bytecodes (HelloWorld.class) é gerado. Durante a compilação, há uma
checagem de erros do código fonte. O fonte em bytecodes só será gerado se nenhum
erro tiver sido detectado. Por fim, qualquer dispositivo que execute Java será capaz de
interpretar este novo arquivo fonte e executar a aplicação. Os bytecodes são lidos e
executados (ou seja, interpretados) pela Máquina Virtual Java (JVM – Java Virtual
Machine) em um computador, em um celular, etc., além de serem independentes de
plataforma.
4
A geração dos bytecodes ou, para o exemplo, do arquivo HelloWorld.class é
feita a partir da execução do comando javac HelloWorld.java . Então, o próximo
passo é fazer com que a JVM execute o arquivo HelloWorld.java, através do comando
java HelloWorld.class . Dessa maneira, a JVM traduz o código compilado para uma
linguagem que a máquina entenda e o programa é executado.
Resumindo, um programa Java é um conjunto de instruções a serem interpretadas por
uma JVM. Como já citado, os bytecodes são independentes de plataforma. Dessa forma,
os programas em Java podem executar em qualquer plataforma de hardware ou
software que possua uma versão da JVM.
A Máquina Virtual Java é definida como uma máquina imaginária implementada
através da emulação em um software executado em uma máquina real. Ela possui uma
arquitetura que permite garantir segurança. Quando um programa Java é executado,
seus bytecodes são verificados pela JVM para que estejam de acordo com os seus
requisitos de segurança, impedindo a execução de código com alguma irregularidade.
Assim, códigos fonte com instruções que acessem áreas restritas da memória ou até
mesmo recursos do hardware não são executados pela JVM.
5
2. Estrutura de um Código Java
2.2 Classe
6
Listagem 2 - Métodos da Classe
class Passaro{
void cantar(){
}
void voar(){
}
}
2.3 Método
void cantar(){
declaracao;
instrução1;
}
void voar(){
declaração;
instrução1;
instrução2;
}
}
7
Listagem 4 - public static void main
public static void main (String[] args){
//Código do programa a ser executado
}
O próximo passo é a JVM executar o código que se encontra entre as chaves ({})
do método principal. Toda aplicação Java tem ao menos uma classe e no mínimo um
método principal ou método main. É importante saber que não existe um método main
por classe, mas sim por aplicação.
A Listagem 5 apresenta o primeiro exemplo deste material escrito em Java.
Listagem 5 - PrimeiroExemplo.Java
public class PrimeiroExemplo{
public static void main (String[] args){
System.out.println (“Primeiro Exemplo em Java”);
}
}
A palavra public indica que a classe terá um nível de acesso público, ou seja, que
ela será acessível de qualquer classe. A palavra class indica que uma classe está sendo
declarada e seu nome é PrimeiroExemplo. A { delimita o limite inicial da classe.
A segunda linha declara o método principal da classe:
A palavra public já foi descrita, portanto permite que o método seja acessado
publicamente. A palavra static será descrita posteriormente. O lugar onde fica a palavra
void é onde se deve indicar o tipo de retorno da classe. Neste caso, não há tipo de
retorno, dessa maneira o método é declarado como void. O conjunto String[] args
presentes entre () são os argumentos do método. Neste exemplo, o método possui um
8
único argumento (um vetor de Strings denominado args. A { delimita o limite inicial do
método.
A terceira linha consiste no conteúdo do método principal, formado apenas por
um comando:
9
Listagem 6 - Elementos de um Arquivo Fonte
// Declaração de Pacote
package br.com.j2eebrasil.meuPacote
// Definição da Classe
public class NomeClasse {...}
10
As palavras reservadas ou palavras chaves são aquelas que não podem ser
utilizadas como identificadores. Java possui diversas palavras reservadas, entre elas as
listadas na Listagem 9.
6. Variáveis
Variáveis são valores que são atribuídos dinamicamente, ou seja, não têm um
valor pré-definido. Podem ser definidos pelo programador, bem como por qualquer
método.
As variáveis podem pertencer à classe (variáveis globais) ou aos métodos
(variáveis locais). Também podem ser utilizadas como argumentos de métodos e como
tipos de retornos.
11
6.1 Declaração de Variáveis
12
Listagem 10 - Exemplo de Atribuição de Valor de Tamanho Maior em Variável de
Tamanho Menor
int x = 24;
byte b = x;
6.1.1 Exemplos
13
Antes da declaração da variável numPortas aparece a palavra chave private,
fazendo com que a variável tenha acesso privado, ou seja, tornando-a acessível apenas
dentro da definição da própria classe. Já antes da declaração da variável numJanelas
aparece a palavra chave public, que é um modificador de acesso público, permitindo
que a variável seja acessada de qualquer classe.
O código fonte abaixo também mostra como se comentar o código Java. O
comentário a ser escrito em uma única linha deve ser precedido por duas barras //. Já
um comentário que se estenda por várias linhas, deve ser escrito entre /**/.
A classe Casa do código da Listagem 12 é formada por 4 variáveis (duas inteiras,
uma boleana e uma de ponto flutuante), por dois métodos (um que define o número de
portas da casa e o outro que retorna o número de janelas) e um método principal.
A primeira linha do método principal exibe na tela a String “ Criação de uma
casa” seguida de uma quebra de linha. O espaço em branco antes da palavra “Criação”
corresponde a um TAB. Normalmente, os caracteres em uma String são exibidos
exatamente como são colocados entre as aspas duplas. Entretanto, na mensagem a ser
exibida pelo método principal os caracteres \t e \n não são enviados para a saída.
Quando uma barra invertida encontra-se juntamente com uma string de caracteres, o
caractere seguinte a \ é combinada à mesma para formar uma seqüência de escape. A
seqüência de escape \t é o caractere do TAB, já a seqüência \n é o caractere de nova
linha.
O segundo comando do método principal refere-se à declaração de um objeto. É
importante distinguir que uma variável primitiva possui um tamanho em bits que a
representa, porém uma variável que referencia um objeto não, ela possui um tamanho
em bits que representa um caminho para se obter o objeto, ou seja, um endereço de
memória.
O último comando do método principal, int numJan = c.getNumJanelas(),
atribui a variável inteira numJan o número de janelas do objeto Casa obtido a partir da
chamada ao método getNumJanelas() (que retorna um valor também inteiro). A
chamada a um método de um objeto é feita utilizando o operador ponto (.), ou seja,
c.getNumJanelas() . Nesse caso, significa que o objeto referenciado pela variável c
invoca o método getNumJanelas().
14
boolean grande = true; double valor;
A declaração consiste no trecho Casa casa, o que faz a JVM alocar espaço em
memória para a referência ao objeto do tipo Casa.
A criação do objeto é feita a partir do trecho new Casa(); . Neste ponto, a JVM aloca
espaço em memória para o novo objeto Casa que acaba de ser criado.
O operador igual (=) faz a ligação entre a referência em memória ao objeto Casa.
As variáveis de uma classe, também conhecidas como variáveis globais, se, após
a sua declaração, não forem inicializadas pelo programador (como por exemplo, a
variável numPortas da Listagem 12 que recebe o valor 3), recebem valores padrões.
Os valores padrões estão apresentados na Tabela 2.
15
Tabela 2 - Valores Padrões de Variáveis Globais
Tipo Valor Inicial
byte 0
short 0
int 0
long 0L
float 0.0f
double 0.0d
char ‘\u0000’
boolean false
reference null
Esta seção destina-se apenas a mostrar outras seqüências de escape que não
foram apresentadas na seção anterior. Veja a Tabela 3.
8. Separadores
16
Tabela 4 - Separadores
Separador Descrição
() Separador utilizado para: delimitar parâmetros em chamadas de métodos;
modificar precedência em expressões; identificar tipo-alvo em comandos;
delimitar condicionais em comandos.
{} Separador para delimitar blocos de código e inicialização de vetores.
[] Separador para declarar e manusear vetores.
; Finalizar de comando.
. Separador utilizado para selecionar campos e métodos de um objeto.
, Utilizado na declaração de variáveis e na estrutura de controle for.
9. Operadores
17
A Listagem 13 apresenta exemplos de usos dos operadores unários.
d = -d; /*Variável d assume seu próprio valor com sinal negativo, ou seja
-43.5*/
18
double numD = 10.0;
int numero = (int)numD; //Casting do tipo double para o tipo int
19
Tabela 7 - Precedência dos Operadores Aritméticos
Operador Precedência
() Os operadores em expressões contidas dentro de pares de parênteses
são avaliados primeiro. Se os parênteses estiverem aninhados, a
expressão no par mais interno será avaliada primeiramente. Se existe
pares não aninhados em um mesmo nível, eles são avaliados da
esquerda para direita.
*, /, % São avaliados em segundo lugar. Havendo vários, são avaliados da
esquerda para direita.
+ ou - Avaliados por último. Havendo vários, são avaliados da esquerda para
direita.
20
9.4 Operadores de Comparação
21
9.5 Operadores Bit-a-Bit
22
if (a != null && b.equals(“Testando”) ){ //Teste com operador &&
System.out.println(“Olá Pessoal”);
}
Em todos os testes da Listagem 16, o resultado será o mesmo, ou seja, nada será
escrito na tela. Considere o primeiro operando como sendo a != null e o segundo
operando como sendo b.equals(“Testando”). As diferenças são as seguintes:
• No teste que utiliza o operador &, ambos operandos serão avaliados, ou seja, vai
ser testado se a é diferente de null e se a String b é igual à String Testando, o
que resultará em um resultado falso.
• No teste que utiliza o operador &&, apenas o primeiro operando será avaliado.
Testa-se a é diferente de null. Como essa condição é falsa, não há mais
necessidade de se avaliar os demais operandos, já que na operação lógica AND,
se qualquer uma das condições for falsa, a expressão toda será falsa.
Nos testes da Listagem 17, o resultado também será o mesmo, ou seja, será
escrito na tela a String Olá Pessoal. Considere o primeiro operando como sendo a ==
null e o segundo operando como sendo b.equals(“Testando”). As diferenças são as
seguintes:
• No teste que utiliza o operador |, ambos operandos serão avaliados, ou seja, vai
ser testado se a é igual a null e se a String b é igual à String Testando, o que
resultará em um resultado verdadeiro.
• No teste que utiliza o operador ||, apenas o primeiro operando será avaliado.
Testa-se a é igual à null. Como essa condição é verdadeira, não há mais
necessidade de se avaliar os demais operandos, já que na operação lógica OR, se
qualquer uma das condições for verdadeira, a expressão toda será verdadeira.
23
9.7 Operador de Atribuição
Uma estrutura de controle é aquela que executa blocos de código delimitados por
chaves “{...}”. Todas as variáveis criadas dentro de um bloco de estrutura de controle
são vistas apenas dentro desse bloco.
24
estrutura if é executado. Caso seja falso (false), esse bloco de código não será
executado e o fluxo de controle segue adiante. A sintaxe básica dessa estrutura está
apresentada na Listagem 19.
25
Listagem 21 - Sintaxe da Estrutura IF/ELSE
if (condicao){
//Conjunto de instruções para condição VERDADEIRA
instrucao_1;
instrucao_2;
}else{
//Conjunto de instruções para condição FALSA
instrucao_3;
instrucao_4;
instrucao_5;
}
26
}else{
System.out.println(“E”);
}
Uma estrutura de repetição especifica que uma ação será repetida enquanto
alguma condição permanecer verdadeira. No caso da estrutura de repetição while, a
sintaxe básica é mostrada na Listagem 24. As instruções dentro das {} da estrutura
while ficam sendo executadas até que a condição se torne falsa. A Listagem 25
apresenta um exemplo de uso. No exemplo, a variável contador terá seu valor
incrementado de um até que assuma o valor 29.
27
Listagem 26 - Sintaxe da Estrutura DO/WHILE
do{
/* Conjunto de instruções para condição VERDADEIRA. Executado pelo
menos uma vez.*/
instrucao_1;
instrucao_2;
}while (condicao)
28
10.6 Estrutura de seleção múltipla switch
29
10.7 Interrompendo os Fluxos de Controle
11. Vetores/Arrays
30
alocados 10 elementos para o vetor de inteiros vetorInt. Quando o tamanho do vetor é
definido, automaticamente, as variáveis numéricas do tipo de dados primitivo são
inicializadas com zero; as variáveis booleanas são inicializadas com false; e as
referências (variáveis de tipos não-primitivo) são inicializadas com null.
31
A Listagem 36 apresenta um exemplo de uso com vetor. No método principal,
inicialmente, um vetor de nome n é declarado e inicializado com 10 posições. Em
seguida, utilizando a estrutura de um loop for, percorre-se todos os elementos do vetor
e atribui a eles o valor da variável de controle do for (no caso, o valor da variável i)
multiplicada por 2. Observa-se que na condição da estrutura do for, o valor de i deve
ser menor que n.length. A expressão n.length determina o comprimento do vetor.
Neste caso, como o vetor tem tamanho igual a 10, o laço do for será executado até que
o valor da variável dontrole i seja menor que 10.
A Listagem 37 apresenta um exemplo com vetor, onde esse vetor é formado por
elementos que são objetos do tipo Casa.
32
c[1] = new Casa();
c[2] =c1;
33
vb[0][2] = 15; //Elemento da 1ª linha e da 3ª coluna
vb[1][0] = 13; //Elemento da 2ª linha e da 1ª coluna
vb[1][1] = 12; //Elemento da 2ª linha e da 2ª coluna
vb[1][2] = 5; //Elemento da 2ª linha e da 3ª coluna
Listagem 39 – Exemplo que percorre vetor com for controlado por contador
public class Vetor{
public static void main( String args[] ){
34
System.out.println(“Soma = ” + soma);
}
}
35
12. Programação Orientada a Objetos
12.1 Abstração
Figura 3 - Abstração
36
12.2 Objetos
12.3 Classes
37
Listagem 43 – Exemplo de uma Classe
class Retangulo{
//Atributos da Classe
float orig_x, orig_y;
float altura, largura;
//Método da classe
public void translacao (float x, float y) {
orig_x = x;
orig_y = y;
}
}
38
12.4 Passagem de Argumentos para Métodos
39
Listagem 45 – Exemplo de Passagem por Valor
class Retangulo{
//Atributos da Classe
float orig_x, orig_y;
float altura, largura;
//Método da classe
public void translacao (float x, float y) {
//Realiza translação
orig_x = x;
orig_y = y;
40
método sem retorno chamado modificaElemento que recebe como argumento um
elemento do tipo inteiro (passado por valor) e um método chamado getMensagem que
retorna um objeto do tipo String.
No método inicializa, um vetor v é declarado com 5 elementos (1, 2, 3, 4, 5).
Em seguida, a variável mensagem recebe o valor:
41
//Mensagem a ser escrita na tela
mensagem = “Os valores originais do vetor são: ”;
modificaElemento( v[2] );
mensagem += “\n O valor de v[2] é =” + v[2];
}
//Modifica todos os elementos do vetor. Multiplica todos por 3
public void modificaVetor( int v1[] ){
for (int j = 0; j < v1.length; j++ ){
v1[j] *= 3;
}
}
//Modifica um elemento, multiplicando-o por 3
public void modificaElemento( int elem ){
elem *= 3;
}
//Retorna a mensagem
public String getMensagem(){
return mensagem;
}
42
}
}
43
System.out.print(“Média entre ” + d1 + “, ” + d2 + “ e ” + d3 “=”
+ media2);
Um método pode declarar apenas um tipo de retorno. Se for desejado que ele
retorne mais de um valor, é possível declarar o tipo de retorno como sendo um vetor.
Por exemplo, no método da Listagem 50 é necessário retornar dos valores do tipo int.
Para isso, um vetor de inteiros com duas posições é declarado e possuem os valores que
devem ser retornados.
44
Listagem 50 – Exemplo de Método com Mais de um Valor como Retorno
int[] getValores(){
int retornos[] = new int[2];
retornos[0] = 9;
retornos[1] = 23;
return retornos;
}
12.7 Construtores
45
public ExemploConstrutor(){
var_1 = 10;
var_3 = “Exemplo Construtor sem Argumentos”;
}
public ExemploConstrutor(int valor){
var_1 = valor;
var_2 = 15.0;
}
public ExemploConstrutor(int valor1, String valor2){
var_1 = valor1;
var_3 = valor2;
}
}
46
13. Igualdade entre Objetos
A comparação entre dois tipos primitivos em Java se faz a partir do operador ==,
já em relação a objetos a utilização desse operador vai fazer com que os valores de suas
referências à memória sejam comparados e não o seu conteúdo semântico. Assim,
utilizar o operador == para comparar objetos pode gerar desigualdades para objetos
idênticos. No caso das Strings, para resolver este problema basta utilizar o método
equals(), como mostrado anteriormente.
Para compreender o porquê de dois objetos iguais serem diferentes quando
comparados com o operador == é necessária a introdução de dois conceitos: Pilha ou
Memória Stack (Pilha) e Heap de Objetos.
Assim como em outras linguagens de programação, em Java, as estruturas de
dados usadas durante a execução do programa na memória são mapeadas em duas
partes: a memória stack e a memória heap. A memória stack (também conhecida como
pilha) possui uma estrutura organizada de maneira seqüencial, em que os dados são
colocados uns sobre os outros. Os dados primitivos e as referências a objetos são
armazenados na pilha.
A memória heap consiste em uma estrutura de armazenamento não ordenada,
mas ampla, que pode ser acessada diretamente. Ela contém os objetos na linguagem
Java. O acesso direto a esses objetos somente é feito porque se sabe onde o dado
desejado está localizado a partir de sua referência armazenada na memória stack.
Para melhor entender esses conceitos, observe os exemplos presentes na
Listagem 55 e Listagem 56. A Listagem 55 apresenta uma classe Teste com uma única
variável e dois métodos. Já a Listagem 56 possui a TesteStackHeap com um método
principal onde algumas variáveis são instanciadas. No método principal, uma variável
double é criada e recebe o valor 12.5, uma variável do tipo int é instanciada e recebe o
valor 23, dois objetos do tipo Teste são instanciados e atribuídos as variáveis teste1 e
teste2, que guardarão suas referências e uma outra variável do tipo Teste é declarada
e recebe o conteúdo da variável teste2.
47
return var1;
}
}
Após a apresentação desses conceitos, pode-se concluir que um objeto pode ser
referenciado por várias variáveis, ou seja, podem existir várias posições na memória
stack com o mesmo conteúdo (mesma referência). Dessa forma, comparar objetos a
partir do operador == implicará em comparar os valores de suas referências na
memória stack, mas na verdade o que se deseja é comparar se dois objetos são iguais
no que diz respeito a possuírem os mesmos valores para os seus dados internos.
48
Em Java, a comparação do conteúdo interno dos objetos é feita através do
método equals() que retorna verdadeiro se eles forem iguais e falso, caso contrário.
Para algumas classes (String, ArrayList), a API Java já disponibiliza esse método
implementado. Para as classes criadas pelo programador, esse método deve ser
implementado e seu código conterá a comparação entre os dados relevantes dos objetos
a serem comparados.
Todas as classes escritas em Java são filhas da classe Object, ou seja, todas as
classes herdam os seus métodos. Na classe Object é definido que a comparação entre
dois objetos é feita a partir do método equals(), também herdado. Esse método possui
a seguinte assinatura:
49
public void setNome(String nome){
this.nome = nome;
}
}
aluno1.setMatricula(12345);
aluno1.setNome(“Carlos”);
aluno2.setMatricula(54321);
aluno2.setNome(“Janaina”);
50
//Comparando aluno1 com aluno2
if (aluno1.equals(aluno2)){
System.out.println(“Aluno 1 = Aluno 2”);
}else{
System.out.println(“Aluno 1 != Aluno 2”);
}
aluno3.setMatricula(12345);
aluno3.setNome(“Carlos”);
aluno4.setMatricula(12345);
aluno4.setNome(“Carlos”);
51
return nome;
}
public void setNome(String nome){
this.nome = nome;
}
public boolean equals(Object obj){
Aluno alun = (Aluno)obj;
if (matricula == alun.getMatricula()){
if (nome.equals(alun.getNome)){
return true;
}
}
return false;
}
}
14. Encapsulamento
Figura 5 - Encapsulamento
52
Na Listagem 60, há uma classe ContaCorrente que possui um atributo (saldo) e
alguns métodos que não foram exibidos. No método principal, uma instância dessa
classe é criada e através do acesso direto ao conteúdo da variável saldo é possível
saber se a conta tem ou não saldo. Na verdade, para algumas aplicações que acessam a
classe ContaCorrente não interessa saber o valor do saldo da conta, mas sim, apenas,
se a conta tem ou não saldo. Dessa forma, outras classes podem ter acesso ao
conteúdo da variável, o que não deveria ocorrer.
// outros métodos...
53
ContaCorrente c = new ContaCorrente();
if (c.isSaldoNegativo()) {
//Sem saldo
}
}
}
15. Herança
54
Figura 6 – Herança
55
15.1 Como Definir o Relacionamento de Herança
56
do animal ao comer), sleep() (comportamento do animal dormindo) e roam()
(comportamento do animal quando não está nem dormindo nem comendo,
provavelmente vagueando no ambiente).
57
Figura 10 - Definição de Relacionamento de Herança: 3º Passo
58
Figura 11 - Definição de Relacionamento de Herança: 4º Passo
59
Figura 12 - Definição de Relacionamento de Herança: 5º Passo
60
Figura 13 - Hierarquia do Animal tipo Wolf
61
long cpf;
int estadoCivil;
62
public void setCnpj( long cnpj ){
this.cnpj = cnpj;
}
public void setRazaoSocial (String razaoSocial){
this.razaoSocial = razaoSocial;
}
public void setNomeRepresentante (String nomeRepresentante){
this.nomeRepresentante = nomeRepresentante;
}
}
Caso uma aplicação necessite utilizar ambas as classes acima, estaria tendo
replicação de dados e métodos nelas. Uma solução seria a utilização da herança. Um
alternativa seria criar uma classe Pessoa (Listagem 64) com as informações comuns as
duas classes e as classes PessoaFisica e PessoaJuridica herdaria seus atributos e
métodos e implementariam apenas as suas particularidades, como visto na Listagem 65
e na Listagem 66. Como já citado, a herança é realizada a partir da palavra chave
extends.
63
Listagem 65 – Herança: Classe PessoaFisica
class PessoaFisica extends Pessoa{
long cpf;
int estadoCivil;
Percebe-se que nos exemplos dessa seção uma nova palavra chave é introduzida,
this. Ela é utilizada para que um objeto tenha uma referência a ele próprio, a referência
this. Essa referência é utilizada para referenciar variáveis de instância e métodos de um
objeto. Nos exemplos apresentados, os métodos recebiam como argumentos variáveis
com os mesmos nomes de suas variáveis globais. A referência this foi utilizada para
dizer que quem estava recebendo o novo valor era a variável do objeto instanciado e
não a própria variável passada como argumento.
64
A referência this pode ser utilizada para: parâmetros com o mesmo nome de
campos; passar o objeto corrente como parâmetro; retornar o objeto corrente.
Existe um modificador de acesso que não permite que uma classe seja herdada, é
o modificador final. Por exemplo, se a classe for definida como na Listagem 67. O
modificador final também pode ser aplicado a variáveis e a métodos. No caso de uma
variável, ele indica que o seu valor não pode ser modificado. No caso de um método,
implica que ele não pode ser redefinido em subclasses.
16. Polimorfismo
No primeiro caso, tanto o tipo da variável como o tipo do objeto são do tipo Dog,
como mostrado na Figura 14. Usando o polimorfismo, a referência (tipo da variável) e o
objeto podem ser de tipos diferentes, porém o tipo da referência deve ser do tipo da
superclasse do objeto atual, como ilustrado na Figura 15.
65
Figura 15 - Com Polimorfismo
66
Também é permitido usar polimorfismo em tipos de argumentos dos métodos e
tipos de retornos. Por exemplo, a classe da Listagem 70 possui um método que recebe
como argumento um objeto do tipo da superclasse Animal. Na classe da Listagem 71,
um método cria e inicializa dois objetos diferentes das subclasses Dog e Hippo que são
passados como argumentos ao método metodoPolimorfico da classe
ArgumentoPolimorfismo. Dentro deste método, o método makeNoise será invocado
de acordo com a instância da classe Animal passada.
ap.metodoPolimorfico(d);
ap.metodoPolimorfico(h);
}
}
67
do método definido na superclasse; nesse caso, está sendo utilizado o mecanismo de
redefinição de métodos (overriding).
Quando uma classe é herdada por outra, todos os seus métodos não privados são
herdados. Em algumas situações, deseja-se alterar o comportamento de algum desses
métodos herdados. Isso é feito redefinindo o método da superclasse, a partir de sua
reescrita, definindo-o com o mesmo nome e mesma lista de parâmetros. Dessa maneira,
a chamada a um método sobrescrito a partir de um objeto instância da subclasse
executará o comportamento redefinido pela subclasse e não mais o definido pela
superclasse. Se o objeto for instância da superclasse, o método executará o
comportamento definido na própria superclasse.
Para exemplificar a redefinição de métodos, observa-se o exemplo da Figura 16.
Neste exemplo, a classe Ponto é herdada pelas classes Circulo e Retangulo. Dessa
maneira, os métodos desenhar() e getCor() estão disponíveis para as duas
subclasses. O primeiro método desenha o objeto na tela, já o segundo retorna sua cor.
Considerando um ponto, um círculo e um retângulo, verifica-se que eles devem ser
desenhados de maneiras distintas, pois apresentam conceitualmente formas diferentes.
Assim, o método desenhar() das subclasses devem ser redefinidos.
68
Retângulo são chamados, respectivamente, e o método getCor() da classe Ponto é
chamado três vezes.
Quando uma classe é criada, o primeiro pensamento pode ser que ela seja criada
para funcionar como um tipo e que objetos desse tipo serão instanciados, ou seja, crie-
se uma classe concreta. Por outro lado, podem existir casos em que o programador
defina uma classe e nunca pretenda instanciá-la. Essas classes são conhecidas como
classes abstratas e são usadas como modelo ou base para o comportamento de outras.
O diagrama de classes da Figura 17 apresenta um exemplo de herança. No caso,
a classe Animal é herdada pelas classes Cachorro, Canguru e Coelho. Observa-se que
há coerência na existência de objetos dos tipos Cachorro, Canguru e Coelho, porém
não faz sentido existir um objeto do tipo Animal. Como já citado, classes que não
devem ser instanciadas são denominadas de classes abstratas. Dessa maneira, a
superclasse Animal deve ser implementada como uma classe abstrata (ou seja, nunca
será instanciada), possui três atributos e dois métodos um concreto e um abstrato. Um
método concreto é aquele que possui sua implementação em seu corpo. Já um método
abstrato é apenas declarada e sua implementação deve ser feita pelas suas subclasses,
ou seja, ele não possui “corpo”.
Para implementar uma classe ou método abstrato em Java, deve-se utilizar a
palavra chave abstract, como pode ser visto na implementação da classe Animal
presente na Listagem 73.
69
A classe Animal é uma classe abstrata e serve de modelo para suas subclasses.
Ela também possui um método abstrato, andar. Observa-se que este método não possui
implementação, é apenas declarado para ser possivelmente implementado pelas
subclasses. É importante saber que nem toda classe abstrata precisa possuir métodos
abstratos, porém todo método abstrato herdado de uma superclasse deve ser
implementado a menos que a subclasse também seja uma classe abstrata. De acordo
com o diagrama de classe da Figura 17, as classes Cachorro e Canguru implementam
o método abstrato andar. Já a classe Coelho não o implementa, portanto sendo
também de uma classe abstrata.
//Método concreto
public void comer (){
//Este método deve ser codificado
}
//Declaração de método abstrato
public abstract andar();
}
70
O exemplo a seguir ilustrará os conceitos de classes abstratas e polimorfismo. A
Figura 8 apresenta a classe abstrata Animal sendo herdada pelas subclasses concretas
Dog e Cat. O problema consiste em implementar uma lista para armazenar objetos do
tipo Dog e outra para armazenar objetos do tipo Cat.
71
Listagem 75 – Lista de Cat
public class MyCatList{
private Cat[] cats = new Cat[5];
private int nextIndex = 0;
72
Na Listagem 83 são criados dos objetos, um do tipo Dog e outro do tipo Cat e
ambos podem ser inseridos na lista genérica de animais do tipo MyAnimalList.
18. Interfaces
73
Figura 19 - Hierarquia de Animais
74
• Prós: implicará em todas as classes herdarem o comportamento; possibilidade de
se utilizar as vantagens do polimorfismo; cada subclasse poderia implementar
seu próprio comportamento de animal de estimação.
• Contras: como seriam métodos abstratos, toda classe concreta obrigatoriamente
deve implementá-lo e como citado anteriormente, existem algumas subclasses
(Hippo, Lion, Tiger e Wolf) que não precisam desse comportamento, afinal dos
animais presentes na hierarquia apenas o cachorro e o gato têm o perfil de
animais de estimação.
75
Figura 20 - Nova Classe na Hierarquia: Pet
Uma interface possui sintaxe similar a uma classe, mas possui apenas as
especificações de suas funcionalidades e não como essas funcionalidades são
implementadas. Em Java, uma interface é uma classe abstrata em que todos os seus
métodos são abstratos e tem acesso público, já os seus atributos possuem níveis de
acessibilidade public, final e static (Os modificadores serão detalhados em uma seção
futura deste material. Brevemente, o modificador static aplicado a uma variável faz com
que ela seja associada à classe e não a instâncias da classe, ou seja, é uma variável
vista por todas as instâncias da classe onde foi definida).
Uma classe abstrata é herdada por subclasses a partir da palavra chave extends,
já uma interface é implementada por outras classes através da palavra chave
implements.
Utilizando-se do conceito de interfaces, o problema descrito anteriormente
poderia ser solucionado como mostrado pela Figura 21. Na Figura, existe uma interface
Pet que pode ser implementada por qualquer classe que necessite dos comportamentos
para animais de estimação. Por exemplo, as classes RoboDog, Cat e Dog implementam
76
esses comportamentos e também são subclasses das classes Robot, Feline e Canine,
respectivamente.
77
public void roam(){ ... }
public void eat(){ ... }
}
78
Resumindo, quando uma classe implementa uma interface, garante-se que todas
as funcionalidades especificadas pela interface serão oferecidas pela classe. Ou seja,
uma interface representa um comportamento que pode ser compartilhado por várias
classes que devem implementar seus métodos. Uma classe pode implementar várias
interfaces, podendo ser uma alternativa para herança múltipla.
19. Modificadores
19.1 Private
79
19.2 Public
19.3 Protected
O modificador protected pode ser aplicado a variáveis e métodos. Faz com que
eles apenas possam ser acessados por classes pertencentes ao mesmo pacote ou por
subclasses da classe onde o elemento foi definido.
19.4 Package
19.5 Static
80
carregamento da classe ModificadorStatic, essa variável é criada e inicializada com o
valor zero e como tem acesso estático é uma variável da classe. Dessa forma, a última
linha do método principal imprime na tela o valor 3. Observa-se que para imprimir esse
valor, invoca-se o método getVariavel() da classe ModificadorStatic, que por ser um
método estático, pode ser invocado através do nome da classe, ou seja,
ModificadorStatic.getVariavel().
obj1.incrementaVariavel();
obj2.incrementaVariavel();
obj3.incrementaVariavel();
System.out.println( ModificadorStatic.getVariavel() );
}
}
81
19.6 Final
19.7 Abstract
19.8 Transient
19.9 Synchronized
82
O segundo detalhe é que o método sobrescrito deve não pode ter um nível de
acessibilidade menor do que o método original. Afinal, as subclasses são extensões da
superclasse e se tiver nível de acesso a métodos reduzido, seria uma redução e não uma
redução. Resumindo, as regras de utilização de modificadores de acessibilidade na
sobrescrita de métodos são:
• Um método private não pode ser sobrescrito;
• Um método default pode ser sobrescrito para default, protected e public;
• Um método protected pode ser sobrescrito para protected e public;
• Um método public pode ser sobrescrito somente para e public.
Em Java, números podem ser representados por tipos primitivos ou por objetos.
Para representar uma variável de um tipo primitivo como um objeto, basta utilizar uma
instância de alguma das classes de Java chamadas de wrappers de tipos. Um objeto de
uma classe wrapper contém um único valor de um tipo primitivo da linguagem,
permitindo assim estabelecer uma ponte entre valores literais e objetos. A Tabela 14
apresenta os tipos primitivos com suas respectivas classes wrappers.
83
do tipo primitivo ou Strings.
84
Long l1 = new Long(1234567);
long l = l1.longValue();
Antes da versão do Java 5.0, por exemplo, se fosse desejado inserir um valor
primitivo em uma estrutura de dados que armazenasse somente Objects, deveria-se
criar um novo objeto da classe wrapper de tipo correspondente e depois inserir o objeto
na coleção. Da mesma forma, se um objeto da classe wrapper necessitasse ser
recuperado da coleção para que tivesse seu valor primitivo manipulado, o método que o
converte em tipo primitivo deveria ser invocado. Para exemplificar, considera-se o
exemplo presente na Listagem 87. Neste exemplo, um vetor do tipo Integer é criado
com três posições. Em seguida, atribui-se aos elementos do vetor objetos do tipo
Integer com valor 10. Por fim, recupera-se os valores primitivos do vetor utilizando o
método de conversão intValue().
Com o exemplo anterior, percebe-se que valores primitivos são utilizados para
iniciar objetos Integer. Dessa forma, consegue-se o pretendido, porém código extra é
digitado. Além disso, é necessário utilizar o método intValue() para manipular os
valores do vetor como valores de tipos primitivos.
A partir da versão do Java 5.0, essa conversão entre valores de tipos primitivos e
objetos wrappers é simplificada. Não há mais necessidade de nenhum código de
conversão entre eles. Surgiram, então, dois tipos de conversões: autoboxing e auto-
unboxing. Uma conversão boxing converte um valor de um tipo primitivo em um objeto
da classe wrapper do tipo correspondente. Já a conversão auto-unboxing converte um
objeto de uma classe wrapper em um valor do tipo primitivo correspondente. Essas duas
conversões são realizadas automaticamente no Java 5.0. Por exemplo, o código presente
85
na Listagem 88.
vetorInt[0] = 1;
vetorInt[1] = 2;
vetorInt[2] = 3;
86
• Pode ser que haja necessidade de se concatenar Strings (ESTACAO_) para
definir os nomes das constantes de forma que não haja colisão com nomes de
constantes de outras classes.
• Se o valor de uma das constantes for mudado, todas as classes que utilizam
alguma das constantes devem ser recompiladas juntamente com a classe que
define as constantes. Isto se deve ao fato das constantes serem estáticas e terem
seus valores definidos no momento da criação da classe.
• A impressão do valor de uma constante pode não dizer nada se ela for definida
como um inteiro, pois apenas um valor do tipo int será impresso.
Esse problemas podem ser resolvidos através da utilização do padrão TypeSafe Enum
definido por Joshua Bloch, ex-funcionária do SUN. Não caberá a esse material explicar
esse padrão, mas adianta-se que ele trás outros problemas (Mais sobre esse padrão em:
http://java.sun.com/docs/books/effective/).
Com o Java 5.0, a linguagem Java passou a dar suporte a um tipo de enumerações,
assim como em C, C++. Surge, então, um tipo enum que define um conjunto de
constantes representadas como identificadores únicos. Como nas classes, os tipos enum
são tipos por referência, significando que um objeto do tipo enum pode ser
referenciado.
Um tipo enum é declarado com uma declaração enum, uma lista separada por
vírgula de constantes, A declaração também pode incluir outros componentes das
classes tradicionais como construtores, atributos e métodos. A Listagem 90 apresenta a
declaração de uma enumeração para as estações do ano.
• Tipos enum são implicitamente final, pois declaram constantes que não podem
ser modificadas;
• Constantes enum são implicitamente static;
• Tentar criar um objeto do tipo enum usando o operador new implicará em erro
87
de compilação.
//Variáveis
private final String nome;
private final String codigo;
//Construtor
Cor( String nome, String codigo ){
this.nome = nome;
this.codigo = codigo;
}
//Retorna nome da cor
public String getNome(){
return nome;
}
//Retorna código da cor
public String getCodigo(){
return codigo;
}
}
88
A Listagem 92 apresenta um exemplo de uso da classe enum Cor. Para todo
enum, o compilador gera um método estático chamado values que retorna um vetor
das constantes na ordem em que foram declaradas. Neste exemplo, todos as constantes
são percorridas através do uso do método values e têm o identificador da constante, o
nome da cor e o código da cor escritos na tela através da chamada a cor,
cor.getNome(), cor.getCodigo(), respectivamente.
O segundo for exibe os dados das constantes de determinado intervalo através
do método estático range da classe EnumSet (java.util.EnumSet). Este método recebe
dois parâmetros: a primeira constante enum no intervalo e a última constante enum no
intervalo. A ordenação das constantes obedece à ordem em que foram declaradas na
enum. A Listagem 93 apresenta o resultado da execução do código presente na
Listagem 92.
89
RED => Vermelho - #FF0000
BLUE => Azul - #0000FF
GREEN => Verde - #00FF00
90
23. Referências Bibliográficas
91