Objetos, referencias e igualdad en Java

Aprende qué contiene realmente una variable Java: un valor primitivo o una referencia a un objeto. Sigue los alias y 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

  1. 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 referencia
      Nombra 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 inalcanzables
      Sigue 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 nula
      Explica qué significa null, reconoce dónde aparece, evita NullPointerException y decide cuándo null es un valor razonable.
  2. 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étodos
      Predice 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étodos
      Escribe 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.
  3. 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 objetos
      Modela 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 listas
      Añ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 objetos
      Escribe 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.
  4. 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 contenido
      Explica 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 equals
      Sobrescribe 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 equals
      Explica 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.
  5. 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ógica
      Decide 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 separadas
      Añ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.

Guía de estudio de Objetos y referencias en Java

5 capítulos

1

Sigue 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ónQué 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 new crea 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);
  1. Dibuja la variable a apuntando al objeto A, con peso 2.
  2. Dibuja b apuntando a ese mismo objeto A. Sigue habiendo una caja.
  3. Cambia el peso dentro del objeto A a 5. Ambas vías llegan ahora al peso 5.
  4. Crea el objeto B con peso 7 y redirige solo b hacia é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ónCambio en el diagramaEfecto a través de otros alias
b.setWeight(5)Cambia un campo dentro del objetoVen el nuevo peso
b = new Box(7)Redirige la flecha de bConservan sus flechas actuales
b = nullElimina la flecha de bSiguen 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.

ObjetivoExpresión segura
Comprobar ausencianame == null
Comprobar presencianame != 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

  • null significa 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.
2

Utiliza 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étodoQuien llama observa
Asignar un valor a un parámetro primitivoNingún cambio en la variable de quien llama
Mutar el objeto del parámetroCambio de estado del objeto compartido
Reasignar un parámetro de referenciaNingú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.
  • this designa 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.
3

Conecta 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 constructorConsecuencia para las referencias
Almacenar la referencia a la fecha recibidaQuien llama y la persona pueden compartir la fecha
Crear una fecha nueva a partir de los valores recibidosLa persona empieza con su propia fecha
Almacenar una fecha inmutableCompartirla 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 devueltaEfecto en la lista de reproducción
Eliminar una entradaLas canciones de la lista de reproducción siguen siendo las mismas
Vaciar la copiaLas canciones de la lista de reproducción siguen siendo las mismas
Mutar una canción compartidaLa 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.
4

