Classes em Java
Java fornece suporte aos principais conceitos de orientação de objetos, incluindo: classes, atributos, métodos, herança, interfaces, polimorfismo e encapsulamento.
Classes, atributos e métodos
Em Java, uma classe pode ser definida através de uma declaração contendo
a palavra reservada class, um nome e um corpo (no qual podem ser
definidos atributos e métodos). Além disso, existem outros componentes
opcionais, alguns dos quais serão discutidos em mais detalhes no
decorrer desta parte. Considere, por exemplo, uma classe Conta.
Poderíamos criar essa classe em Java, de maneira pública (palavra
reservada public), de forma que possa ser acessada por outras classes,
da seguinte forma:
public class Conta{
// Corpo da classe
}
No corpo da classe, podemos adicionar atributos e métodos. No exemplo a
seguir, adicionamos um atributo saldo do tipo Double1 (linha
), e um método sacar
(linhas a
), que não retorna
nada (void) e que tem um parâmetro formal chamado valor do tipo
Double. Este método retira do saldo o valor passado como parâmetro
(note que no corpo de sacar temos um acesso de leitura e de escrita ao
atributo saldo):
public class Conta{
public Double saldo;
public void sacar(Double valor){
saldo = saldo - valor; // ou saldo -= valor;
}
}
Analogamente, poderíamos ter também um método depositar, que
incrementa o saldo da Conta:
public class Conta{
// saldo e sacar omitidos
public void depositar(Double valor){
saldo = saldo + valor; // ou saldo += valor;
}
}
Considere, por exemplo, um cliente que exercita algumas operações
possíveis em uma conta. Primeiro, é criado um objeto da classe Conta
através da chamada ao construtor, sendo a referência ao objeto atribuída
à uma variável c (linha
). A seguir, o
saldo da conta é modificado para R$ 100,00 através de uma referência ao
atributo saldo (linha ). Então, é feito um saque
de R$ 50,00 (via chamada a sacar) (linha
) e um depósito de
R$ 100,00 (via chamada a depositar) na linha
. Por fim, a linha
mostra a
impressão do saldo.
void main(){
var c = new Conta();
c.saldo = 100.00;
c.sacar(50.00);
c.depositar(100.00);
IO.println("Saldo: " + c.saldo);
}
A saída deste programa seria:
Saldo: 150.0
Note que os métodos sacar e depositar (chamadas nas linhas
e
, respectivamente)
precisam de um objeto da classe Conta para serem invocados, pois são
métodos de instância, bem como o método main, que agora nas versões
mais recentes também pode ser ou um método estático ou um método de
instância (como neste caso)2.
O exemplo anterior ilustra o uso de atributos e métodos por parte de um
cliente da classe Conta, de uma classe implicitamente declarada, sem
nome. O exemplo, no entanto, em seu método main, acessa diretamente o
saldo da Conta e faz com que o acoplamento entre as classes seja
forte (o que não é recomendável)3. Uma alternativa é encapsular o
acesso ao atributo saldo, fazendo com que as leituras e escritas sejam
feitas através de métodos específicos para tal. Isso desacopla as
classes ao permitir que a representação interna do saldo possa ser
modificada posteriormente (desde que as operações de leitura e escrita
sejam mantidas). Além disso, é recomendável diminuir a visibilidade do
atributo, de forma que ele só possa ser acessado de dentro da classe.
Em Java, por exemplo, existem as seguintes visibilidades: padrão (visível dentro do pacote), privada (visível apenas dentro da classe), pública (visível por todas as classes), e protegida (visível na classe e em todas as suas subclasses). Para cada tipo de visibilidade, pode existir uma palavra chave associada (exceto para o padrão, para o qual nenhuma palavra chave é informada).
O código a seguir mostra as modificações feitas de forma a melhorar o
encapsulamento do saldo e ocultar informações internas da classe de seus
clientes. Isso é feito através da criação de dois métodos públicos de
leitura e de escrita (getSaldo e setSaldo, iniciando nas linhas
e
, respectivamente),
da alteração do modificador de visibilidade de saldo para private
(linha ) e da escrita do saldo
(dentro dos métodos sacar e depositar). Note que o método setSaldo
usa uma referência a this para desambiguar o saldo (existe o atributo
saldo e o parâmetro formal do método chamado saldo). O this
representa o objeto atual. Então quando fazemos this.saldo estamos nos
referindo ao atributo saldo (e não ao parâmetro):
public class Conta{
private Double saldo;
public Double getSaldo(){
return saldo;
}
public void setSaldo(Double saldo){
this.saldo = saldo;
}
public void sacar(Double valor){
setSaldo(getSaldo() - valor);
}
public void depositar(Double valor){
setSaldo(getSaldo() + valor);
}
}
O cliente da classe também é alterado, passando a modificar o saldo
através do método de escrita (setSaldo):
void main(){
var c = new Conta();
c.setSaldo(100.00);
c.sacar(50.00);
c.depositar(100.00);
IO.println("Saldo: " + c.getSaldo());
}
Em Java, as classes possuem um construtor padrão, sem parâmetros, que é
herdado da classe Object. Podem ser definidos novos construtores para
facilitar o uso da classe por parte dos clientes. Considere, por
exemplo, a classe Conta com um construtor que recebe um valor como
parâmetro (linha ) e preenche o
saldo com este valor (através de uma chamada a setSaldo - linha
). Caso fosse
necessário chamar o construtor da superclasse, o método super poderia
ser usado:
public class Conta{
// Atributos e métodos de Conta
public Conta(Double valor){
setSaldo(valor);
}
}
O cliente precisaria ser modificado de forma a usar o novo construtor
definido (linha ), passando o
saldo inicial como parâmetro, pois a classe Conta agora não tem mais o
construtor padrão4.
void main(){
var c = new Conta(100.00);
c.sacar(50.00);
c.depositar(100.00);
IO.println("Saldo: " + c.getSaldo());
}
Em Java não há métodos destrutores, pois os objetos que não estão mais sendo referenciados são coletados automaticamente pelo coletor de lixo.
Este artigo é uma adaptação de conteúdo do livro Orientação a Objetos com Java, de Eduardo Kessler Piveta.
Leia também
- Conceitos Básicos de Orientação a Objetos
- Herança em Java
- Polimorfismo em Java
- Interfaces em Java
- Links POO - Java
- Exercícios de Orientação a Objetos
-
Estamos usando um
Doublepara fins de simplificação. Usualmente valores monetários não são armazenados em variáveis do tipo ponto flutuante, mas sim em inteiros longos (comolongouBigDecimal). ↩ -
Vide JEP 512: https://openjdk.org/jeps/512 ↩
-
Nesta frase, o trecho “as classes” refere-se à classe
Contae a classe implícita, não nomeada, que contém o métodomain. ↩ -
Ao definirmos um novo construtor, a classe deixa de ter o construtor padrão. Caso desejado, pode ser criado também um construtor padrão, sem parâmetros. ↩