Hoje, a maioria das linguagens de programação são orientadas a objetos como Java, C#, Python e C++ e, apesar de terem algumas diferenças na implementação, todas seguem os mesmos princípios e conceitos. Muitos programadores, apesar de utilizarem linguagens orientadas a objetos, não sabem utilizar alguns dos principais conceitos desse paradigma orientado a objetos e, por isso, desenvolvem sistemas com alguns erros conceituais e acabam escrevendo mais código que o necessário, não conseguindo reutilizar o código como seria possível.
Este artigo fará uma revisão dos principais conceitos de orientação a objetos e mostrará diversos exemplos de implementação dos conceitos de orientação a objetos em Java. Os principais conceitos do paradigma orientado a objetos são Classes e Objetos, Associação,Encapsulamento, Herança e Polimorfismo.
Já viu as novidades que a DevMedia preparou nos cursos de Java? Confira!
Classe e Objeto
Uma classe é uma forma de definir um tipo de dado em uma linguagem orientada a objeto. Ela é formada por dados e comportamentos.
Para definir os dados são utilizados os atributos, e para definir o comportamento são utilizados métodos. Depois que uma classe é definida podem ser criados diferentes objetos que utilizam a classe. A Listagem 1 mostra a definição da classe Empresa, que tem os atributos nome, endereço, CNPJ, data de fundação, faturamento, e também o método imprimir, que apenas mostra os dados da empresa.
package com.devmedia.model;
import java.util.Date;
public class Empresa {
private String nome;
private String cnpj;
private String endereco;
private Date dataFundacao;
private float faturamento;
public void imprimir() {
System.out.println("Nome: " + nome);
System.out.println("CNPJ: " + cnpj);
System.out.println("Endereço: " + endereco);
System.out.println("Data de Fundação: " + dataFundacao);
}
public String getNome() {
return nome;
}
public void setNome(String nome) {
this.nome = nome;
}
public String getCnpj() {
return cnpj;
}
public void setCnpj(String cnpj) {
this.cnpj = cnpj;
}
public String getEndereco() {
return endereco;
}
public void setEndereco(String endereco) {
this.endereco = endereco;
}
public Date getDataFundacao() {
return dataFundacao;
}
public void setDataFundacao(Date dataFundacao) {
this.dataFundacao = dataFundacao;
}
public float getFaturamento() {
return faturamento;
}
public void setFaturamento(float faturamento) {
this.faturamento = faturamento;
}
}
Com a classe definida, podem ser criados diversos objetos do tipo Empresa, por isso a Listagem 2 mostra como criar esses objetos, bastando declarar uma variável com o tipo Empresa e com a palavra reservada new criar um novo objeto. Depois podemos definir os dados para os atributos da classe Empresa e, por fim, chamar o método definido.
package com.devmedia.main;
import java.util.Date;
import com.devmedia.model.Empresa;
public class Main {
public static void main(String[] args) {
Empresa empresa1 = new Empresa();
empresa1.setNome("Loja 1");
empresa1.setCnpj("12343232");
empresa1.setDataFundacao(new Date());
empresa1.setEndereco("Rua abc, 100");
empresa1.setFaturamento(50000);
empresa1.imprimir();
Empresa empresa2 = new Empresa();
empresa2.setNome("Loja 2");
empresa2.setCnpj("12354432");
empresa2.setDataFundacao(new Date());
empresa2.setEndereco("Rua abc, 200");
empresa2.setFaturamento(50000);
empresa2.imprimir();
Empresa empresa3 = new Empresa();
empresa3.setNome("Posto de Gasolina");
empresa3.setCnpj("12345434");
empresa3.setEndereco("Rua afd, 500");
empresa3.setFaturamento(10000);
empresa3.setDataFundacao(new Date());
empresa3.imprimir();
}
}
Saiba mais: Cursos de Engenharia de Software
Encapsulamento
O conceito do encapsulamento consiste em “esconder” os atributos da classe de quem for utilizá-la. Isso se deve por dois motivos principais.
Um é para que alguém que for usar a classe não a use de forma errada como, por exemplo, em uma classe que tem um método de divisão entre dois atributos da classe - se o programador java não conhecer a implementação interna da classe, ele pode colocar o valor zero no atributo do dividendo, mas se a classe estiver corretamente encapsulada podemos impedir que o programador faça isso. Esse tipo de implementação é feito via os métodos get e set. A Listagem 3 mostra o código da classe Divisao citada como exemplo.
package com.devmedia.model;
public class Divisao {
private int num1;
private int num2;
public void divisao() {
System.out.println("A divisao e: " + (num1 / num2));
}
public int getNum1() {
return num1;
}
public void setNum1(int num1) {
this.num1 = num1;
}
public int getNum2() {
return num2;
}
public void setNum2(int num2) {
if (num2 == 0) {
num2 = 1;
} else {
this.num2 = num2;
}
}
}
O outro motivo é de manter todo o código de uma determinada classe encapsulada dentro dela mesmo como, por exemplo, se existe uma classe Conta, talvez seja melhor não permitir que um programador acesse o atributo saldo diretamente, nem mesmo com os métodos get e set, mas somente por operações, como saque, depósito e saldo. A Listagem 4 mostra a implementação da classe Conta, onde só é possível acessar a atributo saldo pelas operações disponibilizadas na classe e os outros atributos podem ser acessados via métodos get e set.
package com.devmedia.model;
public class Conta {
private String agencia;
private String numero;
private float saldo;
public void saque(float valor) {
if (saldo < valor) {
System.out.println("O saldo é insuficiente!");
} else {
saldo -= valor;
}
}
public String getAgencia() {
return agencia;
}
public void setAgencia(String agencia) {
this.agencia = agencia;
}
public String getNumero() {
return numero;
}
public void setNumero(String numero) {
this.numero = numero;
}
public void deposito(float valor) {
saldo += valor;
}
public void saldo() {
System.out.println("O saldo é: " + saldo);
}
}
Associação de Classes
A associação de classes indica quando uma classe tem um tipo de relacionamento "tem um" com outra classe como, por exemplo, uma pessoa tem um carro e isso indica que a classe Pessoa tem uma associação com a classe Carro. Esse tipo de relacionamento entre classes é importante, porque define como as classes interagem entre elas nas aplicações.
A Listagem 5 mostra como implementar uma associação um para um. A associação é feita entre as classes Pessoa e Carro. A classe Carro tem os atributos modelo, placa, ano e valor; e a classe Pessoa tem os atributos nome, endereço, telefone e dataNascimento. Além disso, ela tem um relacionamento com a classe Carro.
package com.devmedia.model;
public class Carro {
private String modelo;
private String placa;
private int ano;
private float valor;
public String getModelo() {
return modelo;
}
public void setModelo(String modelo) {
this.modelo = modelo;
}
public String getPlaca() {
return placa;
}
public void setPlaca(String placa) {
this.placa = placa;
}
public int getAno() {
return ano;
}
public void setAno(int ano) {
this.ano = ano;
}
public float getValor() {
return valor;
}
public void setValor(float valor) {
this.valor = valor;
}
}
package com.devmedia.model;
import java.util.Date;
public class Pessoa {
private String nome;
private String endereco;
private String telefone;
private Date dataNascimento;
// Relacionamento com a classe Carro
private Carro carro;
public String getNome() {
return nome;
}
public void setNome(String nome) {
this.nome = nome;
}
public String getEndereco() {
return endereco;
}
public void setEndereco(String endereco) {
this.endereco = endereco;
}
public String getTelefone() {
return telefone;
}
public void setTelefone(String telefone) {
this.telefone = telefone;
}
public Date getDataNascimento() {
return dataNascimento;
}
public void setDataNascimento(Date dataNascimento) {
this.dataNascimento = dataNascimento;
}
public Carro getCarro() {
return carro;
}
public void setCarro(Carro carro) {
this.carro = carro;
}
}
Além do relacionamento um para um, também existem o relacionamento um para N e N para N. Esses relacionamentos são modelados com um vetor de objetos de uma classe para outro como, por exemplo, se uma pessoa pode ter mais de um carro, é necessário mapear esse relacionamento com uma lista de carros na classe Pessoa. A Listagem 6 mostra a alteração necessária na classe Pessoa, mas na classe Carro não é necessária nenhuma alteração.
package com.devmedia.model;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
public class Pessoa {
private String nome;
private String endereco;
private String telefone;
private Date dataNascimento;
// Relacionamento com a classe Carro
private List<Carro> carros = new ArrayList<Carro>();
public String getNome() {
return nome;
}
public void setNome(String nome) {
this.nome = nome;
}
public String getEndereco() {
return endereco;
}
public void setEndereco(String endereco) {
this.endereco = endereco;
}
public String getTelefone() {
return telefone;
}
public void setTelefone(String telefone) {
this.telefone = telefone;
}
public Date getDataNascimento() {
return dataNascimento;
}
public void setDataNascimento(Date dataNascimento) {
this.dataNascimento = dataNascimento;
}
public List<Carro> getCarros() {
return carros;
}
public void setCarros(List<Carro> carros) {
this.carros = carros;
}
}
Herança
A herança é um tipo de relacionamento que define que uma classe "é um" de outra classe como, por exemplo, a classe Funcionario que é uma Pessoa, assim um Funcionário tem um relacionamento de herança com a classe Pessoa. Em algumas linguagens, como C#, é possível fazer herança múltipla, isto é, uma classe pode herdar de diversas outras classes, mas em Java isso não é permitido, pois cada classe pode herdar de apenas outra classe.
A Listagem 7 mostra como implementar a herança entre classes: a classe Pessoa definida será utilizada como base para as outras classes. Será definida a classe Funcionario, com os atributos salario e matricula, e a classe Aluno com os atributos registroAcademico e curso. Como é possível observar, para implementar herança em Java usaremos a palavra reservada extends.
package com.devmedia.model;
public class Funcionario extends Pessoa {
private float salario;
private String matricula;
public float getSalario() {
return salario;
}
public void setSalario(float salario) {
this.salario = salario;
}
public String getMatricula() {
return matricula;
}
public void setMatricula(String matricula) {
this.matricula = matricula;
}
}
package com.devmedia.model;
public class Aluno extends Pessoa {
private String registroAcademico;
private String curso;
private float mensalidade;
public String getRegistroAcademico() {
return registroAcademico;
}
public void setRegistroAcademico(String registroAcademico) {
this.registroAcademico = registroAcademico;
}
public String getCurso() {
return curso;
}
public void setCurso(String curso) {
this.curso = curso;
}
public float getMensalidade() {
return mensalidade;
}
public void setMensalidade(float mensalidade) {
this.mensalidade = mensalidade;
}
}
Sobre a herança em Java é importante saber que existem quatro tipos de acesso aos atributos:
- public: que permite que métodos e atributos sejam acessados diretamente de qualquer classe;
- private: que permite que métodos e atributos sejam acessados apenas dentro da classe;
- protected: que permite que métodos e atributos sejam acessados apenas dentro da própria classe e em classes filhas;
- tipo de acesso padrão, que permite que métodos e atributos sejam acessadas por qualquer classes que esteja no mesmo pacote.
Como usamos a classe Pessoa com herança, uma possibilidade é mudar o tipo de acesso dos atributos da classe Pessoa para protegido. A Listagem 8 mostra a alteração feita.
package com.devmedia.model;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
public class Pessoa {
protected String nome;
protected String endereco;
protected String telefone;
protected Date dataNascimento;
// Relacionamento com a classe Carro
protected List<Carro> carros = new ArrayList<Carro>();
public String getNome() {
return nome;
}
public void setNome(String nome) {
this.nome = nome;
}
public String getEndereco() {
return endereco;
}
public void setEndereco(String endereco) {
this.endereco = endereco;
}
public String getTelefone() {
return telefone;
}
public void setTelefone(String telefone) {
this.telefone = telefone;
}
public Date getDataNascimento() {
return dataNascimento;
}
public void setDataNascimento(Date dataNascimento) {
this.dataNascimento = dataNascimento;
}
public List<Carro> getCarros() {
return carros;
}
public void setCarros(List<Carro> carros) {
this.carros = carros;
}
}
Saiba mais: Os pilares da Programação Orientada a Objetos
Polimorfismo
O Polimorfismo é a possibilidade de em uma hierarquia de classes implementar métodos com a mesma assinatura e, assim, implementar um mesmo código que funcione para qualquer classe dessa hierarquia sem a necessidade de implementações específicas para cada classe. O principal objetivo do polimorfismo é diminuir a quantidade de código escrito, aumentando a clareza e a facilidade de manutenção.
A Listagem 9 mostra um exemplo de polimorfismo, onde a classe Conta tem um método saque que apenas permite o saque caso o cliente tenha saldo em sua conta. A classe ContaEspecial, que é filha da classe Conta, permite que o cliente faça um saque caso tenha saldo ou tenha ainda limite no cheque especial, e a classe ContaPoupanca permite que o cliente faça um saque caso ainda tenha saldo na conta corrente ou saldo na conta poupança.
package com.devmedia.model;
public class Conta {
protected String agencia;
protected String numero;
protected float saldo;
public void saque(float valor) {
if (saldo < valor) {
System.out.println("O saldo é insuficiente!");
} else {
saldo -= valor;
System.out.println("Saque efetuado com sucesso!");
}
}
public String getAgencia() {
return agencia;
}
public void setAgencia(String agencia) {
this.agencia = agencia;
}
public String getNumero() {
return numero;
}
public void setNumero(String numero) {
this.numero = numero;
}
public void deposito(float valor) {
saldo += valor;
}
public void saldo() {
System.out.println("O saldo é: " + saldo);
}
}
package com.devmedia.model;
public class ContaEspecial extends Conta {
private float limite;
public void saque(float valor) {
if (saldo + limite < valor) {
System.out.println("O saldo é insuficiente!");
} else {
saldo -= valor;
System.out.println("Saque efetuado com sucesso!");
}
}
public float getLimite() {
return limite;
}
public void setLimite(float limite) {
this.limite = limite;
}
}
package com.devmedia.model;
public class ContaPoupanca extends Conta {
private float saldoPoupanca;
public void saque(float valor) {
if (saldo + saldoPoupanca < valor) {
System.out.println("O saldo é insuficiente!");
} else {
if (saldo < valor) {
valor -= saldo;
saldo = 0;
saldoPoupanca -= valor;
} else {
saldo -= valor;
}
System.out.println("Saque efetuado com sucesso!");
}
}
public void depositoPoupanca(float valor) {
saldoPoupanca += valor;
}
private float getSaldoPoupanca() {
return saldoPoupanca;
}
private void setSaldoPoupanca(float saldoPoupanca) {
this.saldoPoupanca = saldoPoupanca;
}
}
A Listagem 10 mostra o uso das classes, apesar de serem definidos objetos dos três tipos de classes. O método fazSaque pode ser utilizado para qualquer um dos tipos de conta, não dependendo do tipo que é passado como parâmetro para ele, e isso é possível graças ao polimorfismo, pois o método saque das três classes tem a mesma assinatura e em tempo de execução a JVM sabe o método saque de qual classe executar.
package com.devmedia.main;
import javax.swing.JOptionPane;
import com.devmedia.model.Conta;
import com.devmedia.model.ContaEspecial;
import com.devmedia.model.ContaPoupanca;
public class MainConta {
public static void main(String [] args) {
Conta conta = new Conta();
conta.setAgencia("1234");
conta.setNumero("1244");
conta.deposito(3000);
ContaPoupanca contaPoupanca = new ContaPoupanca();
contaPoupanca.setAgencia("3456");
contaPoupanca.setNumero("1234");
contaPoupanca.deposito(3000);
contaPoupanca.depositoPoupanca(1000);
ContaEspecial contaEspecial = new ContaEspecial();
contaEspecial.setAgencia("3432");
contaEspecial.setLimite(1000);
contaEspecial.setNumero("1434");
contaEspecial.deposito(3000);
fazSaque(conta);
fazSaque(contaPoupanca);
fazSaque(contaEspecial);
}
public static void fazSaque(Conta conta) {
float valor =
Float.parseFloat(
JOptionPane.showInputDialog("Digite o valor do saque")
);
conta.saque(valor);
conta.saldo();
}
}
Este artigo mostrou alguns dos principais conceitos do paradigma de programação Orientado a Objetos com o objetivo de ajudar pessoas que estão começando a programar com esse paradigma e auxiliar aqueles que apesar de já programarem com linguagens orientadas a objetos ainda tem algumas dúvidas de como utilizar alguns conceitos desse paradigma.
Espero que este artigo tenha sido útil! Até a próxima.
Links Úteis
- Você conhece o método chinês?: O método chinês é uma técnica de depuração manual, amplamente utilizada no meio acadêmico, mas especialmente útil quando precisamos analisar um código sem ter o apoio de um editor.
- Criando meu primeiro projeto no Java: Neste curso você aprenderá a criar o seu primeiro programa com Java, e não, ele não será um simples “Hello, World!”.
- Delphi Exceptions: Trabalhando com exceções em Delphi: Neste curso você aprenderá a realizar o tratamento de exceções no Delphi, técnica que visa garantir o bom funcionamento da aplicação mesmo na ocorrência de certos erros.
Saiba mais sobre Orientação a Objetos ;)
- Os pilares da Programação Orientada a Objetos: Neste DevCast veremos quais são os quatro pilares da Orientação a Objetos, seu conceito e sua importância na programação.
- Orientação a Objetos em C#: Neste Guia de Referência você encontrará todo o conteúdo que precisa para aprender a programar Orientado a Objetos na linguagem C#.
- Orientação a Objetos em Java: Nesse Guia de Referência você encontrará todo o conteúdo que precisa para aprender a programar Orientado a Objetos em Java, paradigma padrão dessa linguagem e que visa o desenvolvimento com base em objetos.