Define 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 comparadosSignificado de ==Significado de equals
PrimitivosCompara valores primitivosUn valor primitivo no tiene métodos de instancia
ReferenciasEl mismo objeto, o ambas son nullLa regla de igualdad de la clase
CadenasEl mismo objeto de cadenaContenido de caracteres coincidente
Clase que hereda Object.equalsEl mismo objetoEl 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 que equals sigue la regla de la clase.
  • Object.equals compara la identidad hasta que se sobrescribe.
  • Utiliza String.equals para 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) y y.equals(x) coinciden.
  • Transitiva: si x es igual a y e y es igual a z, x es igual a z.
  • 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 reemplaza equals(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 hashConsecuencia para el contrato
Utilizar los mismos campos de igualdadLos objetos iguales reciben códigos coincidentes
Añadir un campo que la igualdad ignoraLos objetos iguales pueden recibir códigos diferentes
Utilizar menos campos de igualdadEs válido, pero puede crear más colisiones
Devolver siempre 1Es 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 hashCode siempre que sobrescribas equals.
  • 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.
5

Asigna 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.

TrabajoClase responsable
Leer el texto de un comandoInterfaz de usuario
Almacenar una lecturaRegistro de temperaturas
Calcular la media o el máximoRegistro de temperaturas
Redondear e imprimir un resultadoInterfaz de usuario
Rechazar una temperatura imposibleRegistro de temperaturas
Explicar al usuario una entrada no válidaInterfaz 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.
  • main construye 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

5 Lecciones Interactivas

1

Sigue las variables sin perder de vista los objetos

15 pasos
Sigue copias de valores primitivos, referencias compartidas, reasignaciones y null a través de un programa breve.
  1. 1
    Introducció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.

  2. 2
    ExplicaciónUn valor puede ser un dato o una referencia

    Java tiene ocho tipos primitivos: byte, short, int, long, float, double, char y boolean. Sus variables contienen datos directamente. Las clases y los arreglos son tipos de referencia; sus variables contienen referencias o null.

    En int count = 13;, el valor es 13. En Tally t = new Tally(13);, el valor de t es una referencia a un objeto que contiene sus propios datos.

  3. 3
    EjemploCopia 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. start sigue siendo 13 porque ninguna sentencia le asigna un valor nuevo. Dibuja dos cajas que contengan números separados; no interviene ningún objeto compartido.

  4. 4
    EjemploCopia un valor de referencia

    Supón que Tally almacena un entero, addOne() lo incrementa y value() lo lee.

    Tally first = new Tally(13);
    Tally next = first;
    next.addOne();
    

    Un new crea un contador. La asignación da a next otra vía para acceder a él. Llamar a addOne() cambia su valor almacenado a 14, que first.value() también lee ahora. La segunda variable no es un segundo contador.

  5. 5
    DesafíoSepara un objeto de su alias

    Con los mismos métodos de Tally, ¿qué devuelve a.value()?

    Tally a = new Tally(20);
    Tally b = new Tally(a.value());
    b.addOne();
    

    ---reveal---

    Devuelve 20. El segundo new crea 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.

  6. 6
    ConexiónAlgunos objetos compartidos no se pueden mutar

    Las cadenas y los envoltorios como Integer son tipos de referencia. El empaquetado automático convierte valores primitivos en envoltorios para usos como ArrayList<Integer>.

    Las cadenas son inmutables: si left y right comparten «cedar», asignar right = right.toUpperCase() no cambia los caracteres originales. left sigue siendo «cedar». El resultado en mayúsculas devuelto se asigna solo a right.

  7. 7
    Analogí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.

  8. 8
    EjemploCambia primero el estado compartido

    Supón que Crate almacena una masa en kilogramos, con los métodos setMass y mass.

    Crate a = new Crate(3);
    Crate b = a;
    b.setMass(6);
    

    Dibuja a y b apuntando a una caja. El método modificador actualiza la masa dentro de esa caja de 3 a 6. Tanto a.mass() como b.mass() devuelven ahora 6; el método modificador no ha redirigido ninguna de las dos variables.

  9. 9
    EjemploDespué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 variable a sigue 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.

  10. 10
    Desafí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 en c; asignar null a a eliminó solo una ruta. La caja distinta de 9 kg sigue siendo alcanzable a través de b.

  11. 11
    Idea 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.

  12. 12
    ExplicaciónNull significa que falta el destino

    null no es un objeto vacío. Un String nulo no tiene ningún objeto sobre el que llamar a length(), 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; lanza NullPointerException cuando el desempaquetado intenta obtener un valor primitivo del envoltorio ausente.

  13. 13
    ConsejoLee 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.

  14. 14
    Desafí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 && omite mass() 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.

  15. 15
    RepasoComprueba 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.

2

Sigue un objeto al entrar en un método y al salir de él

12 pasos
Sigue la mutación y la reasignación de un parámetro y después elige entre valores de retorno compartidos e independientes.

Guarda esta unidad para seguir cada Lección Interactiva paso a paso.

  1. 1
    IntroducciónSigue una llamada y su resultado
  2. 2
    ExplicaciónUn parámetro recibe su propio valor
  3. 3
    EjemploMuta el objeto compartido
  4. 4
    EjemploReasigna la ruta local
  5. 5
    Desafío¿Puede un método intercambiar las variables de quien llama?
  6. 6
    ConexiónCompara el objeto receptor con su argumento
  7. 7
    ExplicaciónEl tipo de retorno no promete una copia
  8. 8
    EjemploConstruye un objeto independiente para devolverlo
  9. 9
    DesafíoEncuentra una entrada que se pueda actualizar
  10. 10
    ConsejoUn método de acceso puede revelar un alias oculto
  11. 11
    DesafíoCombina un retorno con una llamada que recibe un parámetro
  12. 12
    RepasoExamina un método en ambos límites
3

Construye y protege una colección de objetos

16 pasos
Conecta campos de tipo de referencia y listas, busca entradas almacenadas y distingue las copias del contenedor de las copias de sus elementos.

Guarda esta unidad para seguir cada Lección Interactiva paso a paso.

  1. 1
    IntroducciónSepara las capas de estado
  2. 2
    ExplicaciónModela una relación de pertenencia
  3. 3
    EjemploDelega la regla de fechas
  4. 4
    DesafíoProtege la fecha recibida
  5. 5
    ExplicaciónUna entrada es otra ruta
  6. 6
    EjemploCalcula un total mediante un bucle
  7. 7
    EjemploConserva el objeto seleccionado
  8. 8
    DesafíoDevuelve la ausencia en el momento adecuado
  9. 9
    ConsejoSepara la eliminación del recorrido
  10. 10
    ExplicaciónProporciona al propietario una lista inicializada
  11. 11
    EjemploOfrece operaciones útiles
  12. 12
    DesafíoDistingue una copia de lista de sus elementos
  13. 13
    ConexiónProtege ambas direcciones
  14. 14
    EjemploFiltra en un resultado nuevo
  15. 15
    DesafíoSigue una copia a través de otro objeto
  16. 16
    RepasoRevisa los límites de la colección
4

Construye una igualdad fiable para las colecciones

16 pasos
Elige entre comparar la identidad o el contenido, implementa equals y hashCode conjuntamente y razona sobre claves estables.

Guarda esta unidad para seguir cada Lección Interactiva paso a paso.

  1. 1
    IntroducciónDecide qué significa una coincidencia
  2. 2
    EjemploTexto igual en objetos separados
  3. 3
    ConsejoNo dejes que la caché elija la regla
  4. 4
    DesafíoDota a una clase de igualdad de contenido
  5. 5
    ExplicaciónElige los campos y la firma
  6. 6
    EjemploAcepta la identidad y rechaza tipos incorrectos
  7. 7
    EjemploCompara los campos seleccionados
  8. 8
    DesafíoDetecta un contrato incumplido
  9. 9
    Idea claveComprueba más de una pareja coincidente
  10. 10
    ExplicaciónLos códigos hash reducen los candidatos
  11. 11
    EjemploGenera el hash coherente
  12. 12
    DesafíoDistingue una colisión de la igualdad
  13. 13
    EjemploObserva qué ocurre al cambiar una clave
  14. 14
    ConexiónUn registro sencillo proporciona ambos métodos
  15. 15
    DesafíoSigue el conjunto con la igualdad generada
  16. 16
    RepasoRevisa una clase de valor
5

Añade una función sin mezclar la entrada y las reglas

11 pasos
Conecta una interfaz de consola a una lógica reutilizable, comprueba directamente una consulta nueva y conserva sus requisitos de almacenamiento.

Guarda esta unidad para seguir cada Lección Interactiva paso a paso.

  1. 1
    IntroducciónConecta las reglas con una conversación
  2. 2
    ExplicaciónNombra las dos tareas
  3. 3
    EjemploConecta un conjunto de objetos compartidos
  4. 4
    EjemploDevuelve el resultado para mostrarlo
  5. 5
    DesafíoColoca el rechazo en la clase adecuada
  6. 6
    ExplicaciónProporciona a la consulta nueva un método de lógica
  7. 7
    EjemploComprueba con entradas conocidas
  8. 8
    DesafíoEvalúa la propuesta de almacenamiento
  9. 9
    ConsejoAmplía el programa un paso comprobado a la vez
  10. 10
    DesafíoPasa a una interfaz diferente
  11. 11
    RepasoRevisa una función nueva en su límite

Ejercicios de Objetos y referencias en Java

40 Preguntas de Práctica en 4 sets

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();
  1. Ahora hace referencia al texto «RIVER».
  2. Se vuelve null después de la asignación.
  3. Sigue haciendo referencia al texto «river».
  4. Ahora hace referencia a una cadena vacía.
Mostrar respuestaOcultar respuesta
Respuesta: C
Los objetos String son inmutables, así que la operación de conversión a mayúsculas no puede cambiar los caracteres del original compartido. Su resultado se asigna solo a 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;
  1. El desempaquetado lanza NullPointerException.
  2. value recibe el valor primitivo 0.
  3. reading se convierte en un envoltorio de entero para 0.
  4. La compilación falla porque los envoltorios no pueden contener null.
Mostrar respuestaOcultar respuesta
Respuesta: A
La declaración del envoltorio es válida porque 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);
  1. 9
  2. 22
  3. 25
  4. 14
