INSTITUTO FEDERALGoiano · Campus Rio Verde
Módulo 2 de 5
Encapsulamento e
Membros de Classe
Proteger o estado do objeto com private, expor o que é seguro com getters e setters, e conhecer static, sobrecarga, equals, hashCode e toString.
Programação Orientada a Objetos em Java · Docente: Profa. Andréa Barboza Proto Sardi
Abertura. Este módulo resolve a "ferida" deixada no Módulo 1: atributos abertos. Tudo gira em torno de proteger e dar acesso controlado ao estado. A classe Aluno evolui de fato aqui.
Módulo 2 · Abertura
Objetivos de aprendizagem
Ao final, você será capaz de:
🔒Proteger
Usar private e expor acesso por getters/setters com validação.
🌐Compartilhar
Diferenciar membros de instância de membros static (da classe).
🔀Sobrecarregar
Criar métodos de mesmo nome com assinaturas diferentes.
🟰Comparar & descrever
Sobrescrever equals, hashCode e toString.
Objetivos. Quatro competências. A primeira é o coração do módulo (encapsulamento); as demais são "membros e contratos" da classe. Tudo será aplicado na classe Aluno e numa classe Pessoa.
Retomada · Situação-problema
O perigo do estado aberto
Main.java
Conta c = new Conta();
c.saldo = -9999; // e ninguém impede!
💥o problema
Com atributos abertos, qualquer ponto do código pode colocar o objeto em um estado inválido.
🎯A solução
Esconder os atributos sensíveis (private) e só permitir mudanças por métodos que validam. Isso é encapsulamento.
Gancho. Retome literalmente o saldo=-9999 do Módulo 1. A pergunta-motriz: "como impedir que o objeto fique inválido?". A resposta é o tema do dia. Não entregue a solução ainda; deixe a turma sentir a dor.
Conceito 1
Encapsulamento
Proteger as partes sensíveis do objeto e expor apenas o que é seguro, por meio de métodos.
🔒Estado oculto
Os atributos viram private — invisíveis de fora da classe.
🚪Interface exposta
Métodos public são a porta de entrada controlada para ler e alterar o estado.
🏧analogia
Um caixa eletrônico: você não mete a mão no cofre (private). Usa botões — sacar, depositar (public) — que seguem regras.
Encapsulamento. Cofre × botões é a melhor analogia. A classe decide como seu estado pode mudar. Isso reduz bugs e facilita manutenção: a regra mora num único lugar (o método), não espalhada pelo código.
Conceito 2
Modificadores de visibilidade
| Modificador | Mesma classe | Mesmo pacote | Global (qualquer pacote) |
| public (+) | ✓ visível | ✓ visível | ✓ visível |
| protected (#) | ✓ visível | ✓ visível | ⚠ só por herança |
| default (~) | ✓ visível | ✓ visível | ✗ invisível |
| private (-) | ✓ visível | ✗ invisível | ✗ invisível |
⭐boa prática
Atributos majoritariamente private (protegidos); acesso via métodos public (getters/setters).
Visibilidade. Os símbolos +/-/#/~ são os mesmos do UML. protected e default ganham importância em herança e pacotes (Módulos 4 e 5). Por ora, a regra de ouro: atributo private, método public.
Conceito 3
Getters e setters
A ponte controlada entre o mundo externo e o estado private.
Pessoa.java
private String nome;
public String getNome() { // getter: LÊ
return nome;
}
public void setNome(String nome) { // setter: ESCREVE
this.nome = nome;
}
🔑convenção
get + atributo para ler · set + atributo para escrever. É um padrão universal em Java.
Get/Set. O setter parece "só repassar" agora — mas é o lugar onde colocaremos validação no próximo slide. A IDE gera get/set automaticamente (Alt+Insert), mas o aluno deve entender o que cada um faz. Note o this (Módulo 1) ressurgindo no setter.
Conceito 4 · invariantes
O poder do setter: validar
Conta.java
private double saldo;
public void setSaldo(double saldo) {
if (saldo < 0) {
System.out.println("Saldo inválido!");
return; // barra o estado ruim
}
this.saldo = saldo;
}
🛡️invariante
Agora saldo < 0 é impossível por fora. A regra vive em um único ponto.
Resolvemos o saldo = -9999 do Módulo 1: o atributo é private e o setter recusa valores inválidos.
Validação. Aqui o encapsulamento "paga". O setter é o guardião do invariante. Mostre que sem private isso não adiantaria (alguém ainda mexeria direto). É o fechamento do arco aberto no Módulo 1.
Projeto · Aluno evolui
Aluno: somaNotas protegido
Aluno.java
class Aluno {
String nome;
private double somaNotas; // oculto
public void lerNota(double nota) {
somaNotas += nota; // só aqui muda
}
public double getSomaNotas() {
return somaNotas;
}
}
Main.java
Aluno a = new Aluno();
a.lerNota(6.0);
a.lerNota(6.0);
a.lerNota(6.0);
// a.somaNotas = 20; ✗ NÃO compila
System.out.println(a.getSomaNotas());
// → 18.0
Aluno encapsulado. Direto dos exercícios da disciplina. Antes aluno1.somanotas = 20 funcionava; agora não compila — a única forma de somar é lerNota(). O total só cresce de forma controlada. Mostre a linha comentada que daria erro.
Comparação
Antes × depois
⛔Antes (aberto)
aluno1.somaNotas = 20;
Qualquer um sobrescreve o total a qualquer momento. Estado frágil.
✅Depois (encapsulado)
aluno1.lerNota(6.0);
O total só muda somando notas válidas. Estado confiável.
?
Verificação
Por que tornar somaNotas private muda o comportamento permitido na classe Main?
Antes/depois. Resposta: private remove a permissão de acesso direto fora da classe; a Main passa a depender da interface pública (lerNota/getSomaNotas). É exatamente o exercício "torne somanotas private" da disciplina.
Projeto · regra de negócio
Frequência e limite de faltas
Aluno.java
private final int frequenciaMax = 15;
private int frequencia;
public void lancarFrequencia(int faltas) { frequencia += faltas; }
public void verificarQtdFaltas() {
if (frequencia == frequenciaMax) System.out.println("Atingiu o limite!");
else if (frequencia > frequenciaMax) System.out.println("Ultrapassou o limite!");
else System.out.println("Faltas: " + frequencia);
}
🔐final = constante
private final torna frequenciaMax uma constante: definida uma vez, nunca alterada.
Frequência. Direto do exercício. A regra de negócio (máximo 15 faltas) fica encapsulada na própria classe. Introduza final como "constante". A cada lançamento, o aluno verifica se passou do limite — comportamento centralizado e protegido.
Conceito 5
Membros static: da classe, não do objeto
Um atributo static é compartilhado por todas as instâncias — existe uma única cópia, na classe.
Aluno.java
static int contaAluno = 0; // 1 cópia para TODOS
public Aluno() { // construtor
contaAluno++; // cada novo aluno incrementa
}
🌐instância × static
Atributo de instância: um por objeto (ex.: nome). Atributo static: um para a classe inteira (ex.: contaAluno).
static (atributo). Exercício "quantos alunos cadastrados". Cada objeto tem seu nome (instância), mas o contador é único e vive na classe (static). O construtor incrementa a cada new. Desenhe: 3 alunos, 3 nomes, 1 contador compartilhado.
Conceito 6
Métodos static: chamados pela classe
Aluno.java
static public void qtdAlunos() {
System.out.println(
"Cadastrados: " + contaAluno);
}
Main.java
Aluno a1 = new Aluno();
Aluno a2 = new Aluno();
Aluno.qtdAlunos(); // pela CLASSE!
// → Cadastrados: 2
📞como chamar
Método static é invocado pela classe (Aluno.qtdAlunos()), não por um objeto. É por isso que o main é static.
static (método). Agora "fecha" o mistério do main do Módulo 0: ele é static porque a JVM o chama sem ter um objeto ainda. Método static não acessa atributos de instância (não há "this"). Mostre a chamada Aluno.qtdAlunos() — sem new.
Conceito 7
Sobrecarga (overloading)
Vários métodos com o mesmo nome, diferenciados pela assinatura (tipo/quantidade de parâmetros).
Calculadora.java
int soma(int a, int b) { return a + b; }
double soma(double a, double b) { return a + b; }
int soma(int a, int b, int c) { return a + b + c; }
🎯quem decide é o compilador
Pela assinatura da chamada, o Java escolhe a versão certa em tempo de compilação.
?
Atividade
Mudar só o tipo de retorno (mesmos parâmetros) conta como sobrecarga?
Sobrecarga. Resposta da atividade: NÃO — a assinatura considera nome + parâmetros, não o retorno. Distinga já de "sobrescrita" (Módulo 4): sobrecarga é mesma classe, assinaturas diferentes; sobrescrita é subclasse, mesma assinatura.
Conceito 8 · igualdade
== compara o quê?
Main.java
Pessoa p1 = new Pessoa("Maria", 23);
Pessoa p2 = new Pessoa("Maria", 23);
System.out.println(p1 == p2);
// → false (são objetos diferentes!)
⚠️cuidado clássico
== compara referências (mesmo endereço no heap), não o conteúdo. Dois objetos "iguais" têm endereços diferentes.
==. Liga direto a referências (Módulo 1). p1 e p2 têm os mesmos dados, mas são objetos distintos → == dá false. Para comparar conteúdo, precisamos de equals — próximo slide. Erro frequentíssimo em provas.
Conceito 9
Sobrescrevendo equals( )
Pessoa.java
@Override
public boolean equals(Object o) {
Pessoa p = (Pessoa) o;
return idade == p.idade
&& nome.equals(p.nome);
}
🟰igualdade lógica
Agora p1.equals(p2) compara conteúdo (nome + idade) e retorna true.
@Override avisa o compilador que estamos substituindo um método herdado de Object.
equals. Toda classe herda equals de Object (que compara referência, igual ao ==). Sobrescrevendo, definimos o que "iguais" significa no nosso domínio. @Override é introduzido aqui como anotação de segurança — formalizamos a sobrescrita no Módulo 4. Use String.equals para comparar os nomes (não ==!).
Conceito 10
hashCode( ) e o contrato
Pessoa.java
@Override
public int hashCode() {
return Objects.hash(nome, idade);
}
🤝a regra de ouro
Se sobrescreveu equals, sobrescreva também hashCode. Objetos iguais devem ter o mesmo hash.
Objects.hash(...) gera um código a partir dos mesmos campos usados no equals. É o que coleções como HashSet e HashMap usam para localizar objetos.
hashCode. O "contrato equals/hashCode": iguais por equals ⇒ mesmo hashCode. Quebrar isso causa bugs sutis em HashSet/HashMap (Módulo 3). Use Objects.hash com os MESMOS campos do equals. A IDE gera os dois juntos por isso.
Conceito 11 · retomada do M1
toString( ), agora formalmente
Pessoa.java
@Override
public String toString() {
return "Nome: " + nome
+ " | Idade: " + idade;
}
🔁o ciclo se completa
No Módulo 1 usamos toString pela utilidade. Agora entendemos: é uma sobrescrita de um método de Object.
equals, hashCode e toString são os três métodos de Object que quase toda classe sobrescreve.
toString (formal). Promessa do Módulo 1 cumprida: toString é herdado de Object e sobrescrito com @Override. Agrupe os três (equals/hashCode/toString) como "o trio de Object". Conecte ao polimorfismo que virá: println chama o toString certo em tempo de execução.
Erros frequentes
Onde se tropeça
🟰Usar == para conteúdo
Comparar objetos com == em vez de equals. Quase sempre dá false.
🤝equals sem hashCode
Sobrescrever um e esquecer o outro → bugs em HashSet/HashMap.
🔓Getter que vaza
Private no atributo, mas um getter devolve referência mutável e dá acesso por fora.
🌐static onde devia ser instância
Tornar static um dado que é de cada objeto → todos compartilham sem querer.
Erros. O par == vs equals e o contrato equals/hashCode são campeões de prova. O "getter que vaza" é mais avançado — mencione de leve. O static indevido aparece quando o aluno usa static para "resolver" erros de compilação sem entender.
Prática guiada
Aluno completo (encapsulado)
Aluno.java (resumo)
class Aluno {
String nome;
private double somaNotas;
private final int frequenciaMax = 15;
private int frequencia;
static int contaAluno = 0;
public Aluno() { contaAluno++; }
public void lerNota(double n) { somaNotas += n; }
public double getSomaNotas() { return somaNotas; }
public void lancarFrequencia(int f) { frequencia += f; }
// + verificarQtdFaltas() e static qtdAlunos()
}
Prática guiada. Monte a classe Aluno completa com a turma, juntando tudo: encapsulamento (somaNotas, frequencia), constante (frequenciaMax), static (contaAluno) e construtor. É a versão consolidada dos exercícios. Rode com 2 alunos e chame qtdAlunos().
Exercício autônomo
Sua vez: classe Pessoa
📋Requisitos
• Atributos private: nome (String) e idade (int).
• Construtor que recebe nome e idade (use this).
• Getters e setters para ambos.
• Sobrescreva equals, hashCode e toString.
• Na Main: crie duas Pessoas "Maria, 23" e mostre que == dá false mas equals dá true.
!
Entrega
Saída esperada: false (pelo ==) e true (pelo equals).
Exercício autônomo. Tempo: 15 min. É a classe Pessoa dos exercícios, que consolida encapsulamento + o trio de Object. Reaproveitaremos Pessoa no Módulo 4 (herança). Recolha as soluções.
Revisão · verificação
Revisão relâmpago
✓
private esconde; getters/setters dão acesso controlado.
✓
Setter valida → garante invariantes.
✓
static = membro da classe (1 cópia para todos).
✓
== referência · equals conteúdo · sempre com hashCode.
?
Questão 1
Para que serve um setter além de atribuir?
?
Questão 2
Por que main é static?
?
Questão 3
Qual o "contrato" entre equals e hashCode?
Revisão. Respostas: (1) validar e centralizar regras (invariantes). (2) é chamado pela JVM sem objeto existir → membro da classe. (3) objetos iguais por equals devem ter o mesmo hashCode. Ticket de saída.
Síntese do Módulo 2
O que levamos deste módulo
🔒Proteção
Estado private + acesso por métodos = objetos sempre válidos.
🌐Classe vs instância
static pertence à classe; o resto pertence a cada objeto.
🟰O trio de Object
equals, hashCode e toString sob medida.
➡️próximo módulo
Relacionamentos e Coleções — um Aluno raramente está sozinho. Vamos guardar muitos objetos em listas e relacioná-los.
Síntese. Três pilares: proteção, classe×instância, trio de Object. Ponte para o Módulo 3: precisamos de uma turma de alunos — coleções. Encerramento: "nossos objetos agora são seguros; vamos organizá-los em grupos".