Pular para o conteúdo

Programacao Orientada a Objetos

POO e um paradigma de programacao que organiza o codigo em torno de objetos - entidades que combinam estado (atributos) e comportamento (metodos). Em Java, tudo (exceto tipos primitivos) e um objeto.

Uma classe e o molde. Um objeto e uma instancia concreta desse molde.

public class Carro {
// Atributos (estado)
String marca;
String modelo;
int ano;
double velocidadeAtual;
// Metodos (comportamento)
void acelerar(double incremento) {
velocidadeAtual += incremento;
}
void frear(double decremento) {
velocidadeAtual = Math.max(0, velocidadeAtual - decremento);
}
void buzinar() {
System.out.println("Beep beep!");
}
}

Criando e usando objetos:

public class Main {
public static void main(String[] args) {
Carro meuCarro = new Carro();
meuCarro.marca = "Toyota";
meuCarro.modelo = "Corolla";
meuCarro.ano = 2023;
meuCarro.acelerar(60);
System.out.println(meuCarro.velocidadeAtual); // 60.0
meuCarro.buzinar();
}
}

Construtores inicializam objetos no momento da criacao. Tem o mesmo nome da classe e nenhum tipo de retorno:

public class Carro {
private String marca;
private String modelo;
private int ano;
public Carro(String marca, String modelo, int ano) {
this.marca = marca;
this.modelo = modelo;
this.ano = ano;
}
// Construtor vazio que delega ao principal
public Carro() {
this("Desconhecida", "Desconhecido", 0);
}
}
Carro c1 = new Carro("Honda", "Civic", 2022);
Carro c2 = new Carro(); // usa defaults

Encapsular significa proteger o estado interno de uma classe e expor apenas o necessario. O padrao e deixar atributos private e fornecer metodos public de acesso:

public class ContaBancaria {
private double saldo;
private String titular;
public ContaBancaria(String titular, double saldoInicial) {
this.titular = titular;
this.saldo = saldoInicial >= 0 ? saldoInicial : 0;
}
public double getSaldo() {
return saldo;
}
public void depositar(double valor) {
if (valor > 0) {
saldo += valor;
}
}
public boolean sacar(double valor) {
if (valor > 0 && valor <= saldo) {
saldo -= valor;
return true;
}
return false;
}
}

O saldo nunca fica negativo porque a validacao acontece dentro da classe. Codigo externo nao tem como burlar isso.

Heranca permite que uma classe reutilize e estenda o comportamento de outra. Usa-se extends:

public class Veiculo {
protected String marca;
protected int ano;
public Veiculo(String marca, int ano) {
this.marca = marca;
this.ano = ano;
}
public void ligar() {
System.out.println("Vrum!");
}
public String getInfo() {
return marca + " (" + ano + ")";
}
}
public class Carro extends Veiculo {
private int numeroDePortas;
public Carro(String marca, int ano, int portas) {
super(marca, ano); // chama o construtor da superclasse
this.numeroDePortas = portas;
}
@Override
public String getInfo() {
return super.getInfo() + " - " + numeroDePortas + " portas";
}
}
Carro c = new Carro("Honda", 2022, 4);
c.ligar(); // herdado de Veiculo
System.out.println(c.getInfo()); // "Honda (2022) - 4 portas"

Java suporta apenas heranca simples - uma classe so pode extends uma superclasse. Para heranca multipla de comportamento, usa-se interfaces.

Polimorfismo significa que um mesmo tipo de referencia pode se comportar de formas diferentes dependendo do objeto real:

public class Animal {
public void emitirSom() {
System.out.println("...");
}
}
public class Cachorro extends Animal {
@Override
public void emitirSom() {
System.out.println("Au au!");
}
}
public class Gato extends Animal {
@Override
public void emitirSom() {
System.out.println("Miau!");
}
}
Animal[] animais = {new Cachorro(), new Gato(), new Cachorro()};
for (Animal a : animais) {
a.emitirSom(); // comportamento diferente para cada tipo real
}
// Au au!
// Miau!
// Au au!