Mostrar respuestaOcultar respuesta
Respuesta: D
El primer método modificador cambia a 14 el medidor compartido con 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?
  1. Una lista nueva con las referencias a las piezas almacenadas.
  2. Una lista nueva con copias independientes de las piezas.
  3. La lista almacenada tras copiar cada pieza en su lugar.
  4. La lista almacenada con índices numéricos inmutables.
Mostrar respuestaOcultar respuesta
Respuesta: B
Quien llama necesita una estructura independiente y elementos mutables independientes. Una lista nueva protege las entradas y las copias nuevas de las piezas protegen el estado de las piezas almacenadas. Copiar solo la lista seguiría permitiendo que un método modificador sobre una pieza devuelta cambiara la pieza que contiene el contenedor.

Pregunta 5 de 10

Una búsqueda por nombre comprueba una lista en orden. Con los nombres «Iris», «Noel» y «Sana», devuelve null cuando se busca «Sana». Actualmente tiene return null en un else después de la comprobación del nombre dentro del bucle. ¿Dónde debe ir ese retorno?
  1. Antes de que el bucle empiece a comprobar entradas.
  2. Después de que el bucle termine sin encontrar una coincidencia.
  3. Inmediatamente después de la primera comprobación de nombre satisfactoria.
  4. Dentro del bucle, antes de leer cada nombre nuevo.
Mostrar respuestaOcultar respuesta
Respuesta: B
Que la primera entrada no coincida no indica nada sobre las entradas posteriores, así que la búsqueda debe continuar. Devuelve null solo después de que el bucle haya comprobado todos los candidatos sin encontrar una coincidencia. Retornar desde el primer 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?
  1. Una entrada para el clip modificado.
  2. Una entrada para el clip sin modificar.
  3. Ninguna entrada; ambas listas se vacían.
  4. Una entrada null donde estaba el clip.
Mostrar respuestaOcultar respuesta
Respuesta: A
El constructor copia la estructura de la lista, pero conserva la referencia al clip. Por tanto, la mutación cambia el clip visible mediante 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"))
  1. true, porque coinciden los valores de los campos.
  2. true, porque los constructores definen la igualdad.
  3. false, porque las cadenas no pueden ser campos de igualdad.
  4. false, porque son objetos separados.
Mostrar respuestaOcultar respuesta
Respuesta: D
Dos evaluaciones de 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?
  1. La regla actual puede romper el contrato; una constante es válida pero ineficiente.
  2. La regla actual es válida; una constante rompe el cálculo hash para objetos distintos.
  3. Ambas reglas rompen el contrato porque los códigos hash deben ser únicos.
  4. Ambas reglas son igual de eficientes porque la igualdad confirma las coincidencias.
Mostrar respuestaOcultar respuesta
Respuesta: A
Las etiquetas iguales deben recibir códigos iguales, pero incluir un color que se ignora puede darles códigos diferentes. Un código constante cumple el requisito de corrección porque todos los pares iguales tienen el mismo código, aunque crea muchas colisiones. No se exige unicidad; una reparación útil es calcular el hash solo del campo de igualdad code.

