CodeGym /Cursos /JAVA 25 SELF /Princípios de encapsulamento: para que serve

Princípios de encapsulamento: para que serve

JAVA 25 SELF
Nível 15 , Lição 0
Disponível

1. Definição de encapsulamento

Encapsulamento — é um dos princípios fundamentais da programação orientada a objetos (POO). Em termos simples, encapsulamento é a capacidade de esconder as “entranhas” do objeto e dar acesso a elas apenas por meio de “portas” especialmente previstas — métodos públicos.

Imagine uma cafeteira moderna. O usuário vê apenas botões e o display — ele não precisa saber como são a caldeira, a bomba e os tubos por dentro. Ele aperta “Cappuccino” — e recebe o resultado. Tudo que está dentro fica oculto. Isso é encapsulamento!

Em Java (e em outras linguagens orientadas a objetos), o encapsulamento é alcançado por meio de:

  • Ocultação de dados — os campos da classe são declarados como private (ou pelo menos não public).
  • Interface pública — “expor” apenas os métodos que realmente são necessários ao usuário do objeto.

Esquema: como é o encapsulamento

+-------------------------------+
|         Klass Student         |
|-------------------------------|
| - name: String                |  // campo private
| - age: int                    |  // campo private
|-------------------------------|
| + getName(): String           |  // método public
| + setName(String): void       |  // método public
| + getAge(): int               |  // método public
| + setAge(int): void           |  // método public
+-------------------------------+

Aqui o sinal - significa private (oculto), e o +public (acessível externamente).

O que são getters e setters?

Antes de entender por que o encapsulamento é necessário, vamos nos familiarizar rapidamente com getters e setters — métodos especiais que nos ajudam a “conversar” com os campos privados da classe.

Getter — método que obtém o valor de um campo privado. Geralmente é chamado de getNomeDoCampo().

Setter — método que define o valor de um campo privado. Geralmente é chamado de setNomeDoCampo(valor).

Exemplo simples:

public class Student {
    private String name; // campo privado — não é visível externamente
    
    // Getter — "me dê o nome do estudante"
    public String getName() {
        return name;
    }
    
    // Setter — "defina o nome do estudante"
    public void setName(String name) {
        this.name = name;
    }
}

Como funciona:

Student student = new Student();
student.setName("Vasya");           // definimos o nome via setter
String name = student.getName();   // obtemos o nome via getter

Pense em getters e setters como “pedidos educados” ao objeto: em vez de mexer diretamente nos bolsos dele (student.name = "Vasya"), pedimos educadamente: “Por favor, defina o nome” (student.setName("Vasya")).

Adiantando um pouco: em algumas aulas veremos getters e setters em detalhes, aprenderemos seus segredos e a usá-los ao máximo. Por ora, basta entender a ideia principal!

2. Por que o encapsulamento é necessário?

Proteção dos dados contra uso incorreto

Se todos os campos da classe fossem públicos (public), qualquer código externo poderia alterá-los diretamente de qualquer maneira:

Student s = new Student();
s.age = -1000; // Ops, estudante vampiro!

Isso é perigoso! Seu programa pode começar a se comportar de forma imprevisível, e bugs aparecerão nos lugares mais inesperados.

Possibilidade de alterar a implementação interna sem afetar o código externo

O encapsulamento permite alterar a estrutura interna da classe sem quebrar o código que a utiliza. Por exemplo, você pode mudar a forma de armazenar dados ou adicionar validação nos métodos, e os usuários da classe não perceberão — eles continuarão chamando os mesmos métodos.

Melhora na legibilidade e manutenção do código

Quando todos os detalhes internos são ocultos, a interface externa fica mais limpa e compreensível. O programador que usa sua classe não precisa entender como ela funciona por dentro — basta saber quais métodos estão disponíveis e o que eles fazem.

Exemplo do dia a dia

Lembre-se de como você usa um smartphone. Você não pensa exatamente como os toques na tela são processados, como é a bateria ou como funciona o módulo de comunicação. Você apenas chama as funções necessárias por meio de uma interface clara (ícones, botões). Se o fabricante mudar a implementação interna, você nem vai perceber.