Sobrecarga e quando varios metodos tem o mesmo nome mas assinaturas diferentes. A escolha acontece em tempo de compilacao:

public class Calculadora {
public int somar(int a, int b) {
return a + b;
}
public double somar(double a, double b) {
return a + b;
}
public int somar(int a, int b, int c) {
return a + b + c;
}
}

Sobrescrita (@Override) e quando uma subclasse reimplementa um metodo da superclasse. A escolha acontece em tempo de execucao:

@Override
public void emitirSom() {
System.out.println("Au au!");
}

A anotacao @Override e opcional mas recomendada - se o metodo nao existir na superclasse, o compilador sinaliza o erro.

Abstracao significa expor apenas o que e necessario e ocultar os detalhes de implementacao.

Uma classe abstrata nao pode ser instanciada diretamente. Pode ter metodos concretos e abstratos:

public abstract class Forma {
private String cor;
public Forma(String cor) {
this.cor = cor;
}
public String getCor() {
return cor;
}
// Subclasses sao obrigadas a implementar
public abstract double calcularArea();
public abstract double calcularPerimetro();
}
public class Circulo extends Forma {
private double raio;
public Circulo(String cor, double raio) {
super(cor);
this.raio = raio;
}
@Override
public double calcularArea() {
return Math.PI * raio * raio;
}
@Override
public double calcularPerimetro() {
return 2 * Math.PI * raio;
}
}

Uma interface e um contrato - define o que uma classe deve fazer, sem especificar como:

public interface Pagamento {
boolean processar(double valor);
String getDescricao();
}
public class CartaoCredito implements Pagamento {
private String numero;
public CartaoCredito(String numero) {
this.numero = numero;
}
@Override
public boolean processar(double valor) {
System.out.println("Processando R$ " + valor + " no cartao " + numero);
return true;
}
@Override
public String getDescricao() {
return "Cartao final " + numero.substring(numero.length() - 4);
}
}
public class Pix implements Pagamento {
private String chave;
public Pix(String chave) {
this.chave = chave;
}
@Override
public boolean processar(double valor) {
System.out.println("Enviando R$ " + valor + " via Pix para " + chave);
return true;
}
@Override
public String getDescricao() {
return "Pix - chave: " + chave;
}
}
// Mesma interface, implementacoes diferentes
Pagamento p = new CartaoCredito("1234567890123456");
p.processar(150.00);
Pagamento p2 = new Pix("email@exemplo.com");
p2.processar(150.00);

Uma classe pode implementar multiplas interfaces:

public class ContaDigital implements Pagamento, Investimento, Auditavel {
// implementa todos os contratos
}
CaracteristicaClasse AbstrataInterface
InstanciacaoNaoNao
Heranca multiplaNao (so uma por classe)Sim (varias por classe)
Metodos com corpoSimSim (default methods, Java 8+)
Atributos de instanciaSimNao (so constantes)
Quando usarCompartilhar implementacaoDefinir contratos
ModificadorMesma classeMesmo pacoteSubclasseQualquer classe
publicSimSimSimSim
protectedSimSimSimNao
(default)SimSimNaoNao
privateSimNaoNaoNao

Regra geral: atributos sempre private, metodos public ou protected quando precisam ser herdados, private para auxiliares internos.

static indica que o membro pertence a classe, nao a objetos individuais:

public class Contador {
private static int total = 0; // compartilhado entre todas as instancias
private int id;
public Contador() {
total++;
this.id = total;
}
public static int getTotal() { return total; }
public int getId() { return id; }
}
Contador c1 = new Contador();
Contador c2 = new Contador();
Contador c3 = new Contador();
System.out.println(Contador.getTotal()); // 3

Metodos estaticos nao podem acessar atributos de instancia - nao existe this nesse contexto.