Pregunta 9 de 10

Un programa de seguimiento de duraciones rechaza las duraciones negativas. ¿Dónde deben implementarse el rechazo y el mensaje que pide al usuario que vuelva a intentarlo?
  1. El rechazo en la interfaz; el mensaje en la lógica.
  2. Tanto el rechazo como el mensaje solo en la interfaz.
  3. El rechazo en la lógica; el mensaje en la interfaz.
  4. Tanto el rechazo como el mensaje solo en la lógica.
Mostrar respuestaOcultar respuesta
Respuesta: C
La regla de duración no negativa protege los datos almacenados, así que corresponde a la lógica para todas las llamadas. La interfaz convierte un resultado rechazado en un mensaje y otra oportunidad de entrada. Comprobar solo en la interfaz permitiría que otra interfaz o una llamada directa al método eludieran la regla de datos.

Pregunta 10 de 10

Un registro ofrece actualmente 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?
  1. La media se vuelve incorrecta siempre que las lecturas incluyen un valor umbral.
  2. Distintas listas de lecturas pueden tener los mismos valores agregados pero distintos recuentos por encima de un umbral.
  3. Un máximo solo puede calcularse conservando todos los objetos de lectura originales.
  4. Cambiar un campo privado siempre exige cambiar también todos los comandos de la interfaz.
Mostrar respuestaOcultar respuesta
Respuesta: B
La suma, el recuento y el máximo no describen cómo se distribuyen todas las lecturas. Dos listas pueden compartir esos tres valores y tener cantidades diferentes de lecturas por encima de un límite elegido, así que la nueva consulta necesita conservar más información. Las consultas existentes de media y máximo todavía pueden utilizar correctamente sus valores agregados.
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

¿Qué declaración sobrescribe el método de igualdad heredado de Object?
  1. public boolean equals(Note other)
  2. public int equals(Object other)
  3. public boolean equals(Object other)
  4. public boolean equal(Object other)
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;
  1. 11
  2. 8
  3. 19
  4. 0
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();
  1. false, false
  2. true, true
  3. false, true
  4. true, false
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?
  1. label != null && label.length() >= 3
  2. label.length() >= 3 && label != null
  3. label != null & label.length() >= 3
  4. label.equals(null) && label.length() >= 3
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 5 de 10

Un método recibe un Robot r no nulo. Solo ejecuta r = new Robot();. ¿Qué ocurre con la variable de quien llama que se pasó como argumento?
  1. Ahora hace referencia al robot nuevo.
  2. Se vuelve null cuando termina el método.
  3. Sigue haciendo referencia al robot original.
  4. Hace referencia a una copia del robot original.
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();
  1. 7 and 7
  2. 6 and 7
  3. 6 and 6
  4. 7 and 6
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 7 de 10

Un constructor de 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?
  1. Almacenar la configuración directamente; devolver el objeto almacenado en el método de acceso.
  2. Mantener cada constructor; copiar la configuración solo al ejecutar el método de acceso.
  3. Copiar la configuración en cada constructor; mantener el método de acceso que copia.
  4. Crear una copia compartida de la configuración; pasarla a ambos constructores.
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 8 de 10

Se añade un Sensor s no nulo a dos listas. Una lista elimina su entrada para s. ¿Qué ocurre con el sensor de la otra lista?
  1. Sigue siendo el mismo objeto sensor.
  2. Se elimina también de la otra lista.
  3. Se sustituye por una copia del sensor.
  4. Se convierte en un elemento null de la 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")
  1. true, true
  2. false, false
  3. true, false
  4. false, true
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 10 de 10

Una aplicación de consola registra duraciones de lectura. Más adelante, la misma lógica dará soporte a una página web. ¿Dónde deben situarse el cálculo de los segundos totales y la impresión de «Total»?
  1. El cálculo en la lógica; la impresión en la interfaz de consola.
  2. Tanto el cálculo como la impresión en la interfaz de consola.
  3. Tanto el cálculo como la impresión en la clase de lógica.
  4. La impresión en la lógica; el cálculo en la interfaz de consola.
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;
  1. Dos objetos de celda.
  2. Tres objetos de celda.
  3. Ningún objeto de celda.
  4. Un objeto de celda.
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 2 de 10

Una clase declara 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?
  1. Declarar una lista local dentro de add y dejar el campo intacto.
  2. Asignar null explícitamente a notes en el constructor.
  3. Inicializar notes con new ArrayList<>().
  4. Hacer que notes sea public para que quien llama acceda al campo.
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);
  1. 10 and 16
  2. 16 and 16
  3. 4 and 16
  4. 10 and 7
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 4 de 10

Un 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?
  1. En cada comando de la interfaz que compara miembros.
  2. En SimpleDate.before, llamado por el método del miembro.
  3. En Member.toString, cuyo texto lee quien llama.
  4. En cada llamada, tras exponer públicamente todos los campos de fecha.
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 5 de 10