Exemplo real em código

Imagine que temos a classe BankAccount. Na versão antiga do programa, o saldo era armazenado como uma string com pontos separadores, por exemplo "1.000.50". Depois os programadores decidiram armazenar o saldo como um número double. Se o campo fosse público, todo o código antigo que acessava diretamente account.balance quebraria.

Mas se usarmos encapsulamento e ocultarmos o campo, fornecendo apenas os métodos deposit() e getBalance(), o código externo nem ficará sabendo das mudanças:

public class BankAccount {
    private double balance; // campo oculto

    public void deposit(double amount) {
        if (amount > 0) {
            balance += amount;
        }
    }

    public double getBalance() {
        return balance;
    }
}

Agora, se amanhã quisermos armazenar o saldo, por exemplo, em centavos (long), basta alterar a implementação interna da classe, e todo o restante do código que chama deposit() e getBalance() continuará funcionando como antes.

3. Exemplos de encapsulamento ruim e bom

Exemplo ruim: campos públicos

public class Student {
    public String name;
    public int age;
}

Problemas dessa abordagem:

  • Qualquer código pode atribuir aos campos quaisquer valores, até mesmo incorretos.
  • Não há como adicionar validação dos dados.
  • Se você decidir alterar o tipo ou a estrutura do campo, terá que mudar todo o código que o utiliza.

Exemplo bom: campos privados e métodos públicos

public class Student {
    private String name;
    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        // É possível adicionar validação!
        if (name == null || name.isEmpty()) {
            throw new IllegalArgumentException("O nome não pode ser vazio");
        }
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        // Verificamos que a idade não é negativa
        if (age < 0) {
            throw new IllegalArgumentException("A idade não pode ser negativa");
        }
        this.age = age;
    }
}

Vantagens:

  • O código externo não pode alterar os campos diretamente — apenas por meio de métodos.
  • É possível adicionar validações, logs e ações automáticas (por exemplo, atualização de estatísticas).
  • Se a representação interna mudar (por exemplo, a idade passar a ser armazenada em outro formato), a interface externa permanecerá a mesma.

Como fica ao usar

Student s = new Student();
s.setName("Alisa");
s.setAge(20);

System.out.println(s.getName() + ", idade: " + s.getAge());

Tente atribuir uma idade negativa — você receberá um erro já em tempo de execução! Seu programa fica protegido contra bobagens.

4. Relação com outros princípios de POO

O encapsulamento é a “mãe” de todos os outros princípios da POO. Sem ele, não haveria herança, polimorfismo nem abstração. Vamos estudá-los mais adiante, mas por enquanto, mencionemos brevemente:

  • Herança (extends) permite criar novas classes com base em classes existentes, ampliando ou alterando seu comportamento. Se o interior da classe estivesse aberto, a classe filha poderia, sem querer, quebrar algo importante.
  • Polimorfismo (capacidade de objetos de diferentes classes reagirem de maneiras distintas às mesmas mensagens) é impossível sem uma separação clara entre implementação interna e interface externa.
  • Abstração — é destacar apenas as características essenciais do objeto e ocultar detalhes. O encapsulamento ajuda a colocar a abstração em prática.

Analogia

Imagine um carro. Ao motorista estão disponíveis apenas volante, pedais e alavancas — essa é a interface. Todo o resto (motor, câmbio, eletrônica) — fica sob o capô. Se o motorista pudesse controlar diretamente cada parafuso do motor, os acidentes seriam muito mais frequentes!

5. Exemplo prático: encapsulamento no nosso aplicativo

Vamos continuar evoluindo nosso aplicativo de estudo — por exemplo, o “Livro de endereços”. Suponha que temos a classe Contact, que armazena nome e telefone.

Sem encapsulamento (anti-exemplo):

public class Contact {
    public String name;
    public String phone;
}

Uso:

Contact contact = new Contact();
contact.name = ""; // Opa! Nome vazio
contact.phone = null; // Telefone não definido

Com encapsulamento (abordagem correta):

public class Contact {
    private String name;
    private String phone;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        if (name == null || name.isBlank()) {
            throw new IllegalArgumentException("O nome do contato não pode ser vazio");
        }
        this.name = name;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        if (phone == null || phone.isBlank()) {
            throw new IllegalArgumentException("O telefone não pode ser vazio");
        }
        this.phone = phone;
    }
}

Agora o código externo não conseguirá deixar nome ou telefone vazios:

Contact contact = new Contact();
contact.setName("Ivan");
contact.setPhone("+1-999-123-45-67");

Se tentar definir um nome vazio, o programa lançará um erro.

6. Detalhes úteis

Encapsulamento e manutenção de longo prazo do código

Quando você trabalha em um pequeno projeto de estudo, pode parecer que dá para fazer tudo “na confiança”: quem vai atribuir idade negativa ou nome vazio? Mas assim que o projeto cresce, outros desenvolvedores aparecem e até você mesmo esquece os detalhes da implementação após alguns meses — é aí que o encapsulamento salva do caos.

  • É fácil mudar o interior da classe — se for preciso armazenar o telefone como um objeto do tipo PhoneNumber, e não como string, você apenas altera a implementação e não mexe no código externo.
  • Fica mais fácil testar — se todas as mudanças acontecem apenas por meio de métodos, é simples rastrear quais dados mudam e quando.
  • Menos bugs — proteção contra valores incorretos e alterações acidentais.

Pergunta: getters e setters são sempre necessários?

Muitas vezes iniciantes pensam: “Se encapsulamento é ter campos privados e getters/setters públicos, então é preciso criar getter e setter para cada campo!”. Não é bem assim.

  • Às vezes o campo deve ser somente leitura (por exemplo, um identificador único do objeto). Nesse caso, faça apenas o getter.
  • Às vezes o campo nem precisa ser “exposto” — então não faça nem getter nem setter.
  • O setter pode ser privado se o valor do campo só puder mudar dentro da própria classe.

Regra de ouro: exponha apenas os dados e métodos que realmente são necessários ao código externo.

Visualização: comparação de abordagens

Abordagem Exemplo de acesso ao campo Possibilidade de controle Segurança
campos public
obj.field = value;
Não Baixa
campos private + métodos
obj.setField(value);
Sim Alta

7. Erros comuns ao trabalhar com encapsulamento

Erro № 1: Todos os campos da classe declarados como public. Este é o erro mais comum entre iniciantes. Esse tipo de código rapidamente se torna incontrolável: qualquer um pode alterar quaisquer dados sem você saber. Não faça isso — mesmo que você esteja com muita vontade de economizar tempo!

Erro № 2: Getters e setters sem validação e lógica. Se você cria métodos de acesso, use-os para validação: não permita atribuir valores incorretos. Simplesmente “copiar” o valor do parâmetro para o campo nem sempre é a melhor opção.

Erro № 3: Exposição prematura da estrutura interna. Se você cria getters/setters para todos os campos “por via das dúvidas”, corre o risco de expor detalhes demais, que depois serão difíceis de mudar.

Erro № 4: Retornar objetos mutáveis diretamente. Se o campo é um objeto mutável (por exemplo, uma lista), não o retorne diretamente pelo getter. É melhor retornar uma cópia ou torná-lo imutável.

1
Tarefa
JAVA 25 SELF, nível 15, lição 0
Bloqueado
Dados confidenciais dos membros do clube 🕵️‍♀️
Dados confidenciais dos membros do clube 🕵️‍♀️
1
Tarefa
JAVA 25 SELF, nível 15, lição 0
Bloqueado
Apresentando os membros do clube 🤝
Apresentando os membros do clube 🤝
1
Tarefa
JAVA 25 SELF, nível 15, lição 0
Bloqueado
Validação de dados e proteção contra erros 🚫
Validação de dados e proteção contra erros 🚫
1
Tarefa
JAVA 25 SELF, nível 15, lição 0
Bloqueado
Etiquetas imutáveis de produtos no estoque 🏷️
Etiquetas imutáveis de produtos no estoque 🏷️
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION