- Aprender
- Informática
- Programación
- Programación orientada a objetos con Java
- Objetos y referencias en Java
Objetos, referencias e igualdad en Java
null, pasa objetos a métodos y devuélvelos, y crea clases a partir de otros objetos y de listas de objetos. Después, escribe equals y hashCode para que las listas, los conjuntos y los mapas comparen tus objetos por su contenido, y mantén la interfaz de texto de un programa separada de su lógica.PrincipianteUnidad 6 de 11 del curso Curso de Programación orientada a objetos con Java
Lo que aprenderás
- Explica qué contiene una variable Java de tipo primitivo o de referencia, predice qué efecto tiene la asignación en cada caso y maneja variables que no hacen referencia a ningún objeto.
- Compara variables primitivas y variables de referenciaNombra los tipos primitivos de Java, explica que una variable de referencia contiene una referencia a un objeto y predice el efecto de copiar cada tipo de variable.
- Sigue los alias, las reasignaciones y los objetos inalcanzablesSigue varias variables que hacen referencia a un mismo objeto, distingue entre cambiar un objeto y hacer que una variable apunte a otro objeto, y explica qué ocurre con los objetos a los que nada hace referencia.
- Trabaja de forma segura con la referencia nulaExplica qué significa null, reconoce dónde aparece, evita NullPointerException y decide cuándo null es un valor razonable.
- Compara variables primitivas y variables de referencia
- Explica cómo pasa Java los argumentos, escribe métodos que reciban y cambien objetos o los comparen con otro objeto de la misma clase, y devuelve objetos nuevos o existentes de forma segura.
- Utiliza objetos como parámetros de métodosPredice qué puede y qué no puede cambiar un método cuando recibe un valor primitivo o una referencia, y escribe métodos que reciban objetos de otra clase o de su propia clase.
- Devuelve objetos desde métodosEscribe métodos que creen y devuelvan objetos nuevos, devuelvan referencias a objetos que ya existen y eviten exponer accidentalmente el estado interno de un objeto.
- Utiliza objetos como parámetros de métodos
- Diseña clases cuyos campos sean otros objetos o listas de objetos, trabaja con listas que contienen objetos y decide cuándo se deben compartir o copiar los objetos contenidos.
- Compón una clase a partir de otros objetosModela relaciones de pertenencia con campos de tipo de referencia, delega trabajo en los objetos contenidos y decide si un objeto contenido debe compartirse o copiarse.
- Almacena y procesa objetos en listasAñade, busca, cambia y elimina objetos en un ArrayList, y explica por qué la lista contiene referencias en lugar de copias.
- Crea una clase en torno a una lista de objetosEscribe una clase que mantenga una lista de objetos como campo, la inicialice, ofrezca métodos para trabajar con su contenido y mantenga oculta la propia lista.
- Compón una clase a partir de otros objetos
- Distingue la identidad de la igualdad de contenido, escribe métodos equals y hashCode correctos y predice cómo los utilizan las listas, los conjuntos y los mapas.
- Distingue la identidad de la igualdad de contenidoExplica qué comparan == y equals en valores primitivos, cadenas, envoltorios y tus propias clases, y predice el resultado de cada comparación.
- Escribe un método equalsSobrescribe equals con la firma y los pasos correctos, compara correctamente cada tipo de campo y mantén el método conforme a su contrato general.
- Implementa hashCode de forma coherente con equalsExplica el contrato de hashCode, escribe hashCode para una clase que sobrescribe equals y predice cómo se comportan HashSet y HashMap con él y sin él.
- Distingue la identidad de la igualdad de contenido
- Estructura un programa de texto de modo que una clase se comunique con el usuario y otras clases contengan los datos y las reglas; después, amplíalo y compruébalo sin mezclar ambas partes.
- Divide un programa en una interfaz de usuario y clases de lógicaDecide qué código corresponde a la interfaz de usuario y cuál a la lógica, y conéctalos mediante parámetros del constructor y un método de inicio.
- Amplía y comprueba un programa con la interfaz y la lógica separadasAñade funciones ampliando la lógica y llamándola desde la interfaz, comprueba la lógica directamente con valores conocidos y mantén cada clase centrada en una tarea.
- Divide un programa en una interfaz de usuario y clases de lógica
Guía de estudio de Objetos y referencias en Java
1Sigue los valores y las referencias
Una variable y un objeto son partes distintas de un programa Java. Comprender qué contiene cada variable te permite explicar por qué una asignación crea números independientes mientras que otra da a dos nombres acceso al mismo objeto. Empieza por los valores que se copian y después sigue los cambios una sentencia a la vez. La misma imagen te ayudará a encontrar objetos ausentes, interpretar errores de null y decidir si un cambio debe compartirse.
1.1 Qué contiene una variable
Una variable Java contiene un valor. La pregunta importante es si ese valor es un dato ordinario o una referencia que lleva a un objeto. La asignación copia ese valor en ambos casos.
Datos primitivos y referencias a objetos
Java tiene ocho tipos primitivos: byte, short, int, long, float, double, char y boolean. Una variable primitiva contiene sus datos directamente. Una variable de referencia contiene una referencia a un objeto o el valor especial null. Las clases como String, Scanner y ArrayList, tus propias clases y los arreglos son tipos de referencia.
| Declaración | Qué contiene la variable |
|---|---|
int count = 5; | El valor entero 5 |
Counter c = new Counter(); | Una referencia a un contador nuevo |
Counter c = null; | Ninguna referencia a un objeto |
En new Counter(), Java crea un objeto, ejecuta su constructor y produce una referencia. La asignación almacena esa referencia en c. Dibuja la variable como una caja con una flecha que lleva a un objeto separado. Cada evaluación de new crea un objeto distinto, aunque los argumentos coincidan.
Copia el valor y sigue el resultado
Con valores primitivos, cambiar la variable copiada deja intacta la primera variable:
int a = 5;
int b = a;
b = b + 1;
La última línea lee 5 de b, suma 1 y almacena 6 en b. El valor de a sigue siendo 5.
Para un ejemplo con referencias, utiliza esta clase pequeña:
class Counter {
private int value;
Counter() { this.value = 0; }
Counter(int value) { this.value = value; }
void increase() { value++; }
int value() { return value; }
}
Ahora sigue estas sentencias:
Counter first = new Counter();
Counter second = first;
second.increase();
System.out.println(first.value()); // 1
Solo la primera línea crea un contador. La segunda copia la referencia, así que la llamada al método cambia el único contador al que llegan ambas variables. Para crear un contador independiente con los mismos datos, escribe new Counter(first.value()). Copiar una variable no llama a un constructor.
Envoltorios y cadenas inmutables
Cada tipo primitivo tiene una clase envoltorio, como Integer, Double, Boolean o Character. Las variables de tipo envoltorio contienen referencias o null. Java puede convertir automáticamente entre un valor primitivo y su envoltorio; estas conversiones son el empaquetado automático y el desempaquetado. Las listas contienen referencias, así que una lista de enteros es ArrayList<Integer>.
String también es un tipo de referencia, pero sus objetos son inmutables: sus caracteres no pueden cambiar después de su creación.
String s = "java";
String t = s;
t = t.toUpperCase();
Aquí s sigue haciendo referencia a «java» y t hace referencia a «JAVA». La última línea asigna el resultado a t; no cambia la cadena original compartida. Los métodos de String pueden reutilizar el objeto original cuando no hace falta cambiarlo.
Imprimir un objeto utiliza su método toString(). Sin una sobrescritura, el resultado heredado tiene una forma como Counter@7e9e5f8a: nombre de la clase, @ y un código hash hexadecimal. Define toString() cuando la salida impresa deba mostrar los datos del objeto.
Puntos clave
- La asignación copia un valor: un dato primitivo o una referencia a un objeto.
- Cada evaluación de
newcrea un objeto distinto. - Copiar una referencia comparte el objeto; no copia sus campos.
- Los envoltorios y las cadenas son tipos de referencia; las cadenas son inmutables.
- Imprimir un objeto utiliza
toString().
1.2 Objetos compartidos y cambios de dirección de las flechas
Dos variables que hacen referencia al mismo objeto son alias. Un alias proporciona otra vía para acceder a datos existentes. Para seguir un programa, mantén separadas las variables de los objetos a los que llegan.
La mutación cambia datos; la reasignación cambia una variable
Una mutación cambia el estado de un objeto, como su peso almacenado. Una reasignación introduce un valor diferente en una variable. En una variable de referencia, eso cambia su flecha.
Utiliza una caja cuyo peso se expresa en kilogramos:
class Box {
private double weight;
Box(double weight) { this.weight = weight; }
double getWeight() { return weight; }
void setWeight(double weight) { this.weight = weight; }
}
Sigue esta secuencia:
Box a = new Box(2);
Box b = a;
b.setWeight(5);
b = new Box(7);
- Dibuja la variable
aapuntando al objeto A, con peso 2. - Dibuja
bapuntando a ese mismo objeto A. Sigue habiendo una caja. - Cambia el peso dentro del objeto A a 5. Ambas vías llegan ahora al peso 5.
- Crea el objeto B con peso 7 y redirige solo
bhacia él.
Al final, a.getWeight() es 5 y b.getWeight() es 7. La cuarta sentencia no restablece el objeto A ni mueve a al objeto B.
| Acción | Cambio en el diagrama | Efecto a través de otros alias |
|---|---|---|
b.setWeight(5) | Cambia un campo dentro del objeto | Ven el nuevo peso |
b = new Box(7) | Redirige la flecha de b | Conservan sus flechas actuales |
b = null | Elimina la flecha de b | Siguen llegando a sus objetos |
La alcanzabilidad determina el tiempo de vida de un objeto
Un objeto es alcanzable mientras el programa en ejecución pueda seguir alguna cadena de referencias utilizable hasta él. Esa cadena puede pasar por una variable, un campo de objeto o un elemento de lista. Por tanto, que desaparezca una variable local no basta para que un objeto sea inalcanzable si una lista todavía lo contiene.
Continúa el seguimiento con a = null;. En este ejemplo, el objeto A, que pesa 5 kg, ya no tiene ninguna vía alcanzable. Queda disponible para la recolección de basura. Java recupera automáticamente la memoria de los objetos inalcanzables, pero el recolector decide cuándo; la asignación no garantiza una recolección inmediata. El objeto B sigue siendo alcanzable a través de b.
No liberas objetos Java manualmente. Cuando un programa pierde su última referencia utilizable a un objeto, también pierde la capacidad de inspeccionar o cambiar los datos de ese objeto.
Encuentra los alias ocultos
Los alias aparecen cuando se añade un objeto a una lista, se pasa a un método, se devuelve desde un método o se almacena en el campo de otro objeto. Cada operación puede transmitir una referencia. Antes de mutar un objeto recibido de otro lugar, identifica qué otro código todavía puede llegar a él.
Un error habitual empieza con la idea: «He copiado la variable, así que tengo mi propia caja». Utiliza un objeto nuevo cuando los cambios deban ser independientes. Para diagnosticar el error, cuenta las creaciones de objetos por separado de las asignaciones, etiqueta cada objeto una sola vez y procesa cada sentencia en orden. No vuelvas a dibujar un objeto solo porque una segunda variable apunte ahora a él.
Puntos clave
- Los alias llevan a un mismo objeto, así que comparten sus mutaciones.
- La reasignación redirige una variable y deja intactos los demás alias.
- Sigue los campos y las flechas de referencia por separado.
- Un objeto inalcanzable queda disponible para la recolección de basura; el momento no está especificado.
1.3 Ningún objeto: null y comprobaciones seguras
El valor null significa que una referencia no lleva a ningún objeto. Es útil para indicar un resultado ausente, pero un programa debe manejar esa ausencia antes de intentar utilizar un objeto.
Ausente no es lo mismo que vacío
String text = ""; hace referencia a una cadena real de longitud 0. String text = null; no hace referencia a ninguna cadena, así que no hay ningún objeto sobre el que llamar a length().
Un valor primitivo no puede contener null: int x = null; no se compila. Un envoltorio sí puede contenerlo: Integer x = null; se compila. Convertir ese envoltorio a un valor primitivo con int y = x; lanza una NullPointerException, porque el desempaquetado necesita un objeto envoltorio.
Los campos reciben valores iniciales predeterminados cuando se crea un objeto. Los campos de referencia empiezan con null, los campos numéricos con 0 y los campos booleanos con false, hasta que la inicialización les asigna otros valores. Las variables locales no tienen valor predeterminado. Leer una variable local antes de asignarle un valor causa un error de compilación, como «es posible que la variable no se haya inicializado».
Encuentra la referencia ausente
Llamar a un método de instancia a través de null, acceder a un campo de instancia a través de él o leer la longitud de un arreglo a través de él lanza NullPointerException en tiempo de ejecución. Por ejemplo:
String name = null;
System.out.println(name.length());
Lee el número de línea de la excepción y después identifica qué referencia faltaba en esa línea. Los mensajes informativos están activados de forma predeterminada desde Java 15. Un mensaje puede indicar Cannot invoke "SimpleDate.getYear()" because "this.birthday" is null. Los nombres locales pueden aparecer como marcadores como <local4>, según los detalles de compilación. Sigue el rastro hasta el constructor, la asignación o la búsqueda que debería haber proporcionado el objeto.
Imprimir una variable String cuyo valor es null imprime «null»; concatenarla produce texto como «Hi null». Estas operaciones no necesitan llamar a un método de instancia a través de esa variable. Pueden revelar datos ausentes antes de que falle una llamada posterior a un método.
Comprueba antes de usar
El operador && utiliza evaluación en cortocircuito: evalúa el lado derecho solo si el izquierdo es verdadero.
if (name != null && name.length() > 0) {
System.out.println(name);
}
Cuando name es null, la primera comprobación es falsa y se omite length(). Al invertir las dos comprobaciones se pierde esa protección: la llamada al método se ejecutaría primero.
| Objetivo | Expresión segura |
|---|---|
| Comprobar ausencia | name == null |
| Comprobar presencia | name != null |
| Comparar con un comando fijo | "quit".equals(command) |
La cadena fija es un objeto conocido, así que su equals puede recibir null y devolver false. command.equals("quit") falla cuando command es null. Del mismo modo, x.equals(null) no sirve para comprobar si x existe: ya requiere un objeto.
Haz que la ausencia sea una decisión de diseño clara
Inicializa un campo de lista con new ArrayList<>() para que una colección vacía esté disponible de inmediato. Si una búsqueda devuelve null para indicar «no encontrado», documenta ese resultado y compruébalo en el lugar de la llamada. Evita llenar todos los campos con objetos arbitrarios solo para silenciar una excepción: proporciona el objeto que necesita el programa o da a la ausencia un significado claro que el código que llama pueda manejar.
Puntos clave
nullsignifica que no hay objeto; una cadena vacía es un objeto utilizable.- Los campos reciben valores predeterminados, pero las variables locales deben tener un valor asignado antes de leerse.
- Acceder a un miembro a través de null y desempaquetar un envoltorio nulo fallan en tiempo de ejecución.
- Coloca la comprobación de null antes del acceso al miembro en
&&. - Realiza las comparaciones con texto fijo sobre la cadena conocida.
- Inicializa las colecciones y maneja de forma deliberada los resultados de búsqueda ausentes.
2Utiliza objetos en las llamadas a métodos
Los métodos permiten que los objetos colaboren. Una llamada puede leer o cambiar un objeto compartido, crear un resultado independiente o devolver un objeto que ya se encuentra en otro lugar. Estas decisiones afectan a lo que ve quien llama después de la llamada. Sigue explícitamente las referencias de los parámetros y de retorno, y coloca las comparaciones en la clase propietaria de los datos. Después decide qué métodos deben compartir el estado existente y cuáles deben protegerlo con una copia.
2.1 Los parámetros reciben valores copiados
Java pasa todos los argumentos por valor. Un parámetro es una variable nueva que recibe una copia del valor del argumento. Para un argumento que es un objeto, el valor copiado es una referencia.
¿Cambiar el parámetro o cambiar el objeto?
Un parámetro primitivo tiene sus propios datos:
static void addTen(int n) {
n = n + 10;
}
Si x es 5, llamar a addTen(x) deja x en 5. Solo el parámetro local n pasa a ser 15.
Un parámetro de referencia puede llegar al mismo objeto que quien llama. Supón que Person.growOlder() aumenta la edad almacenada:
static void celebrate(Person p) {
p.growOlder();
}
static void replace(Person p) {
p = new Person("Bo", 30);
}
Durante celebrate(ana), el parámetro p y la variable ana son alias. La edad modificada sigue siendo visible después. Durante replace(ana), asignar un valor a p solo redirige ese parámetro. La variable ana de quien llama sigue haciendo referencia a la persona original. Un método que intercambia dos parámetros de referencia tiene la misma limitación: intercambia sus propias copias, no las variables de quien llama.
| Dentro de un método | Quien llama observa |
|---|---|
| Asignar un valor a un parámetro primitivo | Ningún cambio en la variable de quien llama |
| Mutar el objeto del parámetro | Cambio de estado del objeto compartido |
| Reasignar un parámetro de referencia | Ningún cambio en la variable de quien llama |
Recibe un objeto de otra clase
Un control puede leer la altura de una persona y mantener su propio recuento de comprobaciones. Esta clase espera una persona no nula con un método getHeight() que devuelve centímetros:
class HeightGate {
private int minHeight;
private int checks;
HeightGate(int minHeight) {
this.minHeight = minHeight;
}
boolean allowIn(Person p) {
checks++;
return p.getHeight() >= minHeight;
}
int checks() { return checks; }
}
Para un control configurado en 120 cm, comprobar personas de 132 cm y 115 cm devuelve verdadero y falso. Después, el control registra 2 comprobaciones. Lee la altura de la persona mediante un método público y cambia su propio contador.
Recibe otro objeto de la misma clase
Dentro de SimpleDate.before(SimpleDate other), this es la fecha que recibe la llamada y other es el argumento. Una clase puede leer los campos privados de cualquier objeto de esa misma clase.
Para los campos day, month y year, compara en orden:
boolean before(SimpleDate other) {
if (year != other.year) {
return year < other.year;
}
if (month != other.month) {
return month < other.month;
}
return day < other.day;
}
Para el 14 de marzo de 2001 y el 2 de noviembre de 2001, los años coinciden y el mes 3 es menor que el mes 11, así que la primera fecha es anterior. Las fechas iguales devuelven falso porque ninguna es anterior a la otra.
Person.olderThan(other) puede delegar en birthday.before(other.birthday). La comparación de fechas corresponde a los datos de fecha. Los métodos que llaman a métodos de instancia sobre un parámetro necesitan un argumento no nulo; compruébalo o incluye ese requisito en la documentación de uso del método.
Puntos clave
- Cada parámetro recibe un valor copiado, incluidos los parámetros de referencia.
- La mutación puede afectar a un objeto compartido; reasignar un parámetro no puede reemplazar la variable de quien llama.
thisdesigna el objeto receptor; un parámetro de objeto designa el argumento.- Una clase puede acceder a campos privados de otro objeto de la misma clase.
- Delega las comparaciones en la clase propietaria de los datos relevantes.
2.2 Devolver un objeto nuevo o uno existente
Un método que devuelve un objeto devuelve una referencia. Su tipo indica qué clase de objeto puede devolverse, pero no indica si el objeto es nuevo, compartido o si está ausente. El propósito del método debe dejar clara esa decisión.
Crea un resultado
Un método de fábrica proporciona un objeto. Puede crear uno o reutilizar uno existente. Aquí cada llamada crea deliberadamente un boleto distinto:
class TicketMachine {
private int nextNumber = 1;
Ticket issue() {
Ticket t = new Ticket(nextNumber);
nextNumber++;
return t;
}
}
La máquina espera un constructor de Ticket que almacene su número. Tres llamadas crean tres boletos numerados 1, 2 y 3. Devolver una variable local es seguro: su referencia se copia a quien llama, que puede mantener el objeto alcanzable después de que la variable local salga de ámbito.
Un método de copia promete un objeto independiente. En la clase Counter:
Counter copy() {
return new Counter(this.value);
}
Después de Counter a = new Counter(4); Counter b = a.copy(); b.increase();, a.value() es 4 y b.value() es 5. Sustituir a.copy() por a compartiría el contador.
Un método también puede crear un objeto calculado. Para un Point con campos double x e y:
Point midpoint(Point other) {
return new Point((x + other.x) / 2.0,
(y + other.y) / 2.0);
}
Los puntos (2, 4) y (6, 10) dan (4,0; 7,0). El método calcula cada coordenada y pasa ambas a un punto nuevo; ninguno de los puntos originales cambia.
Devuelve un objeto ya almacenado
El método findByName de un registro debe devolver la persona almacenada que coincida si quienes llaman necesitan trabajar con esa entrada. Crear new Person(name, 0) devolvería datos de sustitución inventados y desvincularía del registro los cambios posteriores.
Una búsqueda puede devolver null cuando no existe ninguna coincidencia:
Person p = register.findByName("Lin");
if (p != null) {
System.out.println(p);
} else {
System.out.println("Not found");
}
Si p es la persona almacenada, los cambios realizados a través de p también cambian la persona a la que se accede mediante el registro. Una sentencia return no copia el objeto.
Mantén bajo control el estado interno mutable
Supón que Person.getBirthday() devuelve su campo SimpleDate y que esa fecha tiene métodos modificadores. Quien llama puede entonces cambiar la fecha de nacimiento de la persona sin llamar a un método de Person. Esto es una exposición del estado mutable.
Para proteger el campo, devuelve una fecha nueva construida a partir de sus valores:
SimpleDate getBirthday() {
return new SimpleDate(birthday.getDay(),
birthday.getMonth(),
birthday.getYear());
}
Esta versión requiere que se haya inicializado un objeto de fecha de nacimiento. Otros diseños utilizan una fecha inmutable u ofrecen consultas específicas sin un método de acceso a la fecha. Elige el comportamiento de cada método: find normalmente comparte una entrada almacenada, mientras que copy crea independencia. El nombre breve de un método de acceso, por sí solo, no garantiza protección.
Puntos clave
- Devolver un objeto pasa una referencia a quien llama.
- Los métodos de fábrica pueden crear o reutilizar objetos; un método de copia crea un objeto distinto.
- Los métodos de búsqueda pueden compartir entradas existentes y deben definir un resultado para la ausencia de coincidencias.
- Devolver un campo mutable puede permitir que quienes llaman eludan los métodos del propietario.
3Conecta objetos y listas
Un modelo más grande es una red de objetos: una persona tiene una fecha, una lista de reproducción tiene una lista y la lista contiene canciones. Cada conexión es una referencia con consecuencias para la compartición y el cambio. Crea esas conexiones de forma deliberada, delega el trabajo en el objeto que lo entiende y mantén las reglas de la colección detrás de métodos útiles. Un contenedor copiado puede proteger la pertenencia a la colección y seguir compartiendo todos los objetos que contiene.
3.1 Objetos con campos de tipo de referencia
La composición construye una clase utilizando campos cuyos tipos son otras clases. Expresa una relación de «tener»: una persona tiene una fecha de nacimiento; un curso tiene un profesor. El objeto externo almacena una referencia al objeto contenido.
Crea y conecta los objetos
Un constructor puede recibir una fecha existente:
class Person {
private String name;
private SimpleDate birthday;
Person(String name, SimpleDate birthday) {
this.name = name;
this.birthday = birthday;
}
boolean olderThan(Person other) {
return birthday.before(other.birthday);
}
public String toString() {
return name + ", born " + birthday;
}
}
La llamada new Person("Ana", new SimpleDate(14, 3, 2001)) crea primero una fecha y después da su referencia a la persona. Otra posibilidad es que un constructor de persona reciba el día, el mes y el año y cree la fecha dentro de su cuerpo. Con ese enfoque, quien llama no conserva una referencia a esa fecha recién creada.
Delega trabajo en el objeto contenido
La llamada birthday.before(other.birthday) pide al objeto de fecha que compare fechas. Person no necesita repetir las comparaciones de año, mes y día. La concatenación en toString() también delega: Java llama al toString() del objeto de fecha de nacimiento para obtener su texto.
Un objeto de fecha puede ayudar a calcular la edad. Empieza por el año actual menos el año de nacimiento. Después forma la fecha del cumpleaños de este año y resta uno si la fecha actual es anterior. Una persona nacida el 14 de marzo de 2001 tiene 24 años el 1 de enero de 2026: la diferencia de años es 25, pero el cumpleaños todavía no ha llegado. El 14 de marzo de 2026, el cumpleaños ya ha llegado y la edad es 25. Mantener la comparación de fechas en SimpleDate evita reproducir sus reglas en cada método de persona.
Decide si se comparte el objeto interno
El constructor anterior almacena su argumento directamente. Si dos personas reciben un mismo SimpleDate mutable, ambos campos de fecha de nacimiento apuntan a la misma fecha. Cambiar esa fecha afecta a ambas personas.
| Elección del constructor | Consecuencia para las referencias |
|---|---|
| Almacenar la referencia a la fecha recibida | Quien llama y la persona pueden compartir la fecha |
| Crear una fecha nueva a partir de los valores recibidos | La persona empieza con su propia fecha |
| Almacenar una fecha inmutable | Compartirla no puede cambiar sus valores de fecha |
Un constructor defensivo puede copiar el día, el mes y el año de la fecha recibida. Una fecha inmutable no tiene métodos que cambien su estado y mantiene fijos sus valores; los campos primitivos simples pueden ser final. Copiar al recibir la fecha importa incluso si un método de acceso devuelve después una copia, porque quien llamó originalmente todavía puede conservar el objeto recibido.
Maneja las conexiones opcionales
Un Course podría no tener profesor todavía. Si ese es un estado con sentido, teacherName() puede comprobar el campo y devolver «todavía no hay profesor». De lo contrario, exige un profesor en el momento de la construcción.
En course.getTeacher().getName(), cada llamada de instancia necesita un objeto: un curso nulo falla en getTeacher() y un profesor nulo falla en getName(). Un método en el curso puede mantener esa comprobación de ausencia en un solo lugar y evitar que cada llamada necesite conocer la misma cadena.
Puntos clave
- La composición conecta un objeto externo con otros objetos mediante campos.
- Delega las operaciones en el objeto propietario de los datos relevantes.
- Almacenar una referencia mutable recibida puede compartir el estado con quien llama.
- Las copias o los objetos internos inmutables pueden proteger un estado independiente.
- Los campos de objeto opcionales necesitan una política clara para manejar null.
3.2 Las listas contienen referencias a sus elementos
Un ArrayList<Person> es un objeto cuyos elementos contienen referencias a personas. La lista controla su secuencia de entradas; cada persona controla sus propios campos. Son tipos de cambio distintos.
Añade un objeto y vuelve a acceder a él
ArrayList<Person> people = new ArrayList<>();
people.add(ana);
people.get(0).growOlder();
Este código necesita import java.util.ArrayList; y una persona ana existente y no nula. add copia la referencia. La llamada a growOlder() llega a la misma persona que ana, así que el cambio de edad es visible por ambas vías. La lista nunca crea aquí una segunda persona.
Puedes crear un objeto al añadirlo: people.add(new Person("Lin", 1998));, utilizando un constructor de persona que acepte un nombre y un año de nacimiento. La lista proporciona entonces al programa una vía para acceder a esa nueva persona mediante get o un bucle. Los argumentos de tipo de las listas deben ser tipos de referencia, así que utiliza ArrayList<Integer> para números enteros.
Calcula a partir de objetos y selecciona un objeto
Supón que una lista contiene paquetes con pesos de 2,5 kg, 4,0 kg y 1,5 kg. Un bucle suma el peso de cada paquete:
double total = 0;
for (Parcel p : parcels) {
total += p.getWeight();
}
Las sumas acumuladas son 2,5, 6,5 y 8,0 kg. Seleccionar el paquete más pesado necesita una referencia además de una comparación:
Parcel heaviest = parcels.get(0);
for (Parcel p : parcels) {
if (p.getWeight() > heaviest.getWeight()) {
heaviest = p;
}
}
Esta selección espera una lista no vacía. El resultado es el objeto del paquete de 4,0 kg, así que quien llama también puede utilizar sus otros métodos. Para una lista vacía, decide cómo indicar la ausencia de resultado antes de leer el elemento 0.
Busca en todas las entradas
Una búsqueda por nombre en una clase que almacena people puede utilizar:
Person findByName(String name) {
for (Person p : people) {
if (p.getName().equals(name)) {
return p;
}
}
return null;
}
Esta versión espera personas no nulas con nombres no nulos. La primera coincidencia se devuelve de inmediato. El return null final va después del bucle: una comparación fallida con la primera persona no descarta coincidencias posteriores.
contains, indexOf y remove(Object) buscan utilizando equals. Si Person hereda una igualdad basada en la identidad, una persona recién creada no coincidirá con una existente solo porque coincidan sus campos.
Elimina la pertenencia sin destruir un objeto
Un objeto puede aparecer dos veces en una lista o en varias listas. Eliminar una entrada no destruye el objeto ni elimina otras referencias a él.
No elimines entradas directamente de una lista mientras la recorres con for-each. Puede provocar ConcurrentModificationException. Recopila primero los candidatos y después elimínalos:
ArrayList<Person> toRemove = new ArrayList<>();
for (Person p : people) {
if (p.getName().equals("Lin")) {
toRemove.add(p);
}
}
people.removeAll(toRemove);
El recorrido termina antes de que cambien los elementos de la lista. removeAll utiliza la igualdad, así que elimina las entradas iguales a los candidatos recopilados.
Puntos clave
- Añadir un objeto a una lista comparte una referencia a ese objeto.
- Un bucle puede calcular un total o conservar una referencia a un objeto seleccionado.
- Una búsqueda fallida solo devuelve un resultado después de comprobar todos los candidatos.
- Las operaciones de pertenencia a una lista dependen de
equals. - Eliminar una entrada deja intactas las demás referencias.
- Recopila primero los elementos que se van a eliminar y cambia la lista después del recorrido.
3.3 Una clase propietaria de una lista
Una clase puede mantener una lista en un campo privado y ofrecer operaciones que describan su propósito. Una lista de reproducción almacena canciones, las cuenta y calcula su duración. Quienes llaman no deberían tener que gestionar su lista directamente.
Inicializa y encapsula la colección
Esta lista de reproducción espera objetos Song no nulos con un método seconds():
import java.util.ArrayList;
class Playlist {
private String name;
private ArrayList<Song> songs;
Playlist(String name) {
this.name = name;
this.songs = new ArrayList<>();
}
void add(Song song) { songs.add(song); }
int count() { return songs.size(); }
int totalSeconds() {
int total = 0;
for (Song song : songs) {
total += song.seconds();
}
return total;
}
}
Sin new ArrayList<>(), el campo de referencia sigue siendo null; la primera llamada a add falla entonces. Una lista vacía ya es un objeto utilizable, con tamaño 0.
Para canciones que duran 210 s, 185 s y 247 s, count() devuelve 3. El total aumenta de 210 a 395 y después a 642 s. La división entera entre 60 da 10 minutos, con un resto de 42 segundos. Un método longest() puede seleccionar y devolver la canción de 247 s, utilizando el mismo patrón de selección de referencias que para el paquete más pesado. Define un resultado para la ausencia de canciones en una lista de reproducción vacía.
Protege la pertenencia mediante métodos
Mantener songs como privado bloquea el acceso directo al campo, pero return songs; expone esa misma lista. Quien llama podría llamar a clear() y eludir todas las reglas de la lista de reproducción.
Las operaciones específicas como add, count y totalSeconds mantienen la responsabilidad en la lista de reproducción. Su método add podría imponer un tamaño máximo o rechazar duplicados con songs.contains(song). Las comprobaciones de duplicados basadas en el contenido necesitan que equals de la clase de canción defina qué campos hacen que dos canciones sean iguales.
Cuando un constructor recibe una lista inicial, copia su estructura con this.songs = new ArrayList<>(initialSongs);. Almacenar el parámetro directamente permitiría que quien llama conserve un alias de la lista interna de la lista de reproducción.
Una lista nueva puede seguir compartiendo canciones
Una consulta puede devolver new ArrayList<>(songs). Esta es una copia superficial: un objeto de lista nuevo que contiene las referencias a las canciones existentes.
| Acción de quien llama sobre la copia devuelta | Efecto en la lista de reproducción |
|---|---|
| Eliminar una entrada | Las canciones de la lista de reproducción siguen siendo las mismas |
| Vaciar la copia | Las canciones de la lista de reproducción siguen siendo las mismas |
| Mutar una canción compartida | La lista de reproducción ve el estado modificado de esa canción |
Una consulta songsLongerThan(200) crea una lista de resultados nueva, recorre las canciones almacenadas y añade las que duran más de 200 s. Para las tres canciones anteriores devuelve las de 210 s y 247 s. Cuando los valores de las canciones también deban ser independientes, copia las canciones mutables o haz que Song sea inmutable.
Puntos clave
- Un objeto propietario, su lista y sus elementos son objetos separados.
- Inicializa la lista antes de utilizarla.
- Ofrece operaciones que hagan respetar las reglas de la colección del propietario.
- Copia las listas recibidas para evitar un alias externo de la lista interna.
- Una copia superficial de una lista comparte los objetos de sus elementos.
4Define qué hace que los objetos sean iguales
Dos objetos pueden describir lo mismo y seguir siendo objetos separados. Java te ofrece distintas comprobaciones para la identidad y el contenido, y las colecciones dependen de la elección que haga tu clase. Empieza por elegir los campos que definen la igualdad y después implementa equals y hashCode como pareja. Sus contratos explican por qué una lista puede encontrar un objeto igual, por qué se permite una colisión de hash y por qué cambiar una clave almacenada puede hacer que falle una búsqueda.
4.1 Identidad y contenido coincidente
La identidad pregunta si dos referencias llevan al mismo objeto. La igualdad de contenido pregunta si los objetos tienen los mismos datos relevantes, según las reglas de la clase. Dos objetos de libro distintos pueden describir el mismo título y año de publicación sin ser idénticos.
Elige la comparación adecuada
| Valores comparados | Significado de == | Significado de equals |
|---|---|---|
| Primitivos | Compara valores primitivos | Un valor primitivo no tiene métodos de instancia |
| Referencias | El mismo objeto, o ambas son null | La regla de igualdad de la clase |
| Cadenas | El mismo objeto de cadena | Contenido de caracteres coincidente |
Clase que hereda Object.equals | El mismo objeto | El mismo objeto |
Toda clase hereda equals(Object) de Object. Esa versión predeterminada comprueba la identidad. Java no inspecciona automáticamente todos los campos de una clase nueva.
String a = "java";
String b = new String("java");
System.out.println(a == b); // false
System.out.println(a.equals(b)); // true
new String crea un objeto distinto, mientras que String.equals compara sus caracteres. La comparación de texto distingue mayúsculas y minúsculas: "Java".equals("java") es falso. equalsIgnoreCase proporciona una comparación que no las distingue cuando esa es la regla buscada.
Por qué a veces parece que la identidad compara el contenido
Los literales de cadena iguales comparten un objeto de cadena, así que "java" == "java" es verdadero. Esto puede ocultar un error en una prueba pequeña. El texto obtenido de la entrada o construido mientras se ejecuta un programa no tiene por qué compartir ese objeto. Utiliza equals para comparar el contenido independientemente de cómo se haya obtenido el texto.
Las comparaciones de envoltorios tienen una trampa similar:
Integer a = 127, b = 127;
Integer c = 1000, d = 1000;
System.out.println(a == b); // true
System.out.println(c.equals(d)); // true
El empaquetado automático reutiliza objetos Integer para los valores de −128 a 127. Que valores mayores compartan o no un objeto no es una regla de comparación numérica portable. En una configuración estándar, los dos envoltorios de 1000 son distintos, pero se permite una caché mayor. Utiliza equals de los envoltorios para comparar sus valores o compara valores primitivos. Si un lado de == es un int, Java desempaqueta el envoltorio y compara valores numéricos; desempaquetar un envoltorio nulo falla.
Decide qué campos definen la igualdad
Para un catálogo de libros, el título y el año de publicación podrían definir la igualdad, mientras que se ignora la persona que tiene el libro prestado en ese momento. Esos campos seleccionados deben determinar el método de igualdad. Otro programa podría elegir en su lugar un identificador único; la regla útil depende de lo que representen sus objetos.
Un ArrayList utiliza equals en contains, indexOf y remove(Object). Supón que Book solo almacena campos y hereda equals. Añadir un new Book("Dune", 1965) no hace que contains(new Book("Dune", 1965)) sea verdadero: la segunda expresión crea un objeto diferente. Sobrescribir la igualdad por título y año permite que una búsqueda encuentre ese contenido igual.
HashSet y HashMap también utilizan hashCode para reducir el número de objetos candidatos antes de comprobar la igualdad. Por tanto, definir una igualdad de contenido también requiere una regla de código hash coherente.
Puntos clave
- Para referencias,
==comprueba la identidad, mientras queequalssigue la regla de la clase. Object.equalscompara la identidad hasta que se sobrescribe.- Utiliza
String.equalspara comparar el contenido de texto. - Los literales compartidos y la caché de envoltorios pueden hacer que las pruebas de identidad resulten engañosas.
- Elige deliberadamente los campos de igualdad; las colecciones dependen de esa elección.
4.2 Implementar equals correctamente
Un método de igualdad debe reemplazar el equals(Object) heredado y comparar los campos que definen el significado de tu objeto. La firma correcta importa tanto como las comparaciones que contiene.
Un patrón completo de igualdad
Esta clase utiliza el título y el año, permite un título nulo y mantiene fijos sus campos de igualdad:
import java.util.Objects;
final class Book {
private final String title;
private final int year;
Book(String title, int year) {
this.title = title;
this.year = year;
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!(other instanceof Book)) {
return false;
}
Book book = (Book) other;
return year == book.year
&& Objects.equals(title, book.title);
}
@Override
public int hashCode() {
return Objects.hash(title, year);
}
}
Lee equals en cuatro pasos. La comprobación de identidad acepta el mismo objeto de inmediato. La comprobación de tipo rechaza todo lo que no sea un libro, incluido null. La conversión explícita proporciona una variable Book mediante la cual se pueden leer los campos. La expresión final comprueba ambos campos de igualdad.
El tipo del parámetro es Object, no Book. Escribir equals(Book other) sobrecarga el nombre con otro método y deja sin cambios el equals(Object) heredado. Una llamada directa puede parecer que funciona mientras ArrayList.contains sigue utilizando la identidad. @Override hace que el compilador detecte ese error de firma.
Desde Java 16, la comprobación de tipo y la conversión explícita se pueden combinar: if (!(other instanceof Book book)) { return false; }. Una vez superada la comprobación, book está disponible para comparar los campos.
Adapta las comparaciones a los tipos de campo
Utiliza == para campos enteros, de carácter y booleanos. Utiliza el equals de un objeto para comparar su contenido. Para un campo de objeto que puede ser nulo, Objects.equals(a, b) devuelve verdadero cuando ambos son null, falso cuando solo uno es null y, en los demás casos, utiliza la igualdad.
Para campos double, Double.compare(x, other.x) == 0 proporciona un tratamiento coherente de los valores especiales: dos valores NaN se comparan como iguales y se distingue entre cero positivo y negativo. Acompaña esa elección de una regla hash compatible. El operador == aplicado directamente a valores en coma flotante trata NaN de otra manera.
Comprueba el contrato de igualdad
Para referencias no nulas, la igualdad debe ser:
- Reflexiva:
x.equals(x)es verdadero. - Simétrica:
x.equals(y)yy.equals(x)coinciden. - Transitiva: si
xes igual ayeyes igual az,xes igual az. - Consistente: las llamadas repetidas coinciden mientras los datos comparados no cambien.
- Falsa para null:
x.equals(null)es falso.
Dos libros de «Dune», 1965, se comparan como iguales; cambiar uno de los años a 1984 hace que sean distintos. Un libro no debe compararse como igual a una cadena que contiene su título: la cadena rechazaría el libro y rompería la simetría.
Algunas implementaciones utilizan other == null || getClass() != other.getClass() para comprobar el tipo. Esta expresión rechaza clases en tiempo de ejecución diferentes. instanceof puede aceptar subclases; para la clase final anterior no existe ninguna subclase y ambas comprobaciones coinciden.
Puntos clave
- Sobrescribe con
public boolean equals(Object other)y@Override. - Comprueba la identidad y el tipo, convierte la referencia y compara los campos de igualdad.
- Utiliza comparaciones de objetos seguras ante null cuando los campos puedan estar ausentes.
- Respeta la reflexividad, la simetría, la transitividad, la consistencia y el rechazo de null.
equals(Book)sobrecarga el método y no reemplazaequals(Object).- La elección de la comprobación de tipo importa cuando se permiten subclases.
4.3 Códigos hash coherentes y claves estables
Un código hash es un número entero que se utiliza para reducir el ámbito de una búsqueda. HashSet y HashMap lo utilizan para encontrar objetos candidatos y después utilizan equals para confirmar una coincidencia. No es un identificador único de objeto.
El contrato funciona en un solo sentido
Los objetos iguales deben devolver el mismo código hash. Los objetos distintos también pueden compartir un código; eso es una colisión. Una colisión no los hace iguales, porque la igualdad se sigue comprobando.
Un código hash debe mantenerse consistente mientras la información utilizada por equals no cambie. En una implementación correcta, códigos hash diferentes descartan la igualdad, pero códigos hash iguales no la establecen.
Cuando una clase sobrescribe equals pero hereda Object.hashCode, los objetos separados que son iguales suelen recibir códigos diferentes. Entonces un conjunto puede conservar ambos y una búsqueda en un mapa con una clave nueva pero igual puede fallar. El fallo exacto no está garantizado, porque los códigos heredados de objetos diferentes pueden coincidir. Corrige el contrato en lugar de depender de un fallo concreto de búsqueda.
Calcula a partir de los campos de igualdad
Para la clase de libro que compara el título y el año:
@Override
public int hashCode() {
return Objects.hash(title, year);
}
Importa java.util.Objects. Los mismos valores de campo producen ahora el mismo resultado. Un conjunto de libros almacena una entrada después de añadir dos libros iguales, y un mapa puede recuperar un valor utilizando como clave un libro nuevo pero igual.
Objects.hash combina los códigos hash de los campos utilizando 31. Para «Dune», el código hash de la cadena es 2 141 736. Al combinar ese título y el año 1965 se multiplica 31 por la suma de 31 y 2 141 736, y después se suma 1965: el resultado es 66 396 742.
El cálculo del hash de una cadena también utiliza 31. Para «ab», los códigos de carácter 97 y 98 dan 97 multiplicado por 31 más 98, es decir, 3105. Los cálculos más largos pueden superar el rango de int y desbordarse, así que un código hash negativo es normal.
| Regla hash | Consecuencia para el contrato |
|---|---|
| Utilizar los mismos campos de igualdad | Los objetos iguales reciben códigos coincidentes |
| Añadir un campo que la igualdad ignora | Los objetos iguales pueden recibir códigos diferentes |
| Utilizar menos campos de igualdad | Es válido, pero puede crear más colisiones |
| Devolver siempre 1 | Es válido, pero reduce el rendimiento de búsqueda |
No cambies la clave mientras esté almacenada
Supón que el año de un libro es mutable y afecta a su código hash. Después de insertarlo en un conjunto, cambiar el año puede hacer que contains(book) falle incluso con esa misma referencia: la búsqueda utiliza ahora un código diferente. El conjunto puede seguir indicando un tamaño de 1.
Mantén estables los campos de igualdad mientras un objeto sea un elemento de un conjunto o una clave de un mapa. Si deben cambiar, elimina el objeto mientras todavía se apliquen sus valores antiguos, cámbialo y vuelve a insertarlo. Intentar eliminarlo solo después del cambio puede provocar el mismo problema de búsqueda.
La igualdad generada sigue necesitando una buena elección de datos
Java 16 y posteriores admiten registros, como record Book(String title, int year) {}. Un registro genera los métodos de acceso title() y year(), además de la igualdad, el cálculo del hash y una representación textual legible a partir de todos sus componentes. Los campos de los componentes son final. Este ejemplo es un valor inmutable útil porque sus componentes son una cadena y un entero; un registro que contenga una lista mutable seguiría compartiendo el estado de esa lista.
Para las clases ordinarias, un IDE puede generar equals y hashCode conjuntamente. Selecciona los campos que definen la igualdad deseada y mantén ambos métodos coherentes cuando cambie esa elección.
Puntos clave
- Los objetos iguales requieren códigos hash iguales; se permiten colisiones entre objetos distintos.
- Sobrescribe
hashCodesiempre que sobrescribasequals. - Calcula el hash solo a partir de datos compatibles con la regla de igualdad.
- Mantén estables los campos relevantes para el hash mientras el objeto pertenezca a un conjunto o se utilice como clave de mapa.
- Elimina una clave antes de cambiarla y después vuelve a insertarla.
- Los registros generan la igualdad a partir de sus componentes; los componentes mutables siguen requiriendo cuidado.
5Asigna una tarea clara a cada clase
Un programa de texto realiza dos tipos de trabajo: comunicarse con un usuario y gestionar los datos y las reglas. Mantener esas tareas en clases separadas facilita comprobar y reutilizar las reglas. Conecta las clases mediante parámetros de constructor y pequeños métodos públicos. Después añade una función primero donde corresponde su regla, verifica el resultado con valores conocidos y proporciona a la interfaz un comando que la llame y muestre la respuesta.
5.1 Entrada, salida y reglas del programa
La interfaz de usuario lee la entrada y muestra la salida. La lógica del programa almacena datos y aplica reglas. Un diseño separado pasa valores a la lógica y recibe resultados; sus clases de lógica no utilizan Scanner ni System.out.
Conecta los objetos en main
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
TemperatureLog log = new TemperatureLog();
UserInterface ui = new UserInterface(scanner, log);
ui.start();
}
Con import java.util.Scanner;, esta conexión crea un lector de entrada, un registro y una interfaz. Se coloca dentro de la clase de entrada del programa. UserInterface almacena los objetos proporcionados en campos para que todos los comandos trabajen con el mismo registro.
| Trabajo | Clase responsable |
|---|---|
| Leer el texto de un comando | Interfaz de usuario |
| Almacenar una lectura | Registro de temperaturas |
| Calcular la media o el máximo | Registro de temperaturas |
| Redondear e imprimir un resultado | Interfaz de usuario |
| Rechazar una temperatura imposible | Registro de temperaturas |
| Explicar al usuario una entrada no válida | Interfaz de usuario |
Devuelve valores que cualquier llamada pueda utilizar
Un registro podría proporcionar add(double celsius), count(), average() y max(). Las lecturas de 18,5, 21,0 y 19,5 °C tienen una suma de 59,0, un recuento de 3, un máximo de 21,0 y una media de aproximadamente 19,6667 °C. La interfaz de usuario puede redondear la media devuelta con Math.round(average * 10) / 10.0 y mostrar 19,7 °C.
Devolver el número facilita las comprobaciones directas. Un método llamado printAverage() que solo imprime vincula un cálculo a la salida por consola, lo que dificulta su reutilización.
Para un bucle de comandos sencillo, supón que add acepta temperaturas válidas y que count devuelve su número:
class UserInterface {
private Scanner scanner;
private TemperatureLog log;
UserInterface(Scanner scanner, TemperatureLog log) {
this.scanner = scanner;
this.log = log;
}
void start() {
while (true) {
String command = scanner.nextLine();
if ("quit".equals(command)) {
break;
}
if ("count".equals(command)) {
System.out.println(log.count());
}
}
}
}
El bucle maneja la interacción. Pide un resultado al registro y lo imprime sin saber cómo se almacenan las lecturas. Un comando de media debe manejar un registro vacío según el resultado documentado de la lógica o comprobar count() antes de llamar a un método que requiere lecturas.
Valida en el límite adecuado
La lógica puede hacer que add devuelva falso sin almacenar una lectura inferior a −273,15 °C. Así, todas las llamadas reciben la misma regla de datos. La interfaz de usuario decide cómo comunicar ese rechazo y volver a preguntar. Convertir una entrada no numérica en un mensaje de error útil también es tarea de la interfaz.
Utiliza un Scanner para System.in y pásalo a quienes lean entrada. Varios escáneres pueden competir por la entrada almacenada en búfer, y cerrar uno cierra el flujo de entrada subyacente. Mantén el tiempo de vida del escáner compartido bajo el control del código propietario de la entrada del programa.
Más adelante, la lógica puede admitir otra interfaz mediante los mismos métodos públicos. La reutilización surge de mantener los cálculos y las reglas de validez disponibles como resultados de métodos, independientemente de un bucle de comandos concreto.
Puntos clave
- La interfaz lee y muestra; la lógica almacena datos, hace respetar reglas y devuelve resultados.
mainconstruye los objetos compartidos e inicia la interfaz.- Pasa un escáner y el objeto de lógica al constructor de la interfaz.
- Las reglas de datos corresponden a la lógica; los mensajes de entrada y el formato de presentación corresponden a la interfaz.
- Devolver resultados facilita las comprobaciones directas y la reutilización con otras interfaces.
5.2 Añade una función y comprueba su comportamiento
Un programa separado puede crecer mediante pequeños cambios sucesivos. Proporciona a la lógica un método útil, compruébalo directamente y después conecta un comando que recoja su entrada y muestre su resultado.
Añade una consulta a la lógica
Un TemperatureLog que almacena un ArrayList<Double> readings inicializado puede contar las lecturas superiores a un límite:
int countAbove(double limit) {
int count = 0;
for (double reading : readings) {
if (reading > limit) {
count++;
}
}
return count;
}
La comparación estricta excluye las lecturas exactamente iguales al límite. Para 18,5, 21,0, 19,5 y 23,0 °C, countAbove(20.0) devuelve 2. Coloca la comparación en el registro para que todas las llamadas obtengan la misma regla.
El comando de la interfaz lee un límite, llama a log.countAbove(limit) e imprime el recuento. Si cada comando se vuelve largo, divide start() en métodos auxiliares privados como addReading() y showStatistics(). Los métodos auxiliares organizan la interacción; no trasladan las reglas de datos fuera del registro.
Comprueba sin escribir comandos
Crea el objeto de lógica y llama a sus métodos con valores cuyas respuestas conozcas:
TemperatureLog log = new TemperatureLog();
log.add(18.5);
log.add(21.0);
log.add(19.5);
log.add(23.0);
System.out.println(log.count() == 4);
System.out.println(log.average() == 20.5);
System.out.println(log.max() == 23.0);
System.out.println(log.countAbove(20.0) == 2);
La suma es 82,0, así que la media esperada es 20,5. Estos valores concretos tienen representaciones binarias exactas, lo que hace que estas comprobaciones directas de igualdad sean adecuadas para el ejemplo. Comprueba también un registro vacío según su comportamiento documentado, un límite que coincida con una lectura y el rechazo de una entrada no válida sin cambiar el recuento. Las herramientas de pruebas automatizadas se basan en la misma idea de una entrada conocida y un resultado esperado.
Cambia el almacenamiento solo si sigue admitiendo las consultas
Una interfaz que solo llama a average, count y max no necesita saber si el registro contiene una lista. Los campos de suma acumulada, recuento y máximo pueden responder a esas tres consultas sin conservar todas las lecturas.
Una consulta countAbove(limit) con cualquier límite necesita más información. Las muestras 18, 22, 23 y 20, 20, 23 tienen la misma suma 63, el mismo recuento 3 y el mismo máximo 23, pero sus recuentos por encima de 20 son 2 y 1. Conservar solo esos tres valores agregados hace perder la distinción. Los métodos públicos describen qué debe seguir funcionando después de un cambio de almacenamiento.
Combina las ideas de diseño
Un recetario puede ser propietario de una lista privada de recetas. Cada receta puede contener ingredientes medidos en gramos y un tiempo de cocción en minutos. La igualdad puede permitir comprobar duplicados, mientras una interfaz añade, enumera y busca recetas mediante los métodos del recetario. Una lista de resultados copiada puede proteger la pertenencia y seguir compartiendo objetos de receta; decide si quienes llaman pueden cambiar esas recetas.
Trabaja en pasos pequeños: implementa un método, compruébalo y después añade su comando. Si un cambio estropea un resultado existente, vuelve a un estado funcional y aísla ese cambio antes de añadir otra función.
Puntos clave
- Añade y comprueba el método de lógica antes de conectar su comando en la interfaz.
- Utiliza entradas conocidas y resultados esperados para comprobar la lógica directamente.
- Incluye casos límite como una colección vacía o un valor exactamente igual al umbral.
- Un cambio de almacenamiento debe conservar información suficiente para todas las consultas públicas.
Ejemplos de Objetos y referencias en Java
1Sigue las variables sin perder de vista los objetos
15 pasos
- 1IntroducciónDibuja variables y objetos
Una variable puede contener un número directamente o una referencia que lleva a un objeto. Esa diferencia explica por qué copiar una variable a veces proporciona datos independientes y otras veces comparte un cambio.
Dibujaremos las variables por separado de los objetos y después seguiremos cada sentencia. Distingue el número de flechas del número de objetos: varias flechas pueden terminar en un mismo objeto.
- 2ExplicaciónUn valor puede ser un dato o una referencia
Java tiene ocho tipos primitivos:
byte,short,int,long,float,double,charyboolean. Sus variables contienen datos directamente. Las clases y los arreglos son tipos de referencia; sus variables contienen referencias onull.En
int count = 13;, el valor es 13. EnTally t = new Tally(13);, el valor detes una referencia a un objeto que contiene sus propios datos. - 3EjemploCopia un valor primitivo
Sigue las asignaciones en orden:
int start = 13; int next = start; next += 2;La segunda sentencia copia 13 en
next. La tercera lee esa copia y la sustituye por 15.startsigue siendo 13 porque ninguna sentencia le asigna un valor nuevo. Dibuja dos cajas que contengan números separados; no interviene ningún objeto compartido. - 4EjemploCopia un valor de referencia
Supón que
Tallyalmacena un entero,addOne()lo incrementa yvalue()lo lee.Tally first = new Tally(13); Tally next = first; next.addOne();Un
newcrea un contador. La asignación da anextotra vía para acceder a él. Llamar aaddOne()cambia su valor almacenado a 14, quefirst.value()también lee ahora. La segunda variable no es un segundo contador. - 5DesafíoSepara un objeto de su alias
Con los mismos métodos de
Tally, ¿qué devuelvea.value()?Tally a = new Tally(20); Tally b = new Tally(a.value()); b.addOne();---reveal---
Devuelve 20. El segundo
newcrea un contador independiente inicializado con 20; su incremento solo cambia ese segundo objeto a 21. Leer el valor de un campo y utilizarlo en un constructor es distinto de copiar la referencia al objeto original. - 6ConexiónAlgunos objetos compartidos no se pueden mutar
Las cadenas y los envoltorios como
Integerson tipos de referencia. El empaquetado automático convierte valores primitivos en envoltorios para usos comoArrayList<Integer>.Las cadenas son inmutables: si
leftyrightcomparten «cedar», asignarright = right.toUpperCase()no cambia los caracteres originales.leftsigue siendo «cedar». El resultado en mayúsculas devuelto se asigna solo aright. - 7AnalogíaUtiliza flechas para seguir los cambios
Piensa en cada variable como una tarjeta que muestra una ruta hacia un objeto. Copiar la tarjeta proporciona otra ruta al mismo destino. Escribir una ruta diferente en una tarjeta no cambia las demás.
En un diagrama de memoria, dibuja una flecha desde cada variable de referencia hasta su objeto. Un método modificador cambia los valores de los campos del objeto; una asignación cambia la flecha de una variable. Mantén visualmente separados esos dos cambios.
- 8EjemploCambia primero el estado compartido
Supón que
Cratealmacena una masa en kilogramos, con los métodossetMassymass.Crate a = new Crate(3); Crate b = a; b.setMass(6);Dibuja
aybapuntando a una caja. El método modificador actualiza la masa dentro de esa caja de 3 a 6. Tantoa.mass()comob.mass()devuelven ahora 6; el método modificador no ha redirigido ninguna de las dos variables. - 9EjemploDespués redirige una variable
Continúa el seguimiento de la caja:
b = new Crate(9);Crea un segundo objeto con masa 9 y redirige solo
b. La variableasigue apuntando a la caja original, cuya masa sigue siendo 6. Ahora hay dos objetos de caja alcanzables. Esto es una reasignación, así que no se restablece la masa almacenada en la primera caja. - 10DesafíoConserva la ruta restante
Continúa con
Crate c = a; a = null;. ¿A través de qué variable sigue siendo alcanzable la caja de 6 kg y queda disponible para la recolección?---reveal---
Sigue siendo alcanzable a través de
c, así que no queda disponible para la recolección. La asignación anterior copió la referencia enc; asignar null aaeliminó solo una ruta. La caja distinta de 9 kg sigue siendo alcanzable a través deb. - 11Idea claveUn campo o una lista pueden conservar una ruta
Cuando desaparece la última ruta utilizable hacia un objeto, este se vuelve inalcanzable y queda disponible para la recolección de basura. Java decide el momento de la recolección; perder una referencia no garantiza una recuperación inmediata de la memoria.
Una ruta puede pasar por otro objeto o una lista. Antes de considerar inalcanzable un objeto, comprueba esos enlaces además de las variables locales. Una entrada de lista puede conservar un objeto después de que su variable original haya salido de ámbito.
- 12ExplicaciónNull significa que falta el destino
nullno es un objeto vacío. UnStringnulo no tiene ningún objeto sobre el que llamar alength(), mientras que""es una cadena utilizable de longitud cero. Los campos de referencia tienen null como valor predeterminado; las variables locales necesitan una asignación antes de leerse.Los envoltorios también pueden ser null.
Integer n = null; int count = n;lanzaNullPointerExceptioncuando el desempaquetado intenta obtener un valor primitivo del envoltorio ausente. - 13ConsejoLee el error en la llamada que falla
Ante un error de null, localiza el número de línea y después la llamada de instancia, el acceso a un campo o el desempaquetado en esa línea. Los mensajes informativos, activados de forma predeterminada desde Java 15, pueden identificar el campo ausente.
Sigue el rastro hacia atrás hasta el constructor, el resultado de búsqueda o la asignación que debería haber proporcionado un objeto. Ver «null» en el texto impreso es una pista: imprimir una cadena nula puede funcionar aunque una llamada de instancia posterior falle.
- 14DesafíoProtege el seguimiento combinado
Una búsqueda asigna
Crate found = findCrate();y devuelve una caja o null. ¿Qué condición comprueba de forma segura si la masa supera los 5 kg? ¿Por qué deben estar las comprobaciones en ese orden?---reveal---
Utiliza
found != null && found.mass() > 5. La comprobación de null se ejecuta primero y&&omitemass()cuando no existe ninguna caja. Si se colocara primero la llamada al método, se accedería a un miembro a través de null antes de llegar a la comprobación. Una referencia devuelta sigue las mismas reglas que los alias analizados antes. - 15RepasoComprueba un seguimiento de referencias
Para un nuevo seguimiento, marca cada
new, copia el valor en cada asignación y actualiza solo el campo al que afecta una mutación. Después sigue todas las rutas restantes antes de decidir que un objeto es inalcanzable.En una línea que falla, identifica la operación exacta que necesita un objeto. Comprueba null antes de esa operación o inicializa el campo con el objeto que el programa necesita realmente, como una lista vacía.
2Sigue un objeto al entrar en un método y al salir de él
12 pasos
Guarda esta unidad para seguir cada Lección Interactiva paso a paso.
- 1IntroducciónSigue una llamada y su resultado
- 2ExplicaciónUn parámetro recibe su propio valor
- 3EjemploMuta el objeto compartido
- 4EjemploReasigna la ruta local
- 5Desafío¿Puede un método intercambiar las variables de quien llama?
- 6ConexiónCompara el objeto receptor con su argumento
- 7ExplicaciónEl tipo de retorno no promete una copia
- 8EjemploConstruye un objeto independiente para devolverlo
- 9DesafíoEncuentra una entrada que se pueda actualizar
- 10ConsejoUn método de acceso puede revelar un alias oculto
- 11DesafíoCombina un retorno con una llamada que recibe un parámetro
- 12RepasoExamina un método en ambos límites
3Construye y protege una colección de objetos
16 pasos
Guarda esta unidad para seguir cada Lección Interactiva paso a paso.
- 1IntroducciónSepara las capas de estado
- 2ExplicaciónModela una relación de pertenencia
- 3EjemploDelega la regla de fechas
- 4DesafíoProtege la fecha recibida
- 5ExplicaciónUna entrada es otra ruta
- 6EjemploCalcula un total mediante un bucle
- 7EjemploConserva el objeto seleccionado
- 8DesafíoDevuelve la ausencia en el momento adecuado
- 9ConsejoSepara la eliminación del recorrido
- 10ExplicaciónProporciona al propietario una lista inicializada
- 11EjemploOfrece operaciones útiles
- 12DesafíoDistingue una copia de lista de sus elementos
- 13ConexiónProtege ambas direcciones
- 14EjemploFiltra en un resultado nuevo
- 15DesafíoSigue una copia a través de otro objeto
- 16RepasoRevisa los límites de la colección
4Construye una igualdad fiable para las colecciones
16 pasos
Guarda esta unidad para seguir cada Lección Interactiva paso a paso.
- 1IntroducciónDecide qué significa una coincidencia
- 2EjemploTexto igual en objetos separados
- 3ConsejoNo dejes que la caché elija la regla
- 4DesafíoDota a una clase de igualdad de contenido
- 5ExplicaciónElige los campos y la firma
- 6EjemploAcepta la identidad y rechaza tipos incorrectos
- 7EjemploCompara los campos seleccionados
- 8DesafíoDetecta un contrato incumplido
- 9Idea claveComprueba más de una pareja coincidente
- 10ExplicaciónLos códigos hash reducen los candidatos
- 11EjemploGenera el hash coherente
- 12DesafíoDistingue una colisión de la igualdad
- 13EjemploObserva qué ocurre al cambiar una clave
- 14ConexiónUn registro sencillo proporciona ambos métodos
- 15DesafíoSigue el conjunto con la igualdad generada
- 16RepasoRevisa una clase de valor
5Añade una función sin mezclar la entrada y las reglas
11 pasos
Guarda esta unidad para seguir cada Lección Interactiva paso a paso.
- 1IntroducciónConecta las reglas con una conversación
- 2ExplicaciónNombra las dos tareas
- 3EjemploConecta un conjunto de objetos compartidos
- 4EjemploDevuelve el resultado para mostrarlo
- 5DesafíoColoca el rechazo en la clase adecuada
- 6ExplicaciónProporciona a la consulta nueva un método de lógica
- 7EjemploComprueba con entradas conocidas
- 8DesafíoEvalúa la propuesta de almacenamiento
- 9ConsejoAmplía el programa un paso comprobado a la vez
- 10DesafíoPasa a una interfaz diferente
- 11RepasoRevisa una función nueva en su límite
Ejercicios de Objetos y referencias en Java
Set 1: Aplica la compartición, las colecciones y los límites del programa10 Preguntas de Práctica
Pregunta 1 de 10
¿Qué ocurre con a después de la última línea?
String a = "river";
String b = a;
b = b.toUpperCase();
Mostrar respuestaOcultar respuesta
b, mientras que a conserva «river». Esperar que a muestre mayúsculas supone tratar una operación de cadena como una mutación del objeto original.Pregunta 2 de 10
¿Qué ocurre cuando se ejecuta este código?
Integer reading = null;
int value = reading;
Mostrar respuestaOcultar respuesta
Integer es un tipo de referencia. La segunda línea necesita desempaquetar un objeto real y falla porque la referencia es null. Los campos primitivos pueden tener cero como valor predeterminado, pero desempaquetar una referencia nula no proporciona ese valor.Pregunta 3 de 10
Meter almacena un entero y tiene set(int) y get(). ¿Qué devuelve m.get() después de la llamada?
static void adjust(Meter x) {
x.set(14);
x = new Meter(22);
x.set(25);
}
Meter m = new Meter(9);
adjust(m);
Mostrar respuestaOcultar respuesta
m. Reasignar después el parámetro x crea un medidor diferente, y su valor posterior de 25 pertenece a ese objeto nuevo. Quien llama conserva su referencia original, así que el resultado visible es la mutación anterior, no el valor local final.Pregunta 4 de 10
Bin.items() devuelve directamente su lista privada de objetos Part mutables. Quien llama debe poder inspeccionar y modificar la lista devuelta y sus piezas sin cambiar las entradas del contenedor ni el estado de las piezas almacenadas. ¿Qué debe contener el resultado?Mostrar respuestaOcultar respuesta
Pregunta 5 de 10
return null en un else después de la comprobación del nombre dentro del bucle. ¿Dónde debe ir ese retorno?Mostrar respuestaOcultar respuesta
else detiene la búsqueda en «Iris» y nunca llega a la entrada almacenada «Sana».Pregunta 6 de 10
copy se crea con new ArrayList<>(original). Ambas listas contienen inicialmente el mismo Clip c mutable. Quien llama cambia c mediante copy.get(0) y después vacía copy. ¿Qué queda en original?Mostrar respuestaOcultar respuesta
original; vaciar después copy solo afecta a las entradas de la lista copiada. Una copia superficial no inmoviliza ni duplica los objetos de sus elementos.Pregunta 7 de 10
Badge tiene campos y un constructor, pero no sobrescribe equals. ¿Cuál es el resultado?
new Badge("Q7").equals(new Badge("Q7"))
Mostrar respuestaOcultar respuesta
new crean insignias distintas. La clase hereda Object.equals, que comprueba la identidad, así que devuelve false a pesar de que los datos coinciden. El contenido de los campos puede definir la igualdad, incluidos los campos de cadena, pero una clase normal debe implementar esa regla explícitamente.Pregunta 8 de 10
Tag.equals compara solo code. Tag.hashCode utiliza tanto code como el color mutable. Dos etiquetas tienen el mismo código, pero colores diferentes. Una persona que desarrolla el programa propone devolver un código hash constante para corregir el comportamiento. ¿Cómo deben evaluarse las dos reglas?Mostrar respuestaOcultar respuesta
code.Pregunta 9 de 10
Mostrar respuestaOcultar respuesta
Pregunta 10 de 10
count(), average() y max(). Su desarrollador añade countAbove(limit) para cualquier límite proporcionado por quien llama y después sustituye todas las lecturas por solo una suma, un recuento y un máximo. ¿Por qué la nueva consulta ya no puede implementarse correctamente para todos los registros?Mostrar respuestaOcultar respuesta
Set 2: Sigue valores, llamadas y comparaciones sencillas10 Preguntas de Práctica
Guarda esta unidad para ver todas las respuestas y practicar cada set.
Pregunta 1 de 10
Object?Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 2 de 10
¿Qué contiene original después de este código?
int original = 8;
int duplicate = original;
duplicate = 11;
Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 3 de 10
Lamp tiene un campo booleano inicialmente false; turnOn() lo establece en true e isOn() lo lee. ¿Qué devuelven a.isOn() y b.isOn(), en ese orden?
Lamp a = new Lamp();
Lamp b = a;
b.turnOn();
b = new Lamp();
Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 4 de 10
label puede ser null. ¿Qué condición comprueba de forma segura que tenga al menos tres caracteres?Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 5 de 10
Robot r no nulo. Solo ejecuta r = new Robot();. ¿Qué ocurre con la variable de quien llama que se pasó como argumento?Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 6 de 10
Gauge.copy() devuelve new Gauge(value). Un medidor empieza en 6 y raise() suma 1. ¿Qué valores indican first y second?
Gauge first = new Gauge(6);
Gauge second = first.copy();
second.raise();
Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 7 de 10
Device almacena directamente su argumento mutable Settings. Su método de acceso devuelve una copia de la configuración almacenada. Dos dispositivos reciben el mismo objeto de configuración y quien llama cambia después ese objeto original. ¿Qué cambio de diseño hace que cada dispositivo sea independiente de ese cambio externo y del otro dispositivo?Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 8 de 10
Sensor s no nulo a dos listas. Una lista elimina su entrada para s. ¿Qué ocurre con el sensor de la otra lista?Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 9 de 10
¿Qué devuelven estas dos expresiones?
String word = new String("maple");
word == "maple"
word.equals("maple")
Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 10 de 10
Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Set 3: Corrige problemas de alias, igualdad y diseño de colecciones10 Preguntas de Práctica
Guarda esta unidad para ver todas las respuestas y practicar cada set.
Pregunta 1 de 10
Solo existen las referencias mostradas y todas las variables siguen en uso. Cell tiene datos de valor mutables. ¿Cuántos objetos de celda siguen siendo alcanzables?
Cell a = new Cell(3);
Cell b = new Cell(8);
Cell c = a;
a = b;
b = null;
c = a;
Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 2 de 10
private ArrayList<Note> notes;. Su constructor nunca asigna un valor a notes, y add llama inmediatamente a notes.add(note). ¿Qué cambio prepara una instancia recién construida para almacenar notas?Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 3 de 10
Box tiene un campo entero que se lee mediante get() y se cambia mediante set(int). ¿Qué valores indican left.get() y right.get() después de esta llamada?
static void revise(Box x, Box y) {
x.set(10);
x = y;
x.set(16);
}
Box left = new Box(4);
Box right = new Box(7);
revise(left, right);
Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 4 de 10
Member almacena un SimpleDate joined no nulo y necesita decidir si se incorporó antes que otro miembro. ¿Dónde debe implementarse la comparación de año, mes y día?Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 5 de 10
Queue almacena directamente un ArrayList<Job> recibido, pero su método de acceso devuelve una lista superficial nueva. Quien llama vacía la lista de entrada original. La cola debe conservar sus entradas originales, mientras que los objetos de trabajo pueden seguir compartidos. ¿Qué reparación cumple ese requisito?Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 6 de 10
Token declara public boolean equals(Token other) sin @Override. Las comparaciones directas parecen correctas, pero un ArrayList<Token> no encuentra un token nuevo con campos coincidentes. ¿Cuál es el problema de la firma?Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 7 de 10
Key.equals y hashCode utilizan ambos serial. Se añade una clave mutable a un HashSet, después cambia su número de serie y falla una búsqueda. ¿Qué secuencia permite correctamente un cambio previsto del número de serie conservando la pertenencia al conjunto?Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 8 de 10
Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 9 de 10
countAbove(limit) cuenta las lecturas estrictamente superiores a su argumento. Un registro contiene 11,5, 15,0, 15,0 y 18,5 °C. ¿Qué debe esperar una comprobación directa de countAbove(15.0)?Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 10 de 10
Mark.equals compara code. Una lista contiene marcas con los códigos «A», «B» y «A»; las dos marcas A son objetos separados. Una segunda lista contiene solo una referencia a la primera marca A. ¿Qué queda después de marks.removeAll(second)?Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Set 4: Razona sobre copias, contratos y cambios de diseño10 Preguntas de Práctica
Guarda esta unidad para ver todas las respuestas y practicar cada set.
Pregunta 1 de 10
Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 2 de 10
Node contiene datos mutables. Solo existen las referencias mostradas. En la última línea, ¿cuántos objetos de nodo son alcanzables mediante first o la lista?
Node first = new Node();
ArrayList<Node> nodes = new ArrayList<>();
nodes.add(first);
first = new Node();
nodes.add(first);
nodes.remove(0);
Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 3 de 10
Locker.find(id) devuelve actualmente un Item mutable recién construido con los mismos valores de campo que el elemento almacenado que coincide. Quienes llaman utilizan el resultado para actualizar la etiqueta del elemento almacenado. ¿Qué comportamiento debe proporcionar el método para que esas actualizaciones afecten al casillero?Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 4 de 10
Schedule almacenan un campo SimpleDate mutable compartido. Una persona que desarrolla el programa hace final solo los campos de las planificaciones. ¿Qué ocurre si el objeto de fecha original cambia mediante su método modificador?Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 5 de 10
Document mutables. matching(prefix) construye una lista nueva que contiene los documentos existentes que coinciden. Quien llama elimina un resultado y modifica el título de otro. ¿Qué efecto puede tener esto en la biblioteca?Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 6 de 10
x e y, ¿qué establece x == y cuando es true?Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 7 de 10
Score.equals(Object) acepta otra puntuación cuando la diferencia absoluta entre sus valores enteros es como máximo 2. Las puntuaciones x, y y z contienen 10, 12 y 14. Ningún valor cambia. ¿Qué problema del contrato de igualdad aparece entre estos tres objetos?Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 8 de 10
record Stamp(String code, int batch) {} utiliza igualdad y cálculo de hash generados. Un conjunto recibe new Stamp("R", 4), otro new Stamp("R", 4) separado y después new Stamp("R", 5). ¿Cuántas entradas contiene?Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 9 de 10
Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 10 de 10
Double.compare(level, other.level) == 0 en equals. Un método hash propuesto siempre devuelve 1. Otro devuelve códigos diferentes para objetos distintos cuyos niveles son ambos NaN. ¿Cómo se comparan estas propuestas con el contrato de igualdad?Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
¿Te atascas con una pregunta tuya? Ayuda con Tareas
Términos Clave
A
Alcanzabilidad
Alias
Box second = first;, second es un alias y su método modificador cambia la caja a la que llega first.Argumento
gate.allowIn(ana), el argumento es el valor de referencia leído de ana, que el parámetro copia.B
Bucle for-each
getWeight() sin cambiar las entradas de la lista.C
Caché de objetos envoltorio
equals en lugar de depender de == para envoltorios.Campo de referencia final
add.Campo de tipo de referencia
birthday de Person puede llevar a un SimpleDate que realiza comparaciones de fechas.Campos que determinan la igualdad
Clase ArrayList
ArrayList<Parcel> puede contener tres referencias a paquetes y permitir que un bucle sume sus pesos en kilogramos.Clase envoltorio
Integer proporciona el tipo de elemento para ArrayList<Integer>; el tipo primitivo int no puede hacerlo.Clase HashSet
HashSet<Book> conserva una entrada para dos libros iguales separados cuando su igualdad y sus códigos hash coinciden.Clave de HashMap
Código hash
Objects.hash(title, year) cuando esos campos definen la igualdad.Colisión de hash
equals los distingue correctamente.Composición
Comprobación directa
Consistencia
Consulta sobre una colección
songsLongerThan(200) construye una lista nueva y conserva las referencias a las canciones coincidentes.Contrato público
Copia defensiva
Copia superficial
D
Delegación
olderThan llama a birthday.before(other.birthday) para comparar fechas.Desempaquetado
NullPointerException si el envoltorio es nulo.int count = boxed;, así que boxed debe hacer referencia a un objeto Integer real.E
Empaquetado automático
numbers.add(12) pase un valor Integer cuando numbers es un ArrayList<Integer>.Encapsulación
Evaluación en cortocircuito
&& cuando su lado izquierdo es falso, lo que permite que una comprobación previa proteja una llamada posterior.name != null && name.length() > 0 omita la llamada al método cuando el valor es null.Excepción ConcurrentModificationException
ConcurrentModificationException antes de que termine el recorrido.Excepción NullPointerException
name.length() lanza NullPointerException cuando la variable name contiene null.I
Identidad
== para dos referencias; unos valores de campo coincidentes por sí solos no la establecen.Igualdad de contenido
Inmutabilidad
toUpperCase() no puede cambiar los caracteres de la cadena original.Interfaz de usuario
add de un registro de temperaturas y muestra cualquier mensaje de rechazo.Internalización de cadenas (string interning)
"oak" == "oak" sea true y ocultar un error que aparece cuando un texto igual procede de la entrada.Inyección por constructor
L
Lógica del programa
M
Método de copia
new Counter(value) para que incrementar la copia deje el original sin cambios.Método de fábrica
issue() de una máquina de boletos construye un boleto con su siguiente número y lo devuelve.Método sobrecargado
equals(Book other) crea un método sobrecargado y deja equals(Object) disponible para las colecciones.Mutación
parcel.setWeight(3.5) es una mutación que también puede observar una lista que contiene ese paquete.O
Objeto mutable
Objeto propietario
add.Objeto receptor
this hace referencia a él.earlier.before(later), el objeto receptor es earlier; la otra fecha se proporciona como argumento.P
Parámetro
allowIn(Person visitor), el parámetro visitor recibe la referencia a la persona proporcionada por quien llama.Paso por valor
Pertenencia a una lista
R
Reasignación
current = new Counter(); redirige current, mientras que los demás alias conservan el contador antiguo.Recolección de basura
Referencia
Counter second = first; permite que ambas variables lleguen al contador original.Referencia nula
null, que no lleva a ningún objeto y puede estar contenido en una variable de referencia, pero no en una variable primitiva.String name no puede recibir length(), mientras que una cadena vacía puede devolver la longitud 0.Reflexividad
x.equals(x).this == other en equals acepta la igualdad consigo mismo antes de comparar los valores de cualquier campo.Registro
Book(String title, int year) compara ambos componentes y proporciona los métodos de acceso title() y year().Resultado de búsqueda
S
Simetría
Sobrescritura
equals(Object) en una clase nueva.@Override permite que el compilador compruebe que el método de igualdad de un libro es una sobrescritura válida.T
Tipo de referencia
null.String es un tipo de referencia, así que una variable puede hacer referencia a un objeto de texto o no contener ninguna referencia a un objeto.Tipo primitivo
int permite que count contenga 12 directamente, así que asignarlo a otra variable copia 12.Transitividad
V
Validación de datos
Valor agregado
Valor de retorno
findByName de un registro puede llevar a una persona almacenada que quien llama y el registro comparten.Valor predeterminado de un campo
ArrayList<Song> sin inicializar es null, así que todavía no existe ninguna lista.- 1Primeros programas en Java
- 2Condiciones y bucles en Java
- 3Métodos en Java
- 4Listas, arreglos y cadenas en Java
- 5Clases y objetos en Java
- 6Objetos y referencias en Java
- 7HashMap y colecciones en Java
- 8Herencia, interfaces y polimorfismo en Java
- 9Excepciones y archivos en Java
- 10Paquetes en Java
- 11Genéricos y expresiones lambda en Java
Sobre esta unidad
Crea tus propios Materiales de Estudio
Convierte tus notas, diapositivas y documentos en Guías de Estudio, Preguntas de Práctica, Flashcards y 6 herramientas más.