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 Alumnodefine el tipo, no crea alumnos. - Creer que
newcrea una clase: crea una instancia de una clase ya declarada. - Esperar una copia con
Alumno b = a: se copia la referencia. - Usar una referencia
nullcomo 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.