W3docs

Clase Object de Java

Métodos que todo objeto Java hereda de java.lang.Object: toString, equals, hashCode, getClass, clone y finalize.

Toda clase en Java — ya sea que escribas extends Object o no — se sitúa por debajo de java.lang.Object en la jerarquía de tipos. Eso significa que toda referencia que manejes tiene un pequeño conjunto de métodos disponibles: toString, equals, hashCode, getClass, y algunos más. Entender lo que hacen por defecto, y cuándo deberías sobrescribirlos, es la diferencia entre objetos que funcionan bien con colecciones, depuradores y frameworks — y objetos que no lo hacen.

Este capítulo es el mapa del territorio. Los próximos capítulos profundizan en los métodos específicos (equals/hashCode, toString, clone) uno a la vez.

Extensión implícita

Escribe una clase sin cláusula extends:

public class Box {
  int contents;
}

El compilador la trata como si hubieras escrito public class Box extends Object. Por eso puedes pasar un Box a cualquier cosa declarada como Object — todo tipo de referencia, en última instancia, es un Object.

Los métodos heredados de un vistazo

MétodoQué hace por defecto
toString()Devuelve ClassName@hashCodeHex — casi nunca es lo que quieres
equals(Object)Igualdad de referencia (==)
hashCode()Un valor derivado de la identidad del objeto, no de su contenido
getClass()El token Class<?> en tiempo de ejecución — nunca null
clone()Una copia superficial campo a campo, pero solo en clases que implementan Cloneable; de lo contrario lanza una excepción
wait(), notify(), notifyAll()Primitivas de monitor de bajo nivel usadas con synchronized
finalize()Hook llamado por el GC antes de reclamar un objeto — obsoleto, no usar

Los tres primeros — toString, equals y hashCode — son los que casi toda clase que lleva valores debería sobrescribir, porque las versiones heredadas describen la identidad de un objeto en lugar de su contenido. getClass se llama sobre los objetos pero nunca se sobrescribe. Las primitivas de hilos raramente se tocan directamente. clone y finalize es mejor evitarlos en código moderno (clone tiene su propio capítulo; finalize ha sido reemplazado por try-with-resources y Cleaner).

Nota
Si usas un record (Java 16+), el compilador genera toString, equals y hashCode por ti a partir de los componentes — los tres basados en el contenido, no en la identidad. Eso elimina la mayor parte del código repetitivo del que trata este capítulo. Ver Java Records.

toString — el valor por defecto no es útil

El toString heredado devuelve algo como Box@1540e19d. Ese es el nombre de la clase y el hash code de identidad en hexadecimal — inútil para logging, depuración o cualquier tipo de diagnóstico:

Box b = new Box();
System.out.println(b);   // Box@1540e19d

Sobrescríbelo para devolver algo legible — println, la concatenación de cadenas, los frameworks de logging y los depuradores de IDE llaman a toString por ti, así que uno bueno rinde frutos en todas partes:

@Override
public String toString() {
  return "Box[contents=" + contents + "]";
}

El próximo capítulo explica cómo hacer esto bien.

equals y hashCode — van juntos

El equals heredado solo devuelve true cuando ambas referencias apuntan al mismo 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 equals

String sobrescribe equals para comparar contenidos. Una clase que tú escribas no lo hace, a menos que tú mismo lo sobrescribas. Y en el momento en que sobrescribes equals, también debes sobrescribir hashCode — de lo contrario tus objetos se comportarán mal de forma silenciosa en HashSet y HashMap. El capítulo sobre equals y hashCode cubre las reglas exactas.

Advertencia
Si sobrescribes equals sin sobrescribir hashCode, dos objetos que consideras iguales pueden caer en diferentes cubos hash — por lo que un HashSet los almacenará a ambos tranquilamente, y map.get(key) devolverá null para una clave que claramente está presente. Siempre sobrescribe los dos juntos, y mantenlos consistentes: los objetos iguales deben devolver códigos hash iguales.

getClass — el token de tipo en tiempo de ejecución

getClass() devuelve el objeto Class<?> que describe el tipo en tiempo de ejecución:

Object o = "hello";
System.out.println(o.getClass().getName());  // java.lang.String

Se usa dentro de código reflexivo, en implementaciones de equals que quieren comparaciones de tipo exacto, y en la salida de depuración. Ten en cuenta que getClass() es final — no puedes sobrescribirlo.

clone y Cloneable

Object.clone() es protected y solo tiene éxito en clases que declaran explícitamente implements Cloneable. El resultado es una copia superficial: una nueva instancia con los mismos valores de campo, incluyendo las mismas referencias a sub-objetos mutables. Es una API notoriamente incómoda, tratada en detalle en el capítulo de clonación.

wait / notify

Estos métodos, junto con notifyAll, forman parte de las primitivas de concurrencia de bajo nivel originales de Java. Están vinculados a bloques synchronized y se usan para construir patrones de espera por condición. En código moderno, prefiere las utilidades de nivel superior en java.util.concurrent (bloqueos, BlockingQueue, CompletableFuture); el uso directo de wait/notify es poco frecuente fuera de las implementaciones internas de bibliotecas.

finalize — mejor no tocarlo

finalize() era la forma que tenía el GC de dar a tu objeto una última oportunidad para liberar recursos. Ha sido obsoleto desde Java 9 y eliminado en versiones más recientes. Usa try-with-resources para I/O y java.lang.ref.Cleaner para el caso muy raro en el que necesitas ejecutar código después de la recolección.

Un ejemplo completo

java— editable, runs on the server

Qué sigue

La sobrescritura más común — y más propensa a errores — de un método Object es equals combinado con hashCode. El próximo capítulo explica el contrato que ambos métodos deben cumplir y los patrones para hacerlo correctamente. Continúa en Java equals y hashCode.

Práctica

Práctica
¿Qué devuelve el `toString()` por defecto heredado de `Object`?
¿Qué devuelve el `toString()` por defecto heredado de `Object`?
Was this page helpful?