1. Introdução
Em Java, como na vida, nem tudo deve estar disponível para todos o tempo todo. Imagine um apartamento: você não quer que os vizinhos circulem pelo seu quarto, certo? Do mesmo modo, em programas às vezes queremos “fechar a porta” para certas variáveis ou métodos, para que não possam ser acessados de fora.
Para isso, em Java existem os modificadores de acesso — palavras especiais que indicam onde uma variável ou método pode ser usado: por exemplo, public ou private.
Principais modificadores de acesso
| Modificador | Onde é visível? |
|---|---|
|
Em qualquer lugar onde a classe for visível (em outros pacotes e em outros arquivos) |
|
Somente dentro da própria classe |
|
Apenas dentro do mesmo pacote (ou seja, dentro de classes no mesmo diretório) |
Existem outros modificadores, mas falaremos deles mais tarde, quando chegarmos à herança.
Exemplo na prática
public class User
{
public String name; // visível para todos
private int age; // visível apenas dentro da classe User
public void sayHello()
{
System.out.println("Olá, meu nome é " + name);
}
private void secretMethod()
{
System.out.println("Este é um método secreto!");
}
}
Explicação:
- name — é visível em qualquer lugar onde a classe User puder ser usada.
- age — é visível apenas dentro da própria classe User.
- sayHello() — método público; pode ser chamado a partir de qualquer outra classe.
- secretMethod() — método privado; não pode ser chamado de fora.
Como isso funciona em tarefas reais?
Suponha que tenhamos uma classe que descreve uma conta bancária. Não queremos que qualquer pessoa possa alterar o saldo diretamente! Por isso, a variável de saldo será private, e haverá métodos específicos para trabalhar com ela.
Por que não se deve tornar tudo public?
Pode surgir a tentação: “Vamos tornar tudo public para não ter trabalho!” Mas esse é o caminho para o caos. Imagine que qualquer um possa alterar seu saldo, o nome do usuário ou até chamar um método que deveria ser apenas para uso interno. Em programas grandes, isso leva a erros e bugs “mágicos”.
Regra de ouro para o desenvolvedor Java:
Comece tornando tudo private e só exponha o que realmente for necessário.
2. Escopo de variáveis
Escopo é a região do código em que a variável “existe” e pode ser usada. Ao sair dessa região, a variável “desaparece” — como se ela nunca tivesse existido.
Em Java há vários tipos de variáveis quanto ao escopo:
- Variáveis locais — declaradas dentro de um método ou bloco de código { ... }.
- Parâmetros de método — declarados entre parênteses do método.
- Campos da classe — declarados dentro da classe, mas fora dos métodos.
Se uma variável for declarada dentro de { bloco },
ela será visível apenas dentro desse bloco
e ficará indisponível fora dele.
Variáveis locais
Uma variável local vive apenas dentro do método ou bloco em que foi declarada.
void printSum(int a, int b)
{
int sum = a + b; // variável local
System.out.println(sum);
}
// aqui, sum já não existe!
Tentar acessar sum fora do método causará um erro de compilação: “Não é possível encontrar a variável sum”.
Parâmetros de método
Parâmetros também são variáveis, mas vivem apenas dentro do método.
void greet(String name)
{
System.out.println("Olá, " + name);
}
// aqui, name já não existe!
Campos da classe (membros)
Campos da classe são declarados dentro da classe, mas fora dos métodos. Eles são visíveis em todos os métodos dessa classe.
public class Counter
{
private int count = 0; // campo da classe
public void increment()
{
count++; // podemos usar o campo
}
public int getCount()
{
return count; // também podemos usar o campo
}
}
3. Sombreamento de variáveis (shadowing)
Sombreamento (shadowing) é a situação em que, em um escopo, é declarada uma variável (ou parâmetro) com o mesmo nome de outra em um escopo externo. Dentro desse bloco, o “novo” nome sombreia o antigo, e não é possível acessar diretamente o valor externo.
Exemplo de sombreamento:
class ShadowDemo
{
int value = 10; // campo da classe
void printValue()
{
System.out.println(value); // 10 — imprime o campo da classe
int value = 5; // a variável local sombreia o campo da classe
System.out.println(value); // imprime 5, não 10
}
}
Neste exemplo, quando escrevemos int value = 5;, declaramos uma nova variável local que tem prioridade sobre o campo da classe com o mesmo nome. Ao acessar value dentro do método printValue(), será usada a variável local, e não o campo da classe.
Se ainda assim você precisar acessar um campo estático da classe, use o nome da classe como prefixo:
class ShadowDemo
{
static int value = 10; // campo estático da classe
void printValue()
{
System.out.println(value); // 10 — campo da classe
int value = 5;
System.out.println(value); // 5 — variável local
System.out.println(ShadowDemo.value); // 10 — campo estático da classe, acesso via 'ShadowDemo'
}
}
Se precisar acessar um campo não estático da classe, use a palavra-chave this. Ela aponta para a instância atual do objeto.
class ShadowDemo
{
int value = 10;
void printValue()
{
System.out.println(value); // 10 — campo da classe
int value = 5;
System.out.println(value); // 5 — variável local
System.out.println(this.value); // 10 — campo da classe, acesso via 'this'
}
}
4. Prática: modificadores de acesso e escopo
Vamos continuar evoluindo nosso aplicativo didático: um sistema simples de cadastro de alunos. Faremos campos e métodos com diferentes modificadores de acesso.
Exemplo: classe Student
public class Student
{
public String name; // nome do aluno (visível para todos)
private int age; // idade (visível apenas dentro da classe)
public Student(String name, int age)
{
this.name = name;
this.age = age;
}
public void sayHello()
{
System.out.println("Olá, meu nome é " + name);
}
private void printSecret()
{
System.out.println("Minha idade: " + age);
}
public void revealSecret()
{
printSecret(); // é possível chamar o método privado dentro da classe
}
}
Uso da classe Student
public class Main
{
public static void main(String[] args)
{
Student s = new Student("Vasya", 20);
s.sayHello(); // OK: método público
s.revealSecret(); // OK: método público que chama o privado internamente
s.age = 30; // Erro! O campo age é privado
s.printSecret(); // Erro! O método printSecret é privado
}
}
5. Erros comuns ao trabalhar com modificadores de acesso e escopo
Erro nº 1: Tudo public — e olá, caos!
Se você tornar todos os campos e métodos públicos, corre o risco de perder o controle sobre quem e como altera seus dados. Torne os campos private e exponha apenas o necessário.
Erro nº 2: Tentar usar uma variável fora do seu escopo.
Por exemplo, você declarou uma variável dentro de um método e depois tenta acessá-la de fora — ocorrerá um erro de compilação. Variáveis locais vivem apenas no seu próprio bloco { ... }.
Erro nº 3: Conflito de nomes (sombreamento de variáveis).
Se você declarar no método uma variável com o mesmo nome do campo da classe, é fácil se confundir sobre qual variável está usando. Utilize this para indicar explicitamente o campo da classe: this.value.
Erro nº 4: Tentar acessar um método ou campo private a partir de outra classe.
Se um método ou campo for marcado como private, ele estará acessível apenas dentro da mesma classe. Tentar acessá-lo de fora causará um erro de compilação.
Erro nº 5: Esquecer o escopo dentro de um laço ou bloco.
Uma variável declarada dentro de um laço ou de qualquer bloco { } vive apenas dentro desse bloco. Fora do bloco ela não existe.
GO TO FULL VERSION