Da trilha de Java Este conceito ainda não saiu como card do dia. ir para o card de hoje

Java POO Básico

Encapsulamento

Encapsulamento é o princípio de esconder o estado interno de um objeto e expor apenas o que ele precisa mostrar. Em Java, isso é feito com modificadores de acesso (private, protected, public) aplicados a atributos e métodos. A ideia é que ninguém de fora consiga alterar o estado de um objeto de forma inválida.

Quando um atributo é private, o acesso externo só acontece por meio de getters e setters. Isso permite validar valores antes de gravar, garantindo que o objeto nunca entre em um estado inconsistente — o que chamamos de invariante. Por exemplo, uma conta bancária não deveria ter saldo negativo se isso não faz sentido para o domínio.

Um passo além é a imutabilidade: se um objeto não muda depois de criado, ele é seguro para compartilhar entre threads e mais fácil de raciocinar. Em Java, isso é obtido com atributos private final, sem setters, e com cópias defensivas de objetos mutáveis recebidos ou devolvidos.

Neste card, vamos ver como encapsular uma classe Conta, proteger seu saldo com validações e, por fim, criar uma versão imutável usando final e ausência de setters.

Pontos-chave

  • private esconde o atributo; public expõe apenas o que faz parte da API da classe.
  • Setters permitem validar valores e manter invariantes, como saldo >= 0.
  • Getters não devem vazar referências internas mutáveis; devolva cópias quando necessário.
  • Imutabilidade combina private final, ausência de setters e cópias defensivas.
  • Encapsular não é só gerar getters/setters: é decidir quais operações fazem sentido para o domínio.
ContaBancaria.java
public class ContaBancaria {
    private final String titular; // final: não muda após o construtor
    private double saldo;         // privado: só acessível dentro da classe

    public ContaBancaria(String titular, double saldoInicial) {
        if (titular == null || titular.isBlank()) {
            throw new IllegalArgumentException("titular obrigatório");
        }
        if (saldoInicial < 0) {
            throw new IllegalArgumentException("saldo inicial não pode ser negativo");
        }
        this.titular = titular;
        this.saldo = saldoInicial;
    }

    public String getTitular() { return titular; }
    public double getSaldo() { return saldo; }

    public void depositar(double valor) {
        if (valor <= 0) throw new IllegalArgumentException("valor deve ser positivo");
        saldo += valor;
    }

    public void sacar(double valor) {
        if (valor <= 0) throw new IllegalArgumentException("valor deve ser positivo");
        if (valor > saldo) throw new IllegalStateException("saldo insuficiente");
        saldo -= valor;
    }

    public static void main(String[] args) {
        ContaBancaria c = new ContaBancaria("Ana", 100.0);
        c.depositar(50.0);
        c.sacar(30.0);
        System.out.println(c.getTitular() + ": " + c.getSaldo());
        // c.saldo = -999; // erro de compilação: saldo é private
    }
}

Exercícios

  1. 1
    Fácil

    Encapsulando uma Pessoa

    Crie uma classe Pessoa com os atributos nome (String) e idade (int), ambos privados. Exponha getters e um setter de idade que rejeite valores negativos lançando IllegalArgumentException.

    Resolver
  2. 2
    Médio

    Conta com invariante de saldo

    Implemente a classe Conta com titular (String) e saldo (double) privados. O saldo nunca pode ficar negativo. Forneça depositar(double) e sacar(double) que lancem IllegalArgumentException para valores não positivos e IllegalStateException quando o saque exceder o saldo.

    Resolver
  3. 3
    Desafio

    Ponto imutável com cópia defensiva

    Crie uma classe imutável Ponto com coordenadas x e y (private final double). Ela deve ter um método mover(double dx, double dy) que retorna um novo Ponto em vez de alterar o atual. Adicione também um método distanciaA(Ponto outro) que calcula a distância euclidiana.

    Resolver

Continue estudando