Saltar al contenido principal

Clases, objetos y referencias

En Java 1 podíamos relacionar alumnos y notas mediante dos arrays:

String[] nombres = {"Ana", "Luis"};
double[] notas = {8.5, 6.0};

La posición mantiene la relación. Si añadimos correo, asistencia o nivel, tendremos más arrays que deben permanecer sincronizados. El problema ya no es recorrer datos, sino expresar que varios datos pertenecen a la misma entidad.

Clase y objeto​

Una clase describe qué datos y operaciones tendrán las entidades de un tipo. Un objeto es una instancia concreta creada a partir de esa descripción.

class Alumno {
String nombre;
double nota;
}

Esta declaración no crea alumnos. Define el modelo. Para crear uno usamos new:

Alumno ana = new Alumno();
ana.nombre = "Ana";
ana.nota = 8.5;

Alumno es la clase, el resultado de new Alumno() es el objeto y ana es una variable que guarda una referencia a ese objeto.

Los atributos son accesibles aquí para observar el modelo con el mínimo de sintaxis. En JV2-03 aprenderemos a protegerlos; no tomes los atributos públicos como diseño definitivo.

Objetos independientes​

Cada new crea un objeto con identidad propia:

Alumno ana = new Alumno();
ana.nombre = "Ana";

Alumno luis = new Alumno();
luis.nombre = "Luis";

luis.nombre = "Luis Miguel";
System.out.println(ana.nombre); // Ana
System.out.println(luis.nombre); // Luis Miguel

Modificar luis no cambia ana: son dos objetos distintos.

Práctica inmediata​

Predice cuántos objetos se crean:

Alumno primero = new Alumno();
Alumno segundo = new Alumno();
Alumno tercero = segundo;

Se crean dos objetos. segundo y tercero contienen referencias al mismo objeto.

Referencias compartidas​

Asignar una referencia no copia el objeto:

Alumno a = new Alumno();
a.nombre = "Ada";

Alumno b = a;
b.nombre = "Ada Lovelace";

System.out.println(a.nombre); // Ada Lovelace

Podemos representarlo así:

a ──┐
├──> objeto Alumno
b ──┘

El cambio se observa desde ambas variables porque solo existe un objeto. Este fenómeno se suele llamar aliasing: varias referencias permiten alcanzar la misma instancia.

null: ninguna referencia​

Una variable de referencia puede no apuntar a ningún objeto:

Alumno pendiente = null;

if (pendiente == null) {
System.out.println("Todavía no hay alumno");
}

Intentar acceder a pendiente.nombre no funciona porque no existe un objeto receptor. Java produciría un NullPointerException; aquí solo necesitamos reconocer la causa, no estudiar excepciones.

Programa completo​

Guarda este código como ReferenciasDemo.java:

class Alumno {
String nombre;
double nota;
}

public class ReferenciasDemo {
public static void main(String[] args) {
Alumno ana = new Alumno();
ana.nombre = "Ana";
ana.nota = 8.5;

Alumno luis = new Alumno();
luis.nombre = "Luis";
luis.nota = 6.0;

Alumno aliasDeAna = ana;
aliasDeAna.nota = 9.0;

System.out.println(ana.nombre + ": " + ana.nota);
System.out.println(luis.nombre + ": " + luis.nota);
System.out.println("Mismo objeto: " + (ana == aliasDeAna));

Alumno sinAsignar = null;
System.out.println("Sin asignar: " + (sinAsignar == null));
}
}

Salida:

Ana: 9.0
Luis: 6.0
Mismo objeto: true
Sin asignar: true

== permite observar aquí si dos referencias apuntan a la misma instancia. No compara el contenido completo de los objetos.

Errores habituales​

  • Confundir la clase con un objeto: declarar class Alumno define el tipo, no crea alumnos.
  • Creer que new crea una clase: crea una instancia de una clase ya declarada.
  • Esperar una copia con Alumno b = a: se copia la referencia.
  • Usar una referencia null como si existiera un objeto.
  • Exponer atributos y asumir que siempre será el diseño correcto: es una simplificación temporal.

Mini reto: productos relacionados​

Modela un Producto con nombre y precio. Crea dos productos independientes y una tercera referencia que apunte al primero. Cambia su precio mediante esa referencia y demuestra que el primer producto refleja el cambio, pero el segundo no.

Solución razonada​

class Producto {
String nombre;
double precio;
}

public class ProductosDemo {
public static void main(String[] args) {
Producto teclado = new Producto();
teclado.nombre = "Teclado";
teclado.precio = 30.0;

Producto raton = new Producto();
raton.nombre = "Ratón";
raton.precio = 20.0;

Producto oferta = teclado;
oferta.precio = 25.0;

System.out.println(teclado.nombre + ": " + teclado.precio);
System.out.println(raton.nombre + ": " + raton.precio);
System.out.println("Oferta comparte objeto: " + (oferta == teclado));
}
}

Hay dos new, por tanto dos objetos. oferta no crea un tercero: permite modificar el mismo objeto que teclado.

En la siguiente unidad construiremos objetos con estado inicial y moveremos operaciones hacia métodos de instancia.