Un constructor de 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?
  1. Copiar también la lista recibida en el constructor.
  2. Mantener el constructor y copiar cada trabajo en el método de acceso.
  3. Devolver la lista interna para que ambas listas sigan sincronizadas.
  4. Cambiar el método de acceso para devolver siempre una lista vacía nueva.
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?
  1. El método sobrescribe equals, pero las listas solo comparan códigos hash.
  2. El método no puede leer campos privados de otro objeto token.
  3. El método sobrecarga equals y deja equals(Object) sin cambios.
  4. El método debe devolver un entero para que funcionen las búsquedas en colecciones.
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?
  1. Cambiar el número de serie, eliminar con el nuevo número y volver a añadir.
  2. Cambiar el número de serie, llamar a contains y dejar la entrada intacta.
  3. Añadir la clave por segunda vez, cambiar el número de serie y eliminar una vez.
  4. Eliminar con el número de serie antiguo, cambiarlo y volver a añadir.
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 8 de 10

Un método de la interfaz necesita el número de tareas almacenadas. ¿Qué debería hacer normalmente el método de lógica?
  1. Imprimir el recuento en la consola.
  2. Devolver el recuento como un entero.
  3. Leer un comando, después imprimir una etiqueta.
  4. Abrir un escáner para solicitar un recuento.
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)?
  1. 0
  2. 1
  3. 2
  4. 3
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)?
  1. Solo la marca con código «B».
  2. La marca B y la segunda marca A.
  3. Las dos marcas con código «A».
  4. Ninguna marca en la lista.
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

Una lista necesita entradas de números enteros que pueden estar ausentes. ¿Qué tipo de elemento permite almacenar un número o null?
  1. int
  2. Integer
  3. double
  4. boolean
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);
  1. Un objeto de nodo.
  2. Dos objetos de nodo.
  3. Tres objetos de nodo.
  4. Ningún objeto de nodo.
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?
  1. Devolver una copia superficial de la lista del casillero.
  2. Devolver un elemento nuevo con la etiqueta actualizada.
  3. Devolver la referencia al elemento almacenado que coincide.
  4. Devolver otra copia después de cada cambio de etiqueta.
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 4 de 10

Dos objetos 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?
  1. Solo la planificación cuyo método de acceso se llamó lo ve.
  2. Ninguna planificación observa la fecha modificada.
  3. Cada planificación conserva su propia copia de la fecha antigua.
  4. Ambas planificaciones siguen observando la fecha modificada.
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 5 de 10

Una biblioteca mantiene una lista privada de objetos 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?
  1. Las entradas cambian; todos los títulos almacenados siguen iguales.
  2. Cambian tanto las entradas de la biblioteca como el título almacenado.
  3. No cambian las entradas de la biblioteca ni ningún título almacenado.
  4. Las entradas siguen iguales; cambia un título almacenado.
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 6 de 10

Para las referencias a objetos no nulas x e y, ¿qué establece x == y cuando es true?
  1. Sus resultados de toString() definen la igualdad.
  2. Hacen referencia al mismo objeto.
  3. Sus clases sobrescriben equals conjuntamente.
  4. Sus campos nunca se han cambiado.
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 7 de 10

Un método 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?
  1. Falla la transitividad: las dos primeras parejas coinciden, pero los extremos no.
  2. Falla la simetría: invertir la misma pareja cambia el resultado de la comparación.
  3. Falla la reflexividad: una puntuación no puede ser igual a su propia referencia.
  4. Falla la consistencia: repetir una pareja da un resultado distinto cada vez.
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?
  1. 1
  2. 3
  3. 2
  4. 0
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 9 de 10

Una aplicación de tareas necesita un comando nuevo que devuelva la mayor duración almacenada. ¿Qué orden de implementación permite comprobarlo directamente antes de añadir la interacción por consola?
  1. Leer e imprimir duraciones en la interfaz y después copiar ese código a la lógica.
  2. Añadir una consulta a la lógica, comprobar duraciones conocidas y conectar el comando de la interfaz.
  3. Añadir mensajes de consola a la consulta de lógica y llamarla desde cada comando.
  4. Hacer pública la lista privada y permitir que la interfaz la busque directamente.
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 10 de 10

Una clase de valor utiliza 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?
  1. La constante no es válida; se requieren códigos diferentes para objetos con NaN.
  2. Ambas son válidas porque los objetos distintos pueden tener cualquier código hash.
  3. La constante es válida; códigos diferentes para valores NaN iguales no lo son.
  4. Ambas son inválidas porque cada valor double necesita un código único.
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

¿Te atascas con una pregunta tuya? Ayuda con Tareas

Términos Clave

61 Términos Clave, cada uno con su definición

A

Alcanzabilidad
La alcanzabilidad significa que el programa en ejecución puede seguir una cadena de referencias utilizable hasta un objeto, posiblemente a través de campos o listas.
Una lista conserva la alcanzabilidad de un paquete incluso después de que la variable local que lo contenía originalmente pase a ser null.
Alias
Un alias es otra variable o referencia que llega al mismo objeto y proporciona varias rutas hacia un estado compartido.
Después de Box second = first;, second es un alias y su método modificador cambia la caja a la que llega first.
Argumento
Un argumento es un valor proporcionado en el lugar de una llamada para inicializar un parámetro correspondiente del método o constructor llamado.
En gate.allowIn(ana), el argumento es el valor de referencia leído de ana, que el parámetro copia.

