ProgramaciónDesarrollador Java

¿Qué son los objetos inmutables en Java, cuál es su valor y cómo implementar correctamente una clase inmutable propia?

Supere entrevistas con el asistente de IA Hintsage

Respuesta.

Objetos inmutables son aquellos cuyos estados no se pueden cambiar después de ser creados. Sus principales características son:

  • Todos los campos son final;
  • No contienen setters;
  • No se puede obtener una referencia mutable a los objetos internos (por ejemplo, en colecciones).

Ventajas de los objetos inmutables:

  • Son seguros para el acceso multihilo (thread-safe);
  • Es fácil de almacenar en caché y reutilizar como claves en colecciones;
  • Simplifican la depuración y las pruebas;
  • Menos errores debido a cambios inesperados en el estado.

Ejemplo de implementación de una clase inmutable:

public final class Person { private final String name; private final int age; private final List<String> phones; public Person(String name, int age, List<String> phones) { this.name = name; this.age = age; // Protección contra la mutación de la lista pasada this.phones = Collections.unmodifiableList(new ArrayList<>(phones)); } public String getName() { return name; } public int getAge() { return age; } public List<String> getPhones() { return phones; } // Devolvemos una lista de solo lectura }

Pregunta engañosa.

¿Por qué String en Java es inmutable y qué pasaría si no fuera así? Muchos responden "por seguridad", pero ¿qué significa esto en la práctica?

Respuesta:

String se utiliza en muchos lugares: como claves en colecciones, en lógica de seguridad (por ejemplo, contraseñas). Si se pudiera cambiar una cadena a través de una referencia, afectaría a todas las demás referencias al mismo objeto, lo que haría imposible que las colecciones funcionaran correctamente (por ejemplo, HashMap — al calcular hashCode) y podría conducir a vulnerabilidades de seguridad.

Ejemplos de errores reales debido al desconocimiento de los matices del tema.


Historia

En un gran proyecto bancario, se pasaban colecciones internas (lista de transacciones) a través de un getter normal. Como resultado, la lista podía ser modificada desde el exterior, rompiendo invariantes (por ejemplo, agregar una transacción con una fecha incorrecta). Esto llevó a la pérdida de datos hasta que empezaron a devolver Collections.unmodifiableList.

Historia

En la clase raíz de configuración había campos-objetos no protegidos (Date, List). En uno de los hilos, se modificó la configuración, y en el segundo se obtuvieron datos obsoletos o inconsistentes, lo que hizo que un algoritmo de negocio funcionara de forma incorrecta.

Historia

En el sistema de inicio de sesión, las contraseñas se almacenaban en un objeto mutable. A través de un acceso no seguro, de repente "se filtró" la contraseña de otro usuario, ya que el mismo objeto se utilizaba por muchos hilos.