1. Introdução
Em Java, os modificadores de acesso são como um sistema de fechaduras em uma casa. Eles determinam quem e de onde pode “entrar” no seu quarto (ou no campo/método da sua classe). Se todas as portas estiverem abertas — qualquer um pode chegar e mudar algo. Se tudo estiver trancado — ninguém vai conseguir quebrar nada, mas você mesmo pode acabar trancado em algum momento.
Lembrando: em Java há quatro níveis principais de acesso:
| Modificador | Disponível dentro da classe | Disponível no pacote | Disponível em subclasses | Disponível em outros pacotes |
|---|---|---|---|---|
|
✔ | |||
| (package) | ✔ | ✔ | ||
|
✔ | ✔ | ✔ | (via herança) |
|
✔ | ✔ | ✔ | ✔ |
(package) — é quando o modificador não é especificado explicitamente. Esse membro da classe é visível apenas dentro de um único pacote.
2. Erros comuns com modificadores de acesso
Erro 1: Campos e métodos com modificador padrão (package-private)
O erro mais comum dos iniciantes é esquecer de especificar o modificador de acesso. Como resultado, o campo ou método fica acessível em todo o pacote, embora isso não fosse sua intenção. Isso pode levar a que outra classe (no mesmo pacote, mas não relacionada à sua) consiga alterar o estado interno do seu objeto.
// Erro: o campo name não está protegido!
class User {
String name; // package-private!
}
Como resultado, qualquer classe desse pacote pode escrever:
User user = new User();
user.name = "Vasya"; // sem restrições!
Erro 2: Violação de encapsulamento — campos públicos (public)
O segundo erro mais comum é declarar os campos da classe como public. Isso é conveniente quando você está apenas aprendendo ou escrevendo um exemplo curto, mas em projetos reais quase sempre é ruim. Você perde o controle sobre quem e como altera seus dados.
public class Account {
public double balance; // PERIGOSO!
}
Agora qualquer código pode fazer:
Account acc = new Account();
acc.balance = -1000000; // E de quem é a culpa agora?
Erro 3: Ausência de getters e setters
Às vezes o desenvolvedor torna os campos private, mas esquece de adicionar métodos para administrá-los. Como resultado, obter ou alterar o valor torna-se impossível mesmo onde seria apropriado.
public class Product {
private String name;
// Não há getName() nem setName()
}
Erro 4: Tentar acessar membros private de outra classe
Se você declarou um campo ou método como private, não é possível acessá-lo de outra classe, mesmo que esteja no mesmo pacote. Iniciantes muitas vezes se surpreendem com o motivo de o campo “não ser visível”.
public class User {
private String password;
}
public class UserService {
public void resetPassword(User user) {
// user.password = "123"; // Erro de compilação!
}
}
Erro 5: Erros com protected
Muitos acham que protected significa “visível em qualquer lugar onde haja herança”. Mas em Java o acesso a membros protected fora do pacote é possível apenas por herança e somente para a subclasse. É uma sutileza fácil de esquecer.
package animals;
public class Animal {
protected void sleep() {}
}
package zoo;
import animals.Animal;
public class Dog extends Animal {
public void test() {
sleep(); // OK — subclasse
}
}
public class NotADog {
public void test() {
Animal a = new Animal();
// a.sleep(); // Erro: não é subclasse!
}
}
3. Como fazer certo: boas práticas
Regra 1: Por padrão, torne os campos private
Este é o principal princípio do encapsulamento. Os campos devem ficar ocultos para todos, exceto para a própria classe. Se você precisa dar acesso, use getters/setters.
public class Book {
private String title;
private int pages;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}
Regra 2: Exponha apenas os métodos necessários
Se um método deve ser acessível externamente — torne-o public. Se for necessário apenas dentro do pacote — deixe package-private. Se o método for destinado apenas a herdeiros — use protected.
Regra 3: Minimize o escopo de visibilidade
Quanto menor a visibilidade, menor a chance de erros acidentais e de “convidados inesperados”. Não torne métodos e campos public se isso não for necessário.
Regra 4: Use getters e setters para controlar o acesso
Isso permite adicionar lógica extra ao ler/gravar o campo, por exemplo, validação.
public class Account {
private double balance;
public void setBalance(double balance) {
if (balance < 0) {
throw new IllegalArgumentException("O saldo não pode ser negativo!");
}
this.balance = balance;
}
public double getBalance() {
return balance;
}
}
Regra 5: Não exponha a implementação interna
Se você tem um array ou lista como campo, não o retorne diretamente pelo getter — retorne uma cópia ou forneça apenas os métodos necessários.
public class Team {
private List<String> members = new ArrayList<>();
// Correto:
public List<String> getMembers() {
return new ArrayList<>(members); // retornamos uma cópia
}
}
4. Exemplos na prática
Suponha que temos a classe LibraryUser, que descreve um usuário de biblioteca.
Exemplo de implementação incorreta
public class LibraryUser {
public String name;
public int borrowedBooks;
}
Nesse estado, qualquer código pode fazer o que quiser com o objeto:
LibraryUser user = new LibraryUser();
user.name = null;
user.borrowedBooks = -10; // Lógica? Que lógica?
Exemplo de implementação correta com encapsulamento
public class LibraryUser {
private String name;
private int borrowedBooks;
public LibraryUser(String name) {
this.name = name;
this.borrowedBooks = 0;
}
public String getName() {
return name;
}
public int getBorrowedBooks() {
return borrowedBooks;
}
public void borrowBook() {
borrowedBooks++;
}
public void returnBook() {
if (borrowedBooks > 0) {
borrowedBooks--;
}
}
}
Agora o código externo não pode alterar diretamente a quantidade de livros emprestados nem o nome do usuário. Tudo é controlado apenas pelos métodos da classe.
5. Particularidades e nuances de implementação
Às vezes parece mais fácil tornar um campo public do que escrever um monte de getters e setters. Mas isso é uma armadilha! Um campo aberto é como a porta do apartamento aberta: sim, é conveniente, mas não é muito seguro.
Outra sutileza: nem sempre é preciso criar getters e setters para todos os campos. Se o valor do campo não deve mudar após a criação do objeto, faça apenas o getter e torne o campo final:
public class Passport {
private final String number;
public Passport(String number) {
this.number = number;
}
public String getNumber() {
return number;
}
}
Também lembre-se: se a classe é declarada como public, o nome do arquivo deve coincidir com o nome da classe! Isso não é exatamente sobre modificadores de acesso, mas é um erro muito comum entre iniciantes.
6. Erros típicos ao trabalhar com modificadores de acesso
Erro nº 1: esquecer de especificar o modificador de acesso de um campo ou método. Como resultado, o campo ou método fica acessível em todo o pacote, mesmo que você não quisesse. Sempre especifique o modificador explicitamente, mesmo que a IDE não acuse erro.
Erro nº 2: todos os campos declarados como public. Isso mata o encapsulamento, torna seu código vulnerável e imprevisível. O hábito de “para simplificar” em exemplos não deve chegar ao código de produção.
Erro nº 3: tentar acessar um campo private de outra classe. O Java não permitirá — o compilador irá protegê-lo — mas se você, por acaso, quiser “contornar” isso por meio de reflexão, reflita sobre por que essa necessidade surgiu.
Erro nº 4: esperar que membros protected estejam acessíveis em qualquer lugar onde haja herança. Na verdade, fora do pacote, só é possível acessá-los a partir da subclasse e somente via this ou através de um objeto da subclasse.
Erro nº 5: retornar a coleção interna via getter. Se você retornar a referência do array ou lista internos, o código externo poderá modificá-los, violando os invariantes da classe.
Erro nº 6: falta de controle ao definir valores via setter. Se o valor recebido não for validado, o objeto pode entrar em um estado inválido (por exemplo, saldo negativo).
Erro nº 7: escopo de visibilidade muito amplo para métodos. Às vezes, métodos são tornados public embora sejam necessários apenas dentro do pacote ou da classe. Isso expõe uma API desnecessária e dificulta a manutenção.
GO TO FULL VERSION