B

Bucle for-each
Un bucle for-each visita los elementos de una colección por turno y asigna cada valor de elemento a una variable de bucle para procesarlo.
Un bucle for-each sobre paquetes puede sumar cada resultado de getWeight() sin cambiar las entradas de la lista.

C

Caché de objetos envoltorio
Una caché de objetos envoltorio reutiliza objetos envoltorio para determinados valores, haciendo que la identidad de referencia dependa de la reutilización en lugar de la igualdad numérica.
La caché de objetos envoltorio Integer incluye valores pequeños, así que utiliza equals en lugar de depender de == para envoltorios.
Campo de referencia final
A un campo de referencia final no se le puede asignar una referencia diferente después de la inicialización, aunque el objeto al que hace referencia todavía puede ser mutable.
Un campo de referencia final que contiene una lista impide reemplazar la lista, pero no detiene las llamadas a add.
Campo de tipo de referencia
Un campo de tipo de referencia conecta a su propietario con otro objeto o contiene null cuando no hay ningún objeto presente.
El campo de tipo de referencia birthday de Person puede llevar a un SimpleDate que realiza comparaciones de fechas.
Campos que determinan la igualdad
Los campos que determinan la igualdad son los datos seleccionados para definir cuándo dos instancias representan el mismo valor para el propósito de un programa.
El título y el año pueden ser campos que determinan la igualdad de libros, así que cambiar solo a quien tiene el préstamo deja esa igualdad sin cambios.
Clase ArrayList
Un ArrayList es un objeto de lista de tamaño variable con entradas ordenadas; las entradas de objetos son referencias, así que añadir una no copia sus campos.
Un ArrayList<Parcel> puede contener tres referencias a paquetes y permitir que un bucle sume sus pesos en kilogramos.
Clase envoltorio
Una clase envoltorio representa un valor primitivo como un objeto, lo que permite utilizar valores como los enteros donde se necesitan tipos de referencia.
La clase envoltorio Integer proporciona el tipo de elemento para ArrayList<Integer>; el tipo primitivo int no puede hacerlo.
Clase HashSet
Un HashSet almacena elementos distintos utilizando sus códigos hash y su igualdad, así que añadir un valor igual no añade otra entrada.
Un HashSet<Book> conserva una entrada para dos libros iguales separados cuando su igualdad y sus códigos hash coinciden.
Clave de HashMap
Una clave de HashMap identifica un valor asociado mediante el cálculo del hash y la igualdad; su estado relacionado con la igualdad debe mantenerse estable durante el almacenamiento.
Una clave de HashMap construida a partir del título y el año puede recuperar una etiqueta de estantería utilizando un objeto de libro igual pero separado.
Código hash
Un código hash es un entero utilizado para reducir los candidatos en una colección basada en hash; los objetos iguales deben producir códigos iguales.
Un código hash coherente para un libro puede calcularse con Objects.hash(title, year) cuando esos campos definen la igualdad.
Colisión de hash
Una colisión de hash ocurre cuando objetos distintos producen el mismo código hash, lo cual está permitido porque la igualdad sigue confirmando una coincidencia.
Una colisión de hash no fusiona valores distintos en un conjunto cuando equals los distingue correctamente.
Composición
La composición modela una relación de pertenencia al dar a un objeto campos que hacen referencia a otros objetos, como la fecha de nacimiento de una persona.
La composición permite que un curso almacene a su profesor como un objeto de persona y obtenga su nombre mediante un método.
Comprobación directa
Una comprobación directa llama a métodos de lógica con entradas conocidas y compara sus resultados con las respuestas esperadas sin pasar por una interfaz de usuario.
Una comprobación directa puede añadir cuatro lecturas y verificar la media sin leer ningún texto de comando.
Consistencia
La consistencia exige que las comprobaciones de igualdad repetidas coincidan mientras la información comparada no cambie, de modo que los resultados sigan siendo fiables.
La consistencia permite que un resultado cambie después de que cambie un campo de igualdad, pero no mientras los datos comparados permanezcan fijos.
Consulta sobre una colección
Una consulta sobre una colección selecciona o calcula información a partir de elementos almacenados y suele devolver un valor, un objeto o una lista de resultados nueva.
La consulta sobre una colección songsLongerThan(200) construye una lista nueva y conserva las referencias a las canciones coincidentes.
Contrato público
Un contrato público es el comportamiento prometido en el que confían quienes llaman al utilizar los métodos públicos de una clase, independientemente de su almacenamiento privado.
Un cambio de almacenamiento respeta el contrato público solo si todas las consultas de temperatura admitidas siguen funcionando.
Copia defensiva
Una copia defensiva es un objeto separado creado para impedir que el código externo cambie un estado protegido mediante una referencia mutable compartida.
Un método de acceso a la fecha de nacimiento devuelve una copia defensiva para que un método modificador de fecha de quien llama deje intacta la fecha de la persona.
Copia superficial
Una copia superficial crea un contenedor externo separado, pero copia las referencias de sus elementos, por lo que los objetos contenidos siguen compartidos.
Una copia superficial de una lista de canciones puede perder una entrada de forma independiente y seguir compartiendo cada canción mutable.

D

Delegación
La delegación consiste en pedir a otro objeto que realice trabajo mediante sus métodos, manteniendo una regla en el objeto propietario de sus datos.
Una persona utiliza delegación cuando olderThan llama a birthday.before(other.birthday) para comparar fechas.
Desempaquetado
El desempaquetado convierte el valor almacenado en un objeto envoltorio a su tipo primitivo; falla con NullPointerException si el envoltorio es nulo.
El desempaquetado ocurre en int count = boxed;, así que boxed debe hacer referencia a un objeto Integer real.

E

Empaquetado automático
El empaquetado automático es la conversión automática que realiza Java de un valor primitivo a la referencia del envoltorio correspondiente durante una asignación o llamada.
El empaquetado automático permite que numbers.add(12) pase un valor Integer cuando numbers es un ArrayList<Integer>.
Encapsulación
La encapsulación mantiene el estado detrás de los métodos de una clase para que esta pueda controlar los cambios y hacer respetar las reglas de sus datos almacenados.
La encapsulación de una lista de reproducción se debilita si un método de acceso devuelve la lista privada y quienes llaman pueden vaciarla directamente.
Evaluación en cortocircuito
La evaluación en cortocircuito omite el lado derecho de && cuando su lado izquierdo es falso, lo que permite que una comprobación previa proteja una llamada posterior.
La evaluación en cortocircuito hace que name != null && name.length() > 0 omita la llamada al método cuando el valor es null.
Excepción ConcurrentModificationException
Una ConcurrentModificationException puede indicar que una colección se ha modificado estructuralmente durante un recorrido basado en un iterador.
Eliminar directamente elementos de una lista durante un bucle for-each puede causar ConcurrentModificationException antes de que termine el recorrido.
Excepción NullPointerException
Una NullPointerException es un fallo en tiempo de ejecución causado por operaciones que necesitan un objeto ausente, como una llamada de instancia o un desempaquetado a través de null.
La llamada name.length() lanza NullPointerException cuando la variable name contiene null.

I

Identidad
La identidad significa ser el mismo objeto y se comprueba mediante == para dos referencias; unos valores de campo coincidentes por sí solos no la establecen.
Dos libros construidos por separado pueden tener títulos y años iguales y seguir teniendo identidades diferentes.
Igualdad de contenido
La igualdad de contenido significa que los campos elegidos por la regla de igualdad de una clase coinciden, incluso cuando los objetos tienen identidades diferentes.
Un catálogo de libros puede definir la igualdad de contenido mediante el título y el año, ignorando a quien tiene el libro prestado en ese momento.
Inmutabilidad
La inmutabilidad significa que el estado de un objeto no puede cambiar después de su construcción, por lo que compartirlo no permite una mutación compartida.
La inmutabilidad de String significa que toUpperCase() no puede cambiar los caracteres de la cadena original.
Interfaz de usuario
Una interfaz de usuario es el código del programa que interactúa con el usuario, como un bucle de comandos que lee texto e imprime resultados.
Una interfaz de usuario lee una temperatura, llama al método add de un registro de temperaturas y muestra cualquier mensaje de rechazo.
Internalización de cadenas (string interning)
La internalización de cadenas comparte objetos de cadena para literales coincidentes y permite que dos expresiones literales idénticas tengan la misma referencia.
La internalización de cadenas puede hacer que "oak" == "oak" sea true y ocultar un error que aparece cuando un texto igual procede de la entrada.
Inyección por constructor
La inyección por constructor proporciona los colaboradores de un objeto como argumentos del constructor, lo que le permite utilizar objetos existentes compartidos.
La inyección por constructor proporciona a una interfaz un escáner y un registro en lugar de crear un registro separado para cada comando.

L

Lógica del programa
La lógica del programa almacena datos y aplica las reglas de la aplicación, y expone métodos que pueden funcionar independientemente de la interacción con la consola.
La lógica del programa calcula la media de temperaturas como un número que puede mostrar una interfaz de consola o web.

M

Método de copia
Un método de copia devuelve un objeto nuevo inicializado a partir de los datos de un objeto existente, proporcionando la independencia que promete su diseño.
El método de copia de un contador utiliza new Counter(value) para que incrementar la copia deje el original sin cambios.
Método de fábrica
Un método de fábrica proporciona un objeto a quien lo llama, ya sea creando una instancia nueva o reutilizando una que ya existe.
El 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
Un método sobrecargado es otro método con el mismo nombre pero diferentes tipos de parámetro; no reemplaza la declaración original.
Escribir equals(Book other) crea un método sobrecargado y deja equals(Object) disponible para las colecciones.
Mutación
Una mutación cambia el estado de un objeto existente y hace que los valores modificados de sus campos sean visibles a través de todas las referencias a ese objeto.
Llamar a parcel.setWeight(3.5) es una mutación que también puede observar una lista que contiene ese paquete.

O

Objeto mutable
Un objeto mutable permite cambiar su estado después de la construcción, así que los alias pueden observar cambios realizados mediante otra referencia.
Una fecha con métodos modificadores es un objeto mutable; compartirla entre dos personas puede cambiar ambas fechas de nacimiento almacenadas.
Objeto propietario
Un objeto propietario gestiona el estado mediante sus propios métodos, como una lista de reproducción que controla las adiciones a su lista privada de canciones.
La lista de reproducción es el objeto propietario responsable de hacer respetar un recuento máximo en su método add.
Objeto receptor
El objeto receptor es el objeto sobre el que se llama a un método de instancia; dentro de ese método, this hace referencia a él.
En earlier.before(later), el objeto receptor es earlier; la otra fecha se proporciona como argumento.

P

Parámetro
Un parámetro es una variable declarada por un método o constructor que recibe el valor copiado de un argumento cuando se ejecuta una llamada.
En allowIn(Person visitor), el parámetro visitor recibe la referencia a la persona proporcionada por quien llama.
Paso por valor
El paso por valor proporciona a un parámetro de método su propia copia del valor de un argumento, incluida una referencia copiada cuando el argumento es un objeto.
El paso por valor permite que un método redirija su parámetro de persona sin redirigir la variable de quien llama.
Pertenencia a una lista
La pertenencia a una lista describe qué entradas contiene una lista; cambiar la pertenencia es distinto de cambiar los campos de un objeto almacenado.
Eliminar una canción cambia la pertenencia en una lista, mientras que otra lista de reproducción puede seguir haciendo referencia a esa misma canción.

R

Reasignación
Una reasignación almacena un valor nuevo en una variable existente; para una variable de referencia, cambia el objeto al que llega la variable.
La reasignación current = new Counter(); redirige current, mientras que los demás alias conservan el contador antiguo.
Recolección de basura
La recolección de basura es la recuperación automática por Java de la memoria de los objetos inalcanzables, en un momento elegido por su entorno de ejecución.
Un contador queda disponible para la recolección de basura después de que el programa en ejecución pierda su última ruta hacia él.
Referencia
Una referencia es un valor que lleva a un objeto; copiarla proporciona a otra variable una ruta hacia ese mismo objeto.
La referencia asignada mediante Counter second = first; permite que ambas variables lleguen al contador original.
Referencia nula
Una referencia nula es el valor null, que no lleva a ningún objeto y puede estar contenido en una variable de referencia, pero no en una variable primitiva.
Una referencia nula en String name no puede recibir length(), mientras que una cadena vacía puede devolver la longitud 0.
Reflexividad
La reflexividad exige que todo objeto no nulo se compare como igual a sí mismo, así que un método de igualdad válido devuelve true para x.equals(x).
Una comprobación inicial this == other en equals acepta la igualdad consigo mismo antes de comparar los valores de cualquier campo.
Registro
Un registro es una clase de datos de Java con campos de componente final y métodos de acceso, igualdad, cálculo de hash y representación textual generados.
El registro Book(String title, int year) compara ambos componentes y proporciona los métodos de acceso title() y year().
Resultado de búsqueda
Un resultado de búsqueda es el valor coincidente devuelto por una consulta; una búsqueda de objetos puede devolver la referencia almacenada o null si no hay coincidencia.
El resultado de búsqueda de un nombre puede ser una persona almacenada en un registro, lo que permite actualizarla mediante la referencia devuelta.

S

Simetría
La simetría exige que invertir dos objetos no nulos en una comparación de igualdad conserve el resultado verdadero o falso.
La simetría falla si un libro acepta como igual la cadena de su título, pero la cadena rechaza el objeto de libro.
Sobrescritura
Una sobrescritura reemplaza el comportamiento de un método de instancia heredado mediante una declaración coincidente, como equals(Object) en una clase nueva.
La anotación @Override permite que el compilador compruebe que el método de igualdad de un libro es una sobrescritura válida.

T

Tipo de referencia
Un tipo de referencia es un tipo de clase, arreglo o interfaz cuyas variables pueden contener referencias a objetos o el valor 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
Un tipo primitivo almacena datos ordinarios directamente, como un número o un valor booleano; Java tiene ocho tipos primitivos.
El tipo primitivo int permite que count contenga 12 directamente, así que asignarlo a otra variable copia 12.
Transitividad
La transitividad exige que, cuando un objeto es igual a un segundo y el segundo es igual a un tercero, el primero también sea igual al tercero.
La transitividad hace que tres libros con los mismos campos de igualdad pertenezcan al mismo grupo de valores iguales.

V

Validación de datos
La validación de datos comprueba si un valor proporcionado cumple las reglas de la aplicación antes de aceptarlo en el estado almacenado.
La validación de datos en un registro de duraciones puede rechazar un valor negativo independientemente de la interfaz que lo llame.
Valor agregado
Un valor agregado es una estadística combinada, como la suma, el recuento o el máximo, que resume valores y pierde parte de la información sobre ellos.
Los mismos valores agregados pueden describir listas con recuentos diferentes por encima de un umbral, así que esa consulta necesita más datos.
Valor de retorno
Un valor de retorno es el valor que un método devuelve a quien lo llama; para un resultado de objeto, es una referencia o null.
El valor de retorno de findByName de un registro puede llevar a una persona almacenada que quien llama y el registro comparten.
Valor predeterminado de un campo
Un valor predeterminado de un campo es el valor inicial que Java proporciona a un campo, como null para referencias o false para booleanos.
El valor predeterminado de un campo ArrayList<Song> sin inicializar es null, así que todavía no existe ninguna lista.

Sobre esta unidad

Por StudeliInformar de un error

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.

Comenzar