Java Modificadores de Acesso
Até aqui, qualquer parte do programa podia ler e mudar qualquer atributo: conta.saldo = -1000000; compilava sem reclamar. Os modificadores de acesso são palavras colocadas antes de classes, atributos, métodos e construtores para dizer quem pode usá-los. São quatro níveis: public, protected, o padrão (sem palavra nenhuma) e private.
- O que cada um dos quatro níveis permite, com uma tabela de visibilidade.
- Como
privateprotege um atributo e o erro realhas private access. - Métodos
privatecomo "ajudantes internos" da classe. - O acesso padrão (de pacote) e o erro
is not public … cannot be accessed from outside package. - Quais modificadores valem para classes e quais são os modificadores que não são de acesso (
static,final,abstract).
A ideia antes do código: as portas da escola
A escola da Ana tem lugares com regras de entrada diferentes:
| Lugar | Quem entra | Modificador |
|---|---|---|
| O pátio e a recepção | Qualquer pessoa, de qualquer lugar | public ("público") |
| A sala dos professores | Quem trabalha naquele prédio, e também professores de outras unidades que são "da família" da escola | protected ("protegido") |
| O corredor interno | Só quem é do mesmo prédio | padrão, sem palavra ("de pacote") |
| O cofre da diretoria | Só a diretoria | private ("privado") |
Em Java, o "prédio" é o pacote (uma pasta de classes; veja Pacotes e import), a "família" são as subclasses (veja Herança) e a "diretoria" é a própria classe.
A tabela de visibilidade
Esta é a tabela que todo programador Java consulta. Cada linha é um modificador; cada coluna, um lugar de onde alguém tenta usar o membro (atributo, método ou construtor):
| Modificador | Na própria classe | Mesmo pacote | Subclasse em outro pacote | Qualquer lugar |
|---|---|---|---|---|
public | sim | sim | sim | sim |
protected | sim | sim | sim | não |
| (nenhum) — padrão / de pacote | sim | sim | não | não |
private | sim | não | não | não |
Lida de cima para baixo, a tabela vai do mais aberto ao mais fechado. Na prática, o dia a dia usa quase só os extremos: private para atributos e public para os métodos que formam a "porta de entrada" da classe. O padrão e o protected aparecem em situações específicas.
Nas aulas anteriores, os atributos e métodos não tinham modificador: eram de acesso padrão. Como todos os exemplos ficavam na mesma pasta, sem declaração de pacote, estavam no mesmo "pacote sem nome" e se enxergavam. Por isso nunca houve erro de acesso.
private: só a própria classe
A Ana marcou o saldo da conta como private. Agora, quem está fora da classe Conta não pode tocar nele:
class Conta {
String titular;
private double saldo;
}
public class Main {
public static void main(String[] args) {
Conta c = new Conta();
c.saldo = 1000000;
System.out.println(c.saldo);
}
}
$ javac Main.java
Main.java:9: error: saldo has private access in Conta
c.saldo = 1000000;
^
Main.java:10: error: saldo has private access in Conta
System.out.println(c.saldo);
^
2 errors
"saldo tem acesso privado em Conta". Nem escrever nem ler: o main está em outra classe. Então como alguém deposita ou consulta? Pelos métodos da própria classe, que podem usar o atributo privado à vontade:
class Conta {
String titular;
private double saldo;
void depositar(double valor) {
if (valor > 0) {
saldo = saldo + valor;
}
}
double consultarSaldo() {
return saldo;
}
}
public class Main {
public static void main(String[] args) {
Conta c = new Conta();
c.titular = "Ana";
c.depositar(500);
c.depositar(-1000);
System.out.println(c.titular + ": " + c.consultarSaldo());
}
}
$ javac Main.java && java Main Ana: 500.0 « o depósito negativo foi ignorado pela regra do método
- private double saldo;
- O modificador vem antes do tipo. Só o código escrito dentro das chaves de
class Contapode usarsaldo. - void depositar(double valor)
- Está dentro da classe: pode mexer em
saldo. E impõe uma regra: só aceita valores positivos. - double consultarSaldo()
- Deixa ler o saldo sem deixar mudá-lo.
- c.depositar(-1000);
- Quem está fora não consegue estragar o saldo: o único caminho passa pela regra.
Esse é o coração do encapsulamento, o primeiro pilar da POO: os dados ficam escondidos e só mudam por métodos que conferem o que está acontecendo. A aula Encapsulamento mostra o padrão completo, com getters e setters.
Missão (provoque o erro): torne o atributo estoque da classe Fruta privado e veja o javac recusar a linha do main que tenta mudá-lo. A missão está cumprida quando aparecer has private access.
class Fruta {
String nome;
int estoque;
}
public class Main {
public static void main(String[] args) {
Fruta f = new Fruta();
f.nome = "maçã";
f.estoque = -50;
System.out.println(f.nome + ": " + f.estoque);
}
}
class Fruta {
String nome;
private int estoque;
}
public class Main {
public static void main(String[] args) {
Fruta f = new Fruta();
f.nome = "maçã";
f.estoque = -50;
System.out.println(f.nome + ": " + f.estoque);
}
}
Métodos private: ajudantes internos
Métodos também podem ser privados. É útil para pedaços de lógica que só fazem sentido dentro da classe, como uma conferência usada por outros métodos:
class Conta {
private double saldo;
private double limite = 200;
boolean sacar(double valor) {
if (!podeSacar(valor)) {
System.out.println("Saque de " + valor + " recusado.");
return false;
}
saldo = saldo - valor;
System.out.println("Saque de " + valor + " feito. Saldo: " + saldo);
return true;
}
private boolean podeSacar(double valor) {
return valor > 0 && valor <= saldo + limite;
}
}
public class Main {
public static void main(String[] args) {
Conta c = new Conta();
c.sacar(150);
c.sacar(100);
}
}
$ javac Main.java && java Main Saque de 150.0 feito. Saldo: -150.0 « usou o limite (cheque especial) Saque de 100.0 recusado. « passaria de 200 negativos
Se o main tentar chamar o ajudante:
$ javac Main.java
Main.java:25: error: podeSacar(double) has private access in Conta
c.podeSacar(10);
^
1 error
Vantagem: a Ana pode mudar ou apagar podeSacar amanhã sem quebrar nenhuma outra classe, porque ninguém de fora depende dele.
Padrão (de pacote) e public
A diferença entre o acesso padrão e o public só aparece quando há pacotes diferentes. Um pacote é uma pasta de classes com uma declaração package no topo de cada arquivo; você vai estudá-los em Pacotes e import. Para ver os modificadores funcionando, a Ana separou o programa em duas pastas:
modif/
├── banco/
│ └── Conta.java (package banco;)
└── app/
└── Main.java (package app;)
package banco;
public class Conta {
public String titular;
double saldo;
private String senha = "1234";
public Conta(String titular) {
this.titular = titular;
}
public void depositar(double valor) {
saldo = saldo + valor;
}
public double consultarSaldo() {
return saldo;
}
}
package app;
import banco.Conta;
public class Main {
public static void main(String[] args) {
Conta c = new Conta("Ana");
c.depositar(500);
System.out.println(c.titular + ": " + c.consultarSaldo());
System.out.println(c.saldo);
System.out.println(c.senha);
}
}
$ javac -d out banco/Conta.java app/Main.java
app/Main.java:10: error: saldo is not public in Conta; cannot be accessed from outside package
System.out.println(c.saldo);
^
app/Main.java:11: error: senha has private access in Conta
System.out.println(c.senha);
^
2 errors
Membro de Conta | Modificador | O Main (pacote app) pode usar? |
|---|---|---|
titular | public | Sim |
construtor, depositar, consultarSaldo | public | Sim |
saldo | padrão | Não: is not public in Conta; cannot be accessed from outside package |
senha | private | Não: has private access in Conta |
Tirando as duas linhas proibidas, o programa compila e roda. A opção -d out manda o javac colocar os .class na pasta out, organizados por pacote, e -cp out diz ao java onde procurá-los:
$ javac -d out banco/Conta.java app/Main.java $ java -cp out app.Main Ana: 500.0
Repare que a classe Conta e o seu construtor também precisaram de public: sem isso, o pacote app não conseguiria nem enxergar a classe nem criar contas.
E o protected?
protected é o padrão "com um extra": vale para o mesmo pacote e para as subclasses, mesmo em outro pacote. Ele só faz sentido com herança, então fica para a aula Herança (e super). Por enquanto, guarde a posição dele na tabela: entre o padrão e o public.
Modificadores em classes
Uma classe "de primeiro nível" (que não está dentro de outra) só aceita duas opções:
| Declaração | Quem enxerga a classe | Regra |
|---|---|---|
public class Conta | Todo mundo | Tem de estar no arquivo Conta.java; só uma por arquivo |
class Conta | Só o mesmo pacote | Pode dividir o arquivo com outras classes |
private e protected não fazem sentido para uma classe de primeiro nível (privada de quem?):
$ javac Main.java
Main.java:1: error: modifier private not allowed here
private class Conta {
^
1 error
Classes declaradas dentro de outras podem ser privadas; isso aparece em Classes Internas e Anônimas.
Modificadores que não são de acesso
Existem outras palavras que aparecem no mesmo lugar, antes do tipo, mas que não falam de quem acessa, e sim de como a coisa funciona. Cada uma tem a sua aula:
| Modificador | Em uma frase | Aula |
|---|---|---|
static | Pertence à classe, não a cada objeto | static |
final | Não pode mudar: variável que não recebe outro valor, método que não pode ser sobrescrito, classe que não pode ser herdada | final |
abstract | Incompleto de propósito: classe que não pode ser instanciada, método sem corpo | Classes Abstratas |
synchronized | Só uma thread por vez executa o método | synchronized e Locks |
Um membro pode ter um modificador de acesso e vários outros: public static void main, private static final double TAXA = 0.05;. A ordem não muda o significado, mas a convenção é: acesso primeiro, depois static, depois final. Só não dá para usar dois modificadores de acesso juntos:
$ javac Main.java
Main.java:2: error: illegal combination of modifiers: public and private
public private double saldo;
^
1 error
Qual usar? (regras práticas)
| O quê | Use | Por quê |
|---|---|---|
| Atributos | private | Ninguém de fora estraga o estado; mudanças passam por métodos |
| Métodos que outras classes devem usar | public | São a "porta de entrada" da classe |
| Métodos auxiliares, internos | private | Liberdade para mudá-los depois |
| Construtores | public (em geral) | Para outras classes criarem objetos |
| Constantes compartilhadas | public static final | Não mudam, então não há risco em expor (veja Constantes) |
| Na dúvida | O mais restrito que funcione | Abrir depois é fácil; fechar algo que outros já usam quebra o código deles |
Quando dá errado
$ javac Main.java
Main.java:9: error: saldo has private access in Conta
c.saldo = 1000000;
^
O que aconteceu: o código está fora da classe Conta e tentou usar um membro private dela (atributo, método ou construtor).
Como resolver: não tire o private por reflexo. Use (ou crie) um método público da classe que faça o que você precisa: c.depositar(…), c.consultarSaldo(). Veja Encapsulamento.
$ javac -d out banco/Conta.java app/Main.java
app/Main.java:10: error: saldo is not public in Conta; cannot be accessed from outside package
System.out.println(c.saldo);
^
O que aconteceu: o membro tem acesso padrão (nenhum modificador) e o código que tenta usá-lo está em outro pacote. A mesma mensagem aparece para classes e construtores sem public.
Como resolver: se o membro deve mesmo ser usado de fora, marque-o public (ou ofereça um método public que o use). Se não deve, o erro está certo: reorganize o código.
O que aconteceu: private ou protected numa classe de primeiro nível.
Como resolver: tire o modificador; para classes, use public ou nada. Atenção a um primo deste erro: variáveis locais (dentro de um método) não aceitam modificador de acesso, e private int x = 0; no meio do main produz uma cascata de mensagens que começa com error: illegal start of expression. Tire o private: o escopo do método já esconde a variável.
O que aconteceu: dois modificadores de acesso no mesmo membro (public private), ou outra combinação proibida (como abstract final).
Como resolver: escolha um só nível de acesso.
Referência
| Modificador | Visível em | Uso típico |
|---|---|---|
public | Todo lugar | Classes, construtores e métodos da "porta de entrada" |
protected | Mesmo pacote + subclasses | Membros pensados para quem herda |
| (nenhum) | Mesmo pacote | Classes e métodos de uso interno de um pacote |
private | Só a própria classe | Atributos e métodos auxiliares |
Erro has private access | Uso de membro private fora da classe | |
Erro is not public in …; cannot be accessed from outside package | Uso de membro padrão fora do pacote | |
Missão final: o termômetro protegido
Missão: torne o atributo graus privado e crie um método public void ajustar(double novo) que só aceite valores a partir de -273.15 (o zero absoluto); para valores menores, imprima Valor recusado: seguido do valor. O main já usa ajustar e mostrar.
class Termometro {
double graus;
public void mostrar() {
System.out.println("Temperatura: " + graus);
}
}
public class Main {
public static void main(String[] args) {
Termometro t = new Termometro();
t.ajustar(22.5);
t.ajustar(-300);
t.mostrar();
}
}
class Termometro {
private double graus;
public void ajustar(double novo) {
if (novo < -273.15) {
System.out.println("Valor recusado: " + novo);
return;
}
graus = novo;
}
public void mostrar() {
System.out.println("Temperatura: " + graus);
}
}
public class Main {
public static void main(String[] args) {
Termometro t = new Termometro();
t.ajustar(22.5);
t.ajustar(-300);
t.mostrar();
}
}
Exercícios
1. O mais fechado
Qual modificador deixa um atributo visível só dentro da própria classe?
2. Declarar protegido
Complete a declaração de um atributo double chamado saldo, privado:
3. O mais aberto
Qual modificador deixa um método visível em qualquer lugar do programa?
4. Um atributo sem modificador nenhum pode ser usado por…
5. A classe Conta tem private double saldo e double consultarSaldo() { return saldo; }. O main (em outra classe do mesmo pacote) pode fazer c.consultarSaldo()?
6. Qual destas declarações de classe de primeiro nível é válida?
Resumo
- Modificadores de acesso dizem quem pode usar uma classe, atributo, método ou construtor.
public: todos.protected: pacote + subclasses. Padrão (nada): só o pacote.private: só a própria classe.- Regra prática: atributos
private, métodos da "porta de entrada"public, ajudantesprivate. - Erros:
has private access(membro privado usado de fora) eis not public in …; cannot be accessed from outside package(membro padrão usado de outro pacote). - Classes de primeiro nível: só
publicou nada. static,finaleabstracttambém são modificadores, mas não de acesso; cada um tem a sua aula.