Classe Object do Java
Métodos herdados por todo objeto Java de java.lang.Object — toString, equals, hashCode, getClass, clone e finalize.
Toda classe em Java — quer você escreva extends Object ou não — está abaixo de java.lang.Object na hierarquia de tipos. Isso significa que toda referência que você possui tem um pequeno conjunto de métodos disponíveis: toString, equals, hashCode, getClass e alguns outros. Entender o que eles fazem por padrão e quando você deve substituí-los é a diferença entre objetos que funcionam bem com coleções, depuradores e frameworks — e objetos que não funcionam.
Este capítulo é o mapa do território. Os próximos capítulos detalham os métodos específicos (equals/hashCode, toString, clone) um de cada vez.
Extensão implícita
Escreva uma classe sem cláusula extends:
public class Box {
int contents;
}O compilador a trata como se você tivesse escrito public class Box extends Object. É por isso que você pode passar um Box para qualquer coisa declarada como Object — todo tipo de referência, em última análise, é um Object.
Os métodos herdados em resumo
| Método | O que o padrão faz |
|---|---|
toString() | Retorna ClassName@hashCodeHex — quase nunca é o que você quer |
equals(Object) | Igualdade por referência (==) |
hashCode() | Um valor derivado da identidade do objeto, não do seu conteúdo |
getClass() | O token Class<?> em tempo de execução — nunca nulo |
clone() | Uma cópia superficial campo a campo, mas somente em classes que implementam Cloneable; caso contrário, lança exceção |
wait(), notify(), notifyAll() | Primitivas de monitor de baixo nível usadas com synchronized |
finalize() | Hook chamado pelo GC antes de reclamar um objeto — obsoleto, não use |
Os três primeiros — toString, equals e hashCode — são os que quase toda classe que carrega valores deve substituir, porque as versões herdadas descrevem a identidade de um objeto em vez de seu conteúdo. getClass você chama em objetos mas nunca substitui. As primitivas de threading você raramente usa diretamente. clone e finalize devem ser evitados em código moderno (clone tem seu próprio capítulo; finalize é substituído por try-with-resources e Cleaner).
record (Java 16+), o compilador gera toString, equals e hashCode para você a partir dos componentes — os três baseados no conteúdo, não na identidade. Isso elimina a maior parte do boilerplate deste capítulo. Veja Java Records.toString — o padrão não é útil
O toString herdado retorna algo como Box@1540e19d. Esse é o nome da classe e o hash code de identidade em hexadecimal — inútil para logging, depuração ou qualquer tipo de diagnóstico:
Box b = new Box();
System.out.println(b); // Box@1540e19dSubstitua-o para retornar algo legível — println, concatenação de string, frameworks de logging e depuradores de IDE chamam toString por você, então um bom método compensa em todo lugar:
@Override
public String toString() {
return "Box[contents=" + contents + "]";
}O próximo capítulo mostra como fazer isso corretamente.
equals e hashCode — andam juntos
O equals herdado só retorna true quando ambas as referências apontam para o mesmo objeto:
String a = new String("hi");
String b = new String("hi");
System.out.println(a == b); // false — different objects
System.out.println(a.equals(b)); // true — String overrides equalsString substitui equals para comparar conteúdo. Uma classe que você escreve não faz isso, a menos que você substitua por conta própria. E no momento em que você substitui equals, também deve substituir hashCode — caso contrário, seus objetos se comportarão incorretamente em HashSet e HashMap. O capítulo sobre equals e hashCode cobre as regras exatas.
equals sem substituir hashCode faz com que dois objetos que você considera iguais possam acabar em buckets de hash diferentes — então um HashSet armazena os dois sem problemas, e map.get(key) retorna null para uma chave que claramente está presente. Sempre substitua os dois juntos e mantenha-os consistentes: objetos iguais devem retornar hash codes iguais.getClass — o token do tipo em tempo de execução
getClass() retorna o objeto Class<?> que descreve o tipo em tempo de execução:
Object o = "hello";
System.out.println(o.getClass().getName()); // java.lang.StringÉ usado em código reflexivo, por implementações de equals que desejam comparações de tipo exato e por saída de depuração. Note que getClass() é final — você não pode substituí-lo.
clone e Cloneable
Object.clone() é protected e só tem sucesso em classes que declaram explicitamente implements Cloneable. O resultado é uma cópia superficial: uma nova instância com os mesmos valores de campo, incluindo as mesmas referências a sub-objetos mutáveis. É uma API notoriamente desajeitada, coberta em detalhes no capítulo sobre clonagem.
wait / notify
Esses métodos, mais notifyAll, fazem parte das primitivas de concorrência de baixo nível originais do Java. Estão vinculados a blocos synchronized e são usados para construir padrões de espera por condição. Em código moderno, prefira os utilitários de nível mais alto em java.util.concurrent (locks, BlockingQueue, CompletableFuture); wait/notify brutos são raros fora de internos de bibliotecas.
finalize — deixe de lado
finalize() era a forma do GC dar ao seu objeto uma última chance de liberar recursos. Foi depreciado desde o Java 9 e removido em versões mais recentes. Use try-with-resources para I/O e java.lang.ref.Cleaner para o caso muito raro em que você precisa executar código após a coleta.
Um exemplo prático
O que vem a seguir
A substituição mais comum — e mais propensa a erros — de um método de Object é equals combinado com hashCode. O próximo capítulo percorre o contrato que ambos os métodos devem honrar e os padrões para acertar. Continue para Java equals e hashCode.