livro java 2 final

Propaganda
Aprendendo
Java 2
Rodrigo Mello
Ramon Chiara
Renato Villela
Novatec Editora Ltda.
www.novateceditora.com.br
1
Programação Orientada a Objetos
O que é Programação Orientada a Objetos?
É um paradigma de programação baseado no conceito de classes e objetos.
As classes são elementos em que dados e procedimentos podem ser agrupados,
segundo sua função para um determinado sistema; essas classes são codificações
no formato de arquivos. Quando uma dessas classes é utilizada como um tipo de
dado para a criação de uma variável, esta é chamada de objeto.
O que é Classe?
A classe é definida como uma estrutura de dados que contém métodos e
atributos. No paradigma da orientação a objetos, os procedimentos ou funções
(presentes em linguagens estruturadas, tais como C e Pascal) são chamados de
métodos de uma classe. As variáveis, que são declaradas dentro de uma classe,
são chamadas de atributos.
Ao se criar uma classe, o objetivo é agrupar métodos e atributos que estejam
relacionados entre si. Uma classe é composta de partes e estas devem representar
alguma funcionalidade segundo o objetivo da classe. Por exemplo, uma classe
Cliente pode ter atributos como nome_cliente, endereco_cliente e outros dados relacionados
ao cliente. Exemplos dos métodos que atuam sobre tais dados podem ser
inserir_cliente, excluir_cliente e alterar_cliente.
13
Capítulo 1: Programação Orientada a Objetos
As partes que compõem a classe Cliente devem representar funcionalidades
segundo o objetivo dessa classe, ou seja:
1. Quais os dados relacionados a um cliente (nome, endereço, cidade,
estado, país etc.).
2. Quais outras informações desse cliente devem ser armazenadas?
3. Quais atitudes um cliente pode tomar ou quais atitudes podem ser
tomadas diante de um cliente (estas, geralmente, se tornam métodos da
classe)?
Enfim, a classe é o código que declara atributos e métodos, em que cada um
destes possa fazer parte da representação de um mesmo objetivo. O objetivo da
classe é representar de forma adequada uma entidade (tal como um cliente,
fornecedor etc.) dentro de um sistema.
Exemplo de uma classe em Java
// declaração inicial da classe Cliente
public class cliente
{
// atributos da classe Cliente
String nome;
String endereco;
// métodos da classe Cliente
public void setNome (String novo_nome)
{
nome = novo_nome;
}
public void setEndereco (String novo_endereco)
{
endereco = novo_endereco;
}
public String getNome()
{
return nome;
}
public String getEndereco()
{
return endereco;
}
} // fim da declaração da classe Cliente
14
Capítulo 1: Programação Orientada a Objetos
A classe Cliente apresenta atributos, que são utilizados para armazenar
informações relacionadas ao cliente, e métodos, que representam ações tomadas
sobre os dados contidos em um objeto dessa classe ou objetos de outras classes.
Os métodos definidos na classe Cliente recuperam e alteram somente os dados
contidos em um objeto dessa própria classe.
O que é Objeto?
Enquanto uma classe é somente a codificação na forma de um arquivo texto,
um objeto é uma instância de uma classe, ou seja, é uma porção de memória
reservada para armazenar os dados e os métodos declarados na classe. Enfim, um
objeto é a instanciação de uma classe na memória.
A classe é o código-fonte escrito em um arquivo texto, enquanto o objeto é
uma parte de uma aplicação durante o processo de execução. No objeto, pode-se
executar os métodos que foram definidos, além de acessar e alterar dados.
class X {
...
}
Disco
Rígido
Memória
Figur
a1
.1 – Objeto do tipo Cliente e sua classe correspondente.
igura
1.1
O que é Mensagem?
A mensagem é definida como o ato de chamar ou requisitar a execução de
um método. Portanto um objeto do tipo Cliente apresenta diversos métodos; o ato
de requisitar a execução de um desses métodos é conhecido como mensagem.
Um objeto pode enviar mensagens para outros objetos requisitando a execução
de métodos.
15
Capítulo 1: Programação Orientada a Objetos
O que é Encapsulamento?
É a capacidade de restringir o acesso a elementos de uma classe utilizando
qualificadores. Um qualificador é uma palavra reservada, que define a visibilidade
de determinado atributo ou método, em uma linguagem orientada a objetos. Por
exemplo, na linguagem C++ existem os qualificadores private, public e protected, e na
linguagem Java existe, ainda, mais um qualificador, conhecido como package.
A função dos qualificadores é definir o acesso aos dados e métodos de uma
classe. O qualificador public, por exemplo, permite que determinado método ou
atributo seja acessado diretamente por qualquer outro objeto. Observe a seguir a
tabela 1.1 com as características dos qualificadores na linguagem C++ e Java:
Qualificadores
Java
private
O método ou atributo pode ser
acessado somente dentro da
própria classe.
public
O método ou atributo pode ser
acessado externamente por
outro código.
protected
O método ou atributo pode ser
acessado pela própria classe ou
por classes-filhas (aquelas que
herdam desta classe).
package (sem
modificador)
O método ou atributo pode ser
acessado pela própria classe ou
por classes que participem do
mesmo pacote.
C++
O método ou atributo pode ser
acessado somente dentro da
própria classe.
O método ou atributo pode ser
acessado externamente por outro
código.
O método ou atributo pode ser
acessado pela própria classe ou
por classes-filhas (aquelas que
herdam desta classe).
Não existe em C++.
Tabela 1
.1 – Tabela com os qualificadores nas linguagens Java e C++.
1.1
Métodos
Atributos
Figur
a1
.2 – Encapsulamento em uma classe.
igura
1.2
16
Capítulo 1: Programação Orientada a Objetos
A seguir, um exemplo de uma classe em Java que utiliza qualificadores:
// declaração inicial da classe Clientes
public class Clientes
{
// atributos da classe Clientes - estes podem ser acessados somente dentro da classe
private String nome;
private String endereco;
// atributos da classe Clientes acessíveis por meio de código externo à classe
public String cidade;
public int idade;
// atributos da classe Clientes acessíveis por meio de código externo à classe,
// desde que pertença ao mesmo pacote dessa classe existe em Java,
// não existe esse tipo de declaração em C++
float saldo;
String rg;
// métodos da classe Clientes
public void setNome (String novo_nome)
{
nome = novo_nome;
}
public void setEndereco (String novo_endereco)
{
endereco = novo_endereco;
}
public String getNome()
{
return nome;
}
public String getEndereco()
{
return endereco;
}
} // fim da declaração da classe Clientes
17
Capítulo 1: Programação Orientada a Objetos
O que é Herança?
A herança torna possível a reutilização de funcionalidades previamente definidas
em uma classe. A finalidade é que a subclasse (aquela que herda as características de
determinada classe) inclua o comportamento da superclasse (aquela que contém o
comportamento a ser herdado) e adicione mais funcionalidades. Não seria interessante
a herança se não houvesse a possibilidade de adicionar funcionalidades à subclasse.
A figura 1.3 representa uma subclasse que herda características da superclasse
e lhe adiciona alguma funcionalidade.
Figura 1.3 – Mecanismo da Herança.
Ao processo de definição de subclasses, em que uma classe herda características
de outra e assim sucessivamente, dá-se o nome de hierarquia de classes. Essa
hierarquia de classes segue uma apresentação na forma de árvore, em que a raiz é a
superclasse de todo o sistema. Cada nível abaixo dessa superclasse do sistema
acrescenta funções específicas.
Figura 1.4 – Exemplo de Hierarquia de Classes.
18
Capítulo 1: Programação Orientada a Objetos
Na figura 1.4 é apresentada uma hierarquia de classes, em que a classe Funcionario
representa a superclasse do sistema. No nível abaixo dessa superclasse existem
especializações da superclasse, ou seja, subclasses que herdam as características
da classe Funcionario e nas quais podem ser adicionadas características específicas.
Na figura 1.4 existem as subclasses FuncionarioContratado e FuncionarioTemporario.
Pode-se relacionar subclasses à superclasse questionando se a subclasse "é
uma" superclasse. Por exemplo, pense na seguinte questão: "um FuncionarioContratado é
um Funcionario?". A resposta é sim. Caso a resposta seja negativa, a herança
provavelmente esteja incorreta ou durante a fase de projeto do sistema foi necessária
a criação de tal estrutura.
Figura 1.5 – Exemplo de hierarquia errada.
Existem construções de hierarquia de classes que apresentam erros. A figura
1.5 apresenta um exemplo de hierarquia em que a superclasse Carro tem as
subclasses Porta e Pneu. Essa técnica não expressa corretamente o conceito de
hierarquia, em que as subclasses especializam, ou seja, afunilam o conceito da
superclasse. Caso se utilize o mesma linha de raciocínio realizada anteriormente
para essa hierarquia, tal como: "Uma Porta é um Carro?", a resposta será negativa.
Portanto o correto é definir uma subclasse que se relacione com a superclasse, em
que a subclasse seja o conceito da superclasse adicionando-se algum conteúdo
que possa representar de forma mais específica o conceito apresentado pela
superclasse.
A herança pode ser múltipla em que uma subclasse herda características de
diversas superclasses. A linguagem C++ suporta esse tipo de herança, enquanto a
linguagem Java suporta somente a herança de uma única classe.
19
Capítulo 1: Programação Orientada a Objetos
Figura 1.6 – Mecanismo da Herança Múltipla.
A seguir, um exemplo de herança em Java:
/*———declaração da superclasse———*/
class madeira
{
// atributo da classe madeira
String cor;
// métodos da classe madeira
public String getCor()
{
return cor;
}
public void setCor(String nova_cor)
{
cor = nova_cor;
}
} // fim da declaração da superclasse
/*————declaração da subclasse————*/
class cadeira extends madeira
{
// atributos adicionais à superclasse
int peso;
// métodos adicionais à superclasse
public void escreve_tela()
{
System.out.println(this.getCor());
}
} // fim da declaração da subclasse
20
Capítulo 1: Programação Orientada a Objetos
O que é Sobrecarga de Métodos e
Operadores?
Sobrecarregar um método ou operador é permitir que se utilize o mesmo
nome de chamada para operar sobre uma variedade de tipos de dados.
Pode-se ter diversos métodos declarados com o mesmo nome dentro de uma
classe, entretanto tais métodos não podem ter os mesmos tipos de parâmetros
seguindo a ordem de declaração. Por exemplo, pode-se ter o método setValor(float
novo_x) e setValor(float novo_x, float novo_y) em que o tipo do primeiro parâmetro é o
mesmo, mas existe um segundo parâmetro em um dos métodos que diferencia as
duas declarações. Errado, portanto, seria declarar o método setValor(float novo_x) e
setValor(float novo_y), pois o tipo do parâmetro é idêntico e o número de parâmetros
também, não havendo diferenças entre as duas declarações.
Para sobrecarregar métodos, deve-se, inicialmente, detectar quais métodos
serão sobrecarregados. Nunca defina métodos que apresentem a mesma ordem
de declaração de parâmetros, tendo estes o mesmo tipo de dados e estando em
número idêntico.
Como saber se é válida a sobrecarga de métodos?
Método
Caso coexistam em uma classe
1. setValor(float novo_x)
O método tem uma variável do tipo float.
2. setValor(float novo_y)
Caso coexistam os métodos 1 e 2 na mesma classe,
o compilador apresentará erros, pois detectará dois
métodos que recebem apenas uma variável do mesmo
tipo.
3. setValor(float novo_x, float novo_y)
Caso coexistam os métodos 1 e 3 ou os métodos 2 e
3, o exemplo funcionará corretamente.
4. setValor(float novo_x, int nova_variavel)
Caso coexistam os métodos 1 e 4 ou 2 e 4, o exemplo
funcionará corretamente. Caso os métodos 3 e 4
estejam na mesma classe, o exemplo também
funcionará corretamente, pois apesar de o número de
parâmetros ser igual, o tipo deles na ordem de
declaração é diferente. Ou seja, pode-se ter o mesmo
número de parâmetros quando o tipo deles na ordem
de declaração do método é diferente (pois é assim
que o compilador analisa para fazer a chamada ao
método); no caso 3 tem-se (float, float) e no caso 4 temse (float, int).
Existe, ainda, como citado anteriormente, a sobrecarga de operadores. A
linguagem Java suporta somente sobrecarga de métodos. Existem, contudo,
linguagens, como C++, que suportam sobrecarga de métodos e operadores.
21
Capítulo 1: Programação Orientada a Objetos
A seguir, um exemplo de classe em Java que tem o método setValor
sobrecarregado:
// declaração da classe vetor
public class vetor
{
// atributos da classe vetor
private float X;
private float Y;
// métodos da classe vetor
// ocorre sobrecarga no método setValor, pois ele manipula
// uma ou duas variáveis do tipo float
public void setValor(float novo_x)
{
X = novo_x;
}
public void setValor(float novo_x, float novo_y)
{
X = novo_x;
Y = novo_y;
}
} // fim da declaração da classe vetor
A sobrecarga de operadores é oferecida para classes que desejem efetuar
adição, subtração e outras operações.
A seguir, um exemplo na linguagem C++ em que existe a sobrecarga do
operador adição (+) para a manipulação de objetos do tipo Vetor2D.
// declaração da classe em C++
class Vetor2D
{
// atributos
float x, y;
public:
// construtor
Vetor2D(Vetor2D v2);
// métodos
void EscreveVideo(char texto[]);
Vetor2D operator + (Vetor2D v2);
} // fim da classe Vetor2D
22
Capítulo 1: Programação Orientada a Objetos
No exemplo anterior é declarado um método:
Vetor2D operator + (Vetor2D v2);
Existe nesse método a sobrecarga do operador + (adição). Essa sobrecarga
torna possível a soma de dois objetos do tipo Vetor2D:
Vetor2D v1 = new Vetor2D;
Vetor2D v2 = new Vetor2D;
// uso do operador adição entre instâncias do tipo Vetor2D
Vetor2D v3 = v2 + v1;
No caso apresentado acima, o objeto v2 é responsável por executar de forma
implícita uma chamada para o operador adição sobrecarregado (+), o qual executa
uma determinada função sobre os dados. Na chamada para execução do operador
sobrecarregado, é enviado o objeto v1 como parâmetro. Na declaração da classe
Vetor2D, observa-se que o operador adição sobrecarregado recebe somente um
parâmetro que é do mesmo tipo da classe.
O que é Polimorfismo?
É a troca de mensagens entre métodos de uma subclasse com sua superclasse
e assim sucessivamente. O polimorfismo pode ser observado por meio de métodos
virtuais de uma classe como representado a seguir:
Figura 1.7 – Métodos Virtuais.
Na figura acima observa-se que o método M1 da classe A chama o método M2
da mesma classe. Caso seja criada uma classe B que seja subclasse da classe A e seja
reescrito o método M2, têm-se:
1. Quando o método M1 for chamado por meio de um objeto da classe B,
então o método M1 é procurado nesta e não é encontrado, procura-se o
método nas superclasses da classe B até ser encontrado.
23
Capítulo 1: Programação Orientada a Objetos
2. Após o método M1 ser chamado, ele chama o método M2 que é, então,
procurado desde a subclasse, onde foi originada a mensagem, passando
às superclasses da hierarquia até ser encontrado.
3. Caso o método M2 seja encontrado na classe B (que enviou uma mensagem
para M1), então o método a ser chamado é o M2 da classe A ou o M2 da
classe B? A resposta correta é o M2 da classe B, pois ele foi reescrito na
classe que está fazendo chamadas para o método M1.
Para entender melhor o funcionamento do polimorfismo, considere uma
classe Madeira como superclasse, e uma subclasse chamada Cadeira. Considere o
método escreve_na_tela na classe Madeira, o qual faz uma chamada para o método
escreve na mesma classe. Caso o método escreve seja redefinido na subclasse, ou
seja, na classe Cadeira, se ocorrer uma chamada do método escreve_na_tela por meio
de um objeto da classe Cadeira, que originou a mensagem, o método a ser chamado
por escreve_na_tela será o escreve da classe Cadeira.
Desta forma, partindo de um objeto do tipo Cadeira toda mensagem executada,
ou seja, uma chamada a um método gera uma busca do método nesta classe, e
caso não seja encontrado, é feita uma busca na superclasse da classe Cadeira, ou
seja, a classe Madeira.
Caso não seja encontrado na classe Madeira, ocorre uma busca na superclasse
da classe Madeira e assim sucessivamente, até chegar à raiz da hierarquia de classes.
Encontrando o método, ele é executado e qualquer outra chamada dentro dele
gera novamente uma busca por um método desde a classe Cadeira; caso não seja
encontrado, será feita um busca na superclasse da classe Cadeira e assim
sucessivamente. Considere o exemplo em Java a seguir:
/*———declaração da superclasse———*/
class Madeira
{
// atributo da classe Madeira
String cor;
// métodos da classe Madeira
public void escreve_na_tela()
{
escreve();
}
public void escreve()
{
System.out.println("estou na classe Madeira!");
}
} // fim da declaração da superclasse
24
Capítulo 1: Programação Orientada a Objetos
/*——————declaração da subclasse——————*/
class Cadeira extends Madeira
{
// métodos adicionais à superclasse
public void escreve()
{
System.out.println("estou na classe Cadeira!");
}
} // fim da declaração da subclasse
Caso se declare um objeto da classe Madeira e execute o método escreve_na_tela,
este faz uma chamada a escreve da classe Madeira retornando o texto:
estou na classe Madeira!
Caso se declare um objeto da classe Cadeira que chame o método escreve_na_tela,
este faz uma chamada ao método escreve da classe em que o objeto foi declarado,
ou seja, classe Cadeira, e caso o método não seja encontrado, a chamada será
repassada à superclasse da classe Cadeira e assim sucessivamente.
Como o método escreve foi encontrado na classe Cadeira esse é executado e
gera a saída:
estou na classe Cadeira!
Na prática, cada classe procura os métodos sempre da classe de onde está
fazendo as chamadas em direção às superclasses, por exemplo:
Figura 1.8 – Busca de métodos virtuais.
25
Capítulo 1: Programação Orientada a Objetos
Na figura 1.8, o método M1 chama o método M2 na classe A. Considere a
declaração da classe B que herda as características da classe A e nesta seja reescrito
o método M2, declarando um objeto do tipo classe B que executa o método M1, o
que ocorre?
1. Primeiro, como se sabe, os métodos são procurados a partir de um objeto
de uma determinada classe em direção às superclasses.
2. Como o método M1 não existe na subclasse B, ele é procurado nas
superclasses até ser encontrado na classe A.
3. Após ser encontrado, ele é executado e chama o método M2. Quando
essa chamada ocorre, o compilador procura o método M2 a partir do tipo
do objeto que executou a primeira chamada, ou seja, um objeto do tipo B.
4. A procura do método M2 inicia-se a partir da classe B, onde já é encontrado
e chamado.
5. O conceito de polimorfismo é a ordem em que os métodos são montados
para sua chamada, da subclasse para a superclasse até ser encontrado.
Na linguagem Java todos os métodos são virtuais, portanto suportam
polimorfismo, bastando ser redeclarados nas subclasses. Em C++ isso não ocorre,
a superclasse deve permitir que determinado método seja reescrito nas subclasses.
O que são Construtores e Destrutores?
Construtores são métodos especiais chamados no processo de instanciação
de um objeto no sistema. A execução de tais métodos garante a inicialização dos
identificadores de forma correta. Todo construtor tem o mesmo nome da classe.
O exemplo a seguir apresenta dois construtores, um que não recebe parâmetro
algum e outro que recebe o nome de um cliente:
// declaração inicial da classe ciente
public class cliente
{
// atributos da classe cliente
private String nome;
private String endereco;
26
Capítulo 1: Programação Orientada a Objetos
// construtores
public cliente()
{
nome = "";
endereco = "";
}
public cliente(String novo_nome)
{
nome = novo_nome;
endereco = "";
}
// métodos da classe cliente
public void setNome (String novo_nome)
{
nome = novo_nome;
}
public void setEndereco (String novo_endereco)
{
endereco = novo_endereco;
}
public String getNome()
{
return nome;
}
public String getEndereco()
{
return endereco;
}
} // fim da declaração da classe Cliente
Todo construtor recebe o mesmo nome da classe. No exemplo anterior
existem dois construtores sobrecarregados, um que não recebe nenhum parâmetro
e outro que recebe uma string novo_nome. Portanto a criação de um objeto da classe
cliente pode ser feita de duas formas, pois existem dois construtores:
Primeira Forma
// utilizando o construtor sem parâmetros que inicia o objeto do tipo cliente.
// É executado o código contido nesse construtor
cliente c1 = new cliente();
27
Capítulo 1: Programação Orientada a Objetos
Segunda Forma
// utilizando o construtor que recebe como parâmetro uma string que identifica
// o nome do cliente. É executado o código contido nesse construtor
cliente c2 = new cliente("o cliente");
O construtor é chamado somente durante o processo de instanciação do
objeto. Nos construtores são definidos comportamentos de iniciação do objeto.
Quando um objeto, por exemplo, do tipo Cliente é instanciado, espera-se que o
comportamento inicial do cliente seja satisfeito pelos construtores. Quais tipos de
comportamento são esperados na instanciação de um cliente? Existem parâmetros
como nome do cliente, endereço, cidade, estado etc. Por meio dessa análise de
comportamento são implementados os construtores.
Destrutores são métodos especiais chamados na finalização do objeto.
Quando as posições de memória que um objeto ocupa devem ser liberadas, o
destrutor é chamado. Na linguagem C++, todo destrutor tem o nome da classe
precedido por ~ (til).
Observe o exemplo em C++ a seguir:
// declaração da classe em C++
class Vetor2D
{
// atributos
float x, y;
public:
// construtor
Vetor2D(Vetor2D v2);
// destrutor
~Vetor2D();
// métodos
void EscreveVideo(char texto[]);
Vetor2D operator+ (Vetor2D v2);
} // fim da classe Vetor2D
28
Capítulo 1: Programação Orientada a Objetos
Quando um objeto do tipo Vetor2D estiver sendo removido da memória, o
destrutor é chamado e nele deve haver chamadas que liberem a memória que está
sendo utilizada pelo objeto.
Quando uma instância é criada utilizando a palavra new, ela é alocada na memória
heap. As demais instâncias, tais como int, long entre outras, são alocadas na stack
(pilha do sistema). A stack é liberada automaticamente quando um programa ou
objeto morre, entretanto a memória heap não é liberada automaticamente. Portanto,
deve-se liberar a memória que foi alocada na heap utilizando a chamada delete sobre
as instâncias que foram declaradas com o uso da palavra reservada new. Por exemplo:
// para instanciar
cliente c1 = new cliente();
// para liberar a memória heap
delete c1;
Figura 1.9 – A memória heap e stack.
Portanto, o objetivo do destrutor é liberar a memória heap, alocada usando
new, que não é liberada automaticamente. A stack é liberada automaticamente.
29
Download