Clases y objetos en Java

Aprende a escribir tus propias clases en Java. Define los campos que almacenan el estado de un objeto, escribe constructores que den a cada objeto nuevo valores iniciales válidos y añade métodos de instancia que utilicen y modifiquen ese estado. Protege tus objetos con campos privados, métodos de acceso, métodos modificadores y comprobaciones, dales un toString legible y termina diseñando, creando y probando una clase completa a partir de una breve descripción.

PrincipianteUnidad 5 de 11 del curso Curso de Programación orientada a objetos con Java

Lo que aprenderás

  1. Explica cómo una clase define un nuevo tipo de datos con su propio comportamiento, y crea y utiliza objetos de ese tipo en un programa.
    • Explica qué son una clase y un objeto
      Describe una clase como la definición de un tipo, un objeto como una instancia de ese tipo y el estado y el comportamiento como lo que los objetos almacenan y hacen.
    • Crea objetos y llama a sus métodos
      Crea objetos con new, llama a sus métodos mediante la notación de punto y predice cómo se comportan objetos distintos.
  2. Declara las variables de instancia que almacenan el estado de un objeto y escribe constructores que den a cada objeto nuevo valores iniciales válidos.
    • Declara variables de instancia
      Declara campos con tipos adecuados, conoce sus valores predeterminados y distínguelos de las variables locales y los parámetros.
    • Escribe constructores que inicialicen objetos
      Escribe constructores que asignen valores a todos los campos, utiliza this para distinguir los campos de los parámetros y evita los errores que dejan campos sin asignar.
    • Ofrece varios constructores y encadénalos
      Sobrecarga constructores para distintas formas de crear un objeto y utiliza this(...) para escribir el código de inicialización una sola vez.
  3. Escribe métodos de instancia que lean y modifiquen el estado de un objeto, sigue su efecto y decide cuándo algo pertenece a un objeto o a la clase en su conjunto.
    • Escribe métodos de instancia que utilicen y modifiquen el estado
      Escribe métodos que informen sobre los campos de un objeto o los modifiquen, llama a otros métodos del mismo objeto y sigue el estado de los objetos paso a paso.
    • Distingue los miembros de instancia de los miembros estáticos
      Utiliza this para el objeto actual, decide qué debería ser estático y corrige los errores que surgen al mezclar código estático y de instancia.
  4. Oculta los campos mediante modificadores de acceso, proporciona acceso controlado mediante métodos de acceso, métodos modificadores y métodos con una función clara, y mantén cada objeto en un estado válido.
    • Oculta datos con modificadores de acceso
      Utiliza private, acceso de paquete, protected y public para controlar qué código puede utilizar los miembros de una clase, y explica por qué los campos se mantienen privados.
    • Escribe métodos de acceso y métodos modificadores
      Escribe métodos de acceso y modificadores con los nombres estándar, y decide qué campos los necesitan y cuándo es mejor un método con una función clara.
    • Mantén los objetos en un estado válido mediante comprobaciones
      Define las reglas que debe cumplir el estado de un objeto, aplícalas en constructores y métodos y evita entregar datos internos que permitan eludir esas comprobaciones.
  5. Da a los objetos una forma textual legible con toString y después diseña, escribe y prueba una clase completa a partir de una breve descripción.
    • Escribe un método toString
      Escribe un método toString que devuelva una descripción legible de un objeto y reconoce cuándo Java lo llama automáticamente.
    • Diseña, crea y prueba una clase completa
      Convierte una breve descripción en una clase con campos, constructores, métodos y reglas, y después prueba cada comportamiento desde un programa principal.

Guía de estudio de Clases y objetos en Java

5 capítulos

1

Clases, objetos y llamadas a métodos

Un programa suele necesitar mantener juntos varios valores relacionados: por ejemplo, el nombre, la especie y la edad de una mascota. Una clase da un tipo a ese grupo y reúne en un mismo lugar las acciones que lo utilizan. Relacionarás las definiciones de clases con los objetos creados mientras se ejecuta un programa y después seguirás las llamadas a métodos para ver qué datos de cada objeto cambian. Distinguir una clase, un objeto y una variable te ayudará a leer el código que escribirás a continuación.

1.1 Un tipo con estado y comportamiento

Una clase define un tipo: los datos que recuerdan sus objetos y las operaciones que ofrecen. Un objeto, también llamado instancia, es un valor de ese tipo creado mientras se ejecuta un programa. Escribir la definición de una clase no crea un objeto.

Estado y comportamiento

El nombre, la especie y la edad de una mascota forman su estado. Esos valores actuales se guardan en variables de instancia, también llamadas campos. Una operación como celebrar un cumpleaños es un comportamiento, escrito como método. El método de cumpleaños está junto a la edad que modifica, por lo que la regla se mantiene en un solo lugar.

Piensa en una clase como el plano de una construcción. Muchas casas pueden seguir un mismo plano, pero cada una tiene su propio contenido. Del mismo modo, dos mascotas pueden seguir la misma definición y tener edades distintas. Escribes los nombres de los campos una sola vez; cada objeto obtiene sus propios valores.

Lee una clase pequeña
public class Pet {
    private String name;
    private String species;
    private int age;

    public Pet(String name, String species, int age) {
        this.name = name;
        this.species = species;
        this.age = age;
    }

    public void celebrateBirthday() {
        this.age++;
    }
}

Lee esto de fuera hacia dentro. public class Pet introduce el tipo y las llaves exteriores contienen su definición. Las tres primeras declaraciones describen el estado. El bloque llamado Pet es un constructor: da a un objeto nuevo sus primeros valores. celebrateBirthday es un método: aumenta la edad del objeto actual. Las palabras private y public controlan el acceso; los ejemplos posteriores las utilizan para proteger el estado.

Los parámetros del constructor son valores que se proporcionan al crear un objeto. En this.age = age;, el lado izquierdo designa el campo del objeto y el derecho designa el parámetro proporcionado. El constructor almacena esa entrada para que el objeto pueda recordarla una vez terminada la construcción.

Pon una clase a trabajar

Ya has utilizado clases de bibliotecas. Scanner describe objetos que leen datos de entrada, ArrayList describe listas y un objeto String ofrece operaciones como length(). Una clase que escribes sigue la misma idea básica: pone un tipo útil a disposición del resto de tu programa.

Sin Pet, un programa podría pasar un nombre, una especie y una edad por separado a muchos métodos. Debe mantener correctamente unido cada grupo. Un objeto Pet lleva los valores relacionados como una sola unidad. Los métodos de ese objeto saben qué edad deben utilizar.

Organiza y nombra el código

En los archivos de código fuente habituales, una clase pública llamada Pet se guarda en Pet.java, con las mismas mayúsculas. Una clase Main separada puede contener el método main del programa, crear mascotas y llamar a sus métodos. Pet no necesita un main propio. Las clases del mismo paquete sin nombre pueden utilizarse entre sí sin importaciones; los ejemplos sencillos mantienen esas clases en una carpeta.

Utiliza sustantivos en UpperCamelCase para las clases, como LibraryBook. Utiliza lowerCamelCase para campos y métodos: pageCount nombra datos; giveBack() nombra una acción. Son convenciones que ayudan a quien lee a reconocer cada parte, aunque el compilador permita muchos otros nombres.

Puntos clave

  • Una clase define un tipo; un objeto es una instancia creada mientras se ejecuta el programa.
  • Los campos almacenan el estado y los métodos proporcionan el comportamiento.
  • Los objetos de una clase siguen la misma definición y almacenan sus propios valores de campos de instancia.
  • Una clase pública en un archivo de código fuente habitual utiliza el nombre correspondiente de la clase como nombre de archivo.

1.2 Objetos nuevos y las referencias que permiten acceder a ellos

Crear un objeto y declarar una variable son operaciones distintas. Una variable te da un nombre mediante el que acceder a un objeto; new crea el objeto al que hará referencia ese nombre.

Lee una sentencia de creación de objetos
Pet luna = new Pet("Luna", "cat", 3);

Pet es el tipo de la variable y luna es su nombre. new Pet(...) crea un objeto nuevo y llama a un constructor compatible con tres argumentos. El constructor almacena el nombre, la especie y la edad. La referencia resultante se asigna a luna.

Una referencia indica a Java qué objeto debe utilizar. Es útil dibujar una variable como un nombre con una flecha hacia un objeto. La variable no es una segunda copia de todos los campos del objeto.

Llama a un método de un objeto concreto

El punto en luna.celebrateBirthday() conecta una referencia con un método que se ejecuta sobre su objeto. Un método que devuelve un valor puede proporcionarlo a otra expresión:

luna.celebrateBirthday();
int years = luna.getAge();
System.out.println(years); // 4

Aquí getAge() es un método público que devuelve la edad de la mascota. La llamada de cumpleaños cambia el estado; el método de acceso lee ese estado. Pet.celebrateBirthday() falla porque este método de instancia necesita una mascota concreta. El nombre de la clase por sí solo no responde de quién es el cumpleaños.

Objetos separados y referencias compartidas
Pet max = new Pet("Max", "dog", 5);
luna.celebrateBirthday();
System.out.println(luna.getAge()); // 5
System.out.println(max.getAge());  // 5

Luna ya tenía 4 años, así que el siguiente cumpleaños la lleva a 5. Max sigue teniendo 5. Tener la misma edad no hace que sean el mismo objeto: las dos expresiones new crearon mascotas distintas.

Considera ahora una asignación sin new:

Pet other = luna;
other.celebrateBirthday();
System.out.println(luna.getAge()); // 6

La asignación copia la referencia de Luna. Ambos nombres acceden a una sola mascota, por lo que cambiarla mediante other es visible mediante luna. Para crear una segunda mascota, llama de nuevo a new.

Haz coincidir los parámetros del constructor

Los argumentos deben ajustarse al número, orden y tipos de los parámetros de un constructor declarado. Si el único constructor disponible recibe (String, String, int), new Pet() no compila. Java no adivina los valores que faltan. Sin embargo, intercambiar las dos cadenas sí compila: new Pet("cat", "Luna", 3) proporciona los tipos correctos, pero los significados equivocados. Lee los nombres de los parámetros para comprobar ese orden.

Estos dos intentos incompletos fallan por distintas razones:

Pet a;                         // declaration only
// System.out.println(a.getAge()); // a has no assigned value
// Pet b = Pet("Bo", "cat", 2);    // missing new

Una variable local debe tener un valor asignado antes de utilizarse. Sin new, Java busca un método llamado Pet, no una expresión de creación de objetos. Una vez que existe un objeto, al imprimirlo puede mostrarse un texto como Pet@8bcc55f hasta que proporciones un método toString() legible; esa salida no significa que la construcción haya fallado.

Puntos clave

  • Utiliza new con argumentos que coincidan con un constructor disponible.
  • La notación de punto llama a un método de instancia sobre el objeto al que accede una referencia.
  • Otra expresión new crea otro objeto; la asignación de una referencia comparte uno existente.
  • Los argumentos del mismo tipo pueden intercambiarse sin producir un error del compilador, así que comprueba sus significados.
  • Una declaración local por sí sola no crea ningún objeto ni da a la variable un valor utilizable.
2

Campos y estado inicial de un objeto

Los objetos necesitan un lugar donde guardar sus datos y una forma fiable de obtener sus primeros valores. Los campos proporcionan el almacenamiento; los constructores inicializan cada objeto nuevo. Elegirás tipos de campos, separarás el estado duradero del trabajo local temporal y seguirás la construcción desde los valores predeterminados hasta obtener un objeto listo para usar. Después ofrecerás varias formas útiles de crear el mismo tipo de objeto, manteniendo la inicialización y las comprobaciones compartidas en un solo lugar.

2.1 Datos que recuerda un objeto

Un objeto necesita un campo para cada valor que debe recordar entre llamadas a métodos. Elige esos valores antes de escribir los métodos: forman el estado duradero del objeto.

Declara campos con tipos adecuados

Un campo se encuentra dentro de las llaves de la clase, pero fuera de todos los constructores y métodos. Una declaración necesita un tipo y un nombre; también pueden aparecer un modificador de acceso y un inicializador.

private String title;
private int pageCount;
private boolean lentOut = false;

String es adecuado para texto, int para un recuento de números enteros y boolean para una condición de sí o no. Los campos también pueden utilizar tipos de clase como ArrayList<String>. Estos ejemplos utilizan private; omitir el modificador da acceso de paquete, no acceso privado.

Cada objeto obtiene una copia separada de cada campo de instancia. Dos objetos Rectangle tienen cada uno un width y un height, aunque las declaraciones aparezcan una sola vez en la clase.

Los valores predeterminados no siempre son valores iniciales utilizables
Tipo de campoValor predeterminado
int, long0
double0.0
booleanfalse
Tipos de clase, incluidos String y ArrayListnull

Se asigna un valor predeterminado a un campo cuando se crea el espacio de almacenamiento del objeto. Puede que no se ajuste a la finalidad del objeto. Un campo de tipo lista con el valor null no es una lista vacía: todavía necesitas crear la lista antes de añadir un elemento.

Las variables locales no reciben ese valor predeterminado. Dentro de un método, int total; seguido de System.out.println(total); no compila porque no se ha asignado un valor a total. Distingue la regla de los campos de la de las variables locales.

Decide qué debe perdurar
Tipo de variableDónde se declaraPara qué sirve
Campo de instanciaEn la clase, fuera de los métodosValores que recuerda un objeto
ParámetroEn la cabecera de un constructor o métodoEntradas para esa llamada
Variable localDentro de un bloque de método o constructorTrabajo temporal para esa llamada

Un campo permanece con su objeto. Un parámetro está disponible durante su llamada y una variable local está disponible en su bloque mientras se ejecuta esa llamada. Si un contador debe conservar su cuenta de una llamada a la siguiente, la cuenta pertenece a un campo. Un subtotal temporal utilizado para un cálculo puede ser local.

Almacena las entradas; calcula los valores derivados
public class Rectangle {
    private double width;
    private double height;

    public Rectangle(double width, double height) {
        this.width = width;
        this.height = height;
    }

    public double area() {
        return this.width * this.height;
    }
}

Las dimensiones están en centímetros, por lo que area() devuelve un área en centímetros cuadrados. Almacenar un campo separado para el área crearía otro valor que habría que actualizar cada vez que cambiara una dimensión. Calcularlo a partir de las dimensiones mantiene la coherencia del resultado.

Utiliza un campo de instancia final para un valor que se asigna una sola vez, como private final String title;. Asígnale un valor en su declaración o en cada constructor. final impide la reasignación. static tiene otro significado: crea un campo de clase compartido. Añadir static al nombre de una mascota haría que distintas mascotas compartieran una sola variable de nombre.

Puntos clave

  • Los campos de instancia se declaran fuera de los métodos y recuerdan el estado de cada objeto.
  • Los campos reciben valores predeterminados; a las variables locales se les debe asignar un valor antes de usarlas.
  • Elige campos para los datos duraderos y variables locales para el trabajo temporal.
  • Calcula los valores que se derivan de campos almacenados para evitar copias incoherentes.
  • Un campo de instancia final se asigna una vez; un campo estático es compartido por la clase.
  • Un campo de tipo lista con valor null sigue necesitando un objeto de lista antes de poder utilizarse.

2.2 De los argumentos a los campos inicializados

Un constructor inicializa un objeto cuando new lo crea. Su nombre debe coincidir con el de la clase y no tiene tipo de retorno. Incluso void convertiría la declaración en un método.

Transfiere las entradas al estado
public Pet(String name, String species, int age) {
    this.name = name;
    this.species = species;
    this.age = age;
}

Un constructor suele aceptar entradas como parámetros y almacenarlas en campos. Aquí cada parámetro tiene el mismo nombre que su campo. El nombre age por sí solo designa el parámetro; this.age designa el campo del objeto que se está creando. La asignación lee el valor proporcionado a la derecha y escribe el campo de la izquierda.

Para new Pet("Luna", "cat", 3), primero los campos del objeto nuevo reciben valores predeterminados: null, null y 0. Después el constructor recibe los tres argumentos y sustituye esos valores. Cuando termina la construcción, new produce la referencia que almacena quien lo llamó.

Dos errores que compilan
public Pet(String name, String species, int age) {
    name = name;
    this.species = species;
    this.age = age;
}

Ambas apariciones de name hacen referencia al parámetro, por lo que la primera línea asigna ese parámetro a sí mismo. El nombre del objeto permanece en null. El código es válido, por eso es importante comprobar los valores del objeto nuevo.

Una declaración dentro del constructor causa un problema relacionado:

String nick = initialNick;

Si ya existe un campo llamado nick, esta línea crea una variable local con el mismo nombre. La variable local desaparece cuando termina la construcción. Escribe this.nick = initialNick; para almacenar la entrada en el campo; no repitas el tipo.

Identifica cuándo existe un constructor sin argumentos

Si una clase no declara constructores, Java proporciona un constructor predeterminado sin parámetros. En una clase sencilla, sus campos conservan sus inicializadores declarados o valores predeterminados. En cuanto declaras cualquier constructor, Java deja de proporcionar ese constructor predeterminado.

Por tanto, una clase que solo tiene Pet(String name, String species, int age) no puede crearse con new Pet(). Añade un constructor explícito sin argumentos solo cuando exista un estado inicial útil para él. Presta atención también a public void Pet(...): es un método, por lo que no proporciona el constructor previsto.

Establece también los valores iniciales internos

No todos los campos tienen que proceder de un argumento. Un libro nuevo siempre puede empezar con this.lentOut = false;. Una clase que posee una lista puede inicializarla así, con ArrayList importado:

private ArrayList<String> tracks;

public Playlist() {
    this.tracks = new ArrayList<>();
}

La lista vacía está lista para add. Dejar tracks en null hace que una llamada posterior a tracks.add(...) falle con NullPointerException.

Un inicializador de campo se ejecuta antes del cuerpo del constructor. Si private int score = 10; va seguido de this.score = 5; en el constructor, la puntuación del objeto terminado es 5. Prueba los valores después de la construcción, no solo los valores escritos junto a las declaraciones de los campos. Los constructores también necesitan las mismas reglas de validación que los cambios posteriores, para que el primer estado sea válido.

Puntos clave

  • Un constructor tiene el nombre de la clase y no tiene tipo de retorno.
  • Utiliza this.field = parameter cuando los nombres coincidan.
  • Ni la autoasignación de un parámetro ni una nueva variable local inicializan el campo previsto.
  • Java proporciona un constructor predeterminado solo cuando la clase no declara constructores.
  • Inicializa el estado interno, como las listas vacías, y aplica la validación durante la construcción.

2.3 Varias formas de construir un mismo tipo de objeto

Una clase puede ofrecer varias formas útiles de crear un objeto. La sobrecarga de constructores consiste en declarar constructores con distintas listas de parámetros.

Distingue los constructores por sus parámetros

Java distingue los constructores por el número, los tipos y el orden de sus parámetros. Los nombres de los parámetros no los distinguen. Box(String label) y Box(String name) describen la misma lista de parámetros, por lo que no pueden declararse ambos en una misma clase.

Una mascota puede crearse con una edad o como recién nacida:

public Pet(String name, String species, int age) {
    this.name = name;
    this.species = species;
    this.age = age;
}

public Pet(String name, String species) {
    this(name, species, 0);
}

new Pet("Pip", "rabbit") selecciona el constructor de dos parámetros. Ese constructor proporciona 0 como edad que falta y llama al constructor de tres parámetros. El objeto nuevo termina con el estado "Pip", "rabbit" y 0.

Encadena la inicialización del mismo objeto

Dentro de un constructor, this(...) llama a otro constructor de la misma clase. El constructor más breve elige los valores predeterminados; el más completo realiza la inicialización compartida. Las comprobaciones situadas en esa inicialización se aplican a ambas vías de construcción.

Compara dos líneas que se parecen:

Línea dentro de un constructorEfecto
this(name, species, 0);Inicializa el objeto actual mediante otro constructor
new Pet(name, species, 0);Crea una mascota distinta

La segunda línea no rellena los campos del objeto actual. Si su resultado nunca se almacena, esa mascota adicional se descarta. La mascota actual puede seguir teniendo campos con null.

Sigue una cadena antes de recorrer el código posterior

Un reloj puede ofrecer la medianoche como valor predeterminado:

public Clock() {
    this(0, 0);
}

public Clock(int hours, int minutes) {
    this.hours = hours;
    this.minutes = minutes;
}

Para new Clock(), entra en el constructor sin argumentos, sigue this(0, 0) y asigna ambos campos en el constructor de dos argumentos. Después el control regresa al constructor sin argumentos. Esto crea un reloj, no dos. Una llamada a new Clock(9, 5) comienza directamente en el constructor más completo.

Mantén la cadena válida y clara

Un constructor puede contener como máximo una llamada a this(...). La cadena debe llegar finalmente a una inicialización que no vuelva a llamar a la cadena: dos constructores que delegan el uno en el otro producen un error de compilación por constructor recursivo.

Poner this(...) primero es un estilo claro que funciona en todas las versiones de Java. En Java 24 y versiones anteriores sin características preliminares, debe ir primero. Java 25 permite sentencias anteriores para tareas como calcular o comprobar argumentos. Esas sentencias no pueden leer los campos de instancia del objeto actual ni llamar a sus métodos de instancia; se permiten asignaciones a campos declarados en la clase sin inicializadores en su declaración.

Ofrece sobrecargas porque quienes llaman disponen de distintas entradas útiles. Un reloj que empieza a medianoche tiene sentido; un libro sin título puede que no. La existencia de un constructor completo no crea automáticamente sobrecargas más breves.

Puntos clave

  • Los constructores sobrecargados se distinguen por sus listas de parámetros, no por los nombres de estos.
  • La llamada this(...) delega en otro constructor sobre el mismo objeto.
  • Mantén la inicialización y las comprobaciones compartidas en una sola vía de construcción.
  • Una cadena de constructores no debe volver a un constructor que ya esté en esa cadena.
3

Métodos que leen y modifican el estado

Un objeto útil puede responder preguntas sobre sí mismo y realizar acciones que modifiquen su estado. Los métodos de instancia sitúan esas operaciones junto a los campos que utilizan. Seguirás los cambios sentencia por sentencia, manejarás el cambio de hora de un reloj y devolverás valores que otro código pueda utilizar. También decidirás qué datos pertenecen a cada objeto y qué datos u operaciones pertenecen a la clase, para que los objetos distintos sigan siendo independientes.

3.1 Cálculos, cambios y seguimiento del estado

Un método de instancia se ejecuta para un objeto concreto. Decláralo sin static y utiliza los campos de ese objeto mediante nombres como this.width. Decide qué necesita quien lo llama: un cambio de estado, un resultado devuelto o ambas cosas.

Devuelve cálculos; haz explícitos los cambios de estado

Para un rectángulo cuyas dimensiones se almacenan en centímetros:

public double area() {
    return this.width * this.height;
}

public double perimeter() {
    return 2 * (this.width + this.height);
}

public void scale(double factor) {
    this.width = this.width * factor;
    this.height = this.height * factor;
}

Un rectángulo con lados de 3,0 y 4,0 tiene un área de 12,0 centímetros cuadrados y un perímetro de 14,0 centímetros. scale(2) cambia los lados a 6,0 y 8,0. Las siguientes llamadas devuelven un área de 48,0 y un perímetro de 28,0. Cada método lee los campos actuales, por lo que no es necesario actualizar un área almacenada.

area() devuelve un número que puede almacenarse o utilizarse en un cálculo. Si solo imprimiera el número y tuviera el tipo de retorno void, quien lo llama no podría escribir double twice = r.area() * 2;. Deja la impresión a quien llama al método cuando la función de este sea calcular un valor.

Actualiza los campos relacionados en orden

El método advance() de un reloj debe gestionar tanto el cambio de minuto como el de hora:

public void advance() {
    this.minutes++;
    if (this.minutes == 60) {
        this.minutes = 0;
        this.hours++;
        if (this.hours == 24) {
            this.hours = 0;
        }
    }
}

A las 23:58, la primera llamada llega a 23:59 y omite ambos bloques de reinicio. La siguiente llamada llega brevemente al minuto 60, lo reinicia a 0, aumenta la hora a 24 y la reinicia a 0. El estado final es 00:00. Una llamada más da 00:01.

Sigue los objetos por separado

Escribe una fila después de cada sentencia y una columna para los campos de cada objeto. Cambia solo el objeto indicado por el receptor de la llamada. Para mascotas de 2 y 7 años, dos cumpleaños de la primera y setAge(1) en la segunda dejan las edades en 4 y 1. No ejecutes el cuerpo de un método para cada instancia solo porque comparten una clase.

Una declaración local puede ocultar el campo que querías actualizar. public void reset() { int count = 0; } solo crea una variable temporal. this.count = 0; reinicia el contador duradero.

Reutiliza métodos y completa las rutas de retorno

Dentro de un método de instancia, area() y this.area() llaman al método de área del mismo objeto. Un método de descripción puede reutilizar ese cálculo en lugar de copiar su expresión. Un método también puede cambiar el estado y devolver si tuvo éxito, como hace lend() cuando marca un libro como prestado e indica true.

Un método que no es void no puede simplemente llegar al final sin devolver un valor compatible. Si get() solo devuelve un valor dentro de if (count > 0), ¿qué ocurre cuando el contador es 0? Añade la ruta de retorno necesaria o lanza una excepción para un valor que el método rechace. Una ruta que lanza una excepción no regresa a quien llamó al método y no necesita un valor de retorno.

Puntos clave

  • Los métodos de instancia leen o modifican el estado del objeto sobre el que se llaman.
  • Devuelve los valores calculados para que quienes llaman a los métodos puedan utilizarlos en expresiones.
  • Sigue las sentencias en orden y mantén separados los campos de cada objeto.
  • Los métodos que gestionan cambios de unidad deben devolver cada campo relacionado a su rango permitido.
  • Reutiliza otros métodos del mismo objeto llamándolos directamente o con this.
  • Un método que devuelve un valor necesita uno en cada ruta que retorna normalmente.

3.2 Miembros de cada objeto y miembros compartidos de la clase

Pregúntate a quién pertenece un valor o una operación antes de decidir si utilizas static. La edad de una mascota pertenece a una mascota. Un recuento de todas las mascotas creadas pertenece a la clase en su conjunto.

El objeto actual es this

Dentro de un constructor o método de instancia, this hace referencia al objeto que se está construyendo o que recibe la llamada. En this.age++, identifica qué edad cambia. En this.name = name, distingue el campo de un parámetro con el mismo nombre. this(...) es la forma de encadenar constructores.

Un método estático no tiene un objeto actual, por lo que this no puede utilizarse allí. Aun así puede crear un objeto o recibir una referencia y utilizarla para llamar a métodos de instancia.

Campos compartidos y campos de cada objeto
public class Counter {
    private int count;
    private static int created = 0;

    public Counter() {
        this.count = 0;
        created++;
    }

    public void increment() {
        this.count++;
    }

    public int getCount() {
        return this.count;
    }

    public static int getCreated() {
        return created;
    }
}

Después de dos llamadas a new Counter(), hay dos campos count separados, ambos inicialmente en 0, y un campo created igual a 2. Incrementar el primer contador cambia su cuenta a 1; el segundo permanece en 0. Counter.getCreated() sigue devolviendo 2 porque no se ha construido ningún objeto nuevo.

Trabaja a través de un objeto desde main
public static void main(String[] args) {
    Counter c = new Counter();
    c.increment();
    System.out.println(c.getCount());
}

El método main clásico es estático. Una llamada a increment() por sí sola allí no tendría un objeto de destino y no compilaría. La variable c proporciona ese destino. Hacer que count sea estático para eliminar el error cambiaría el diseño: todos los contadores compartirían entonces una sola cuenta.

Tipo de métodoAcceso directo disponible
Método de instanciaSus miembros de instancia y los miembros estáticos de la clase
Método estáticoMiembros estáticos; miembros de instancia mediante una referencia a un objeto

Java 25 también admite formas de instancia de main, pero la cabecera estática clásica sigue siendo válida. Estos ejemplos la utilizan de forma coherente.

Elige métodos y constantes de forma deliberada

Una operación que necesita los campos de un objeto debería ser un método de instancia. Una operación que solo depende de sus parámetros puede ser estática, como un método auxiliar que convierte un número total de minutos en horas. Las llamadas estáticas suelen utilizar el nombre de la clase: Math.sqrt(16) o Counter.getCreated().

Un valor fijo compartido suele declararse como constante:

public static final int MAX_CELSIUS = 30;

public lo pone a disposición de quienes llaman, static da a la clase un campo compartido y final impide la reasignación. UPPER_SNAKE_CASE indica una constante por convención. Esta es una excepción habitual a la regla de hacer privados los campos. No utilices static para nombres, dimensiones u otros datos de estado que deban ser distintos entre objetos.

Puntos clave

  • La referencia this identifica el objeto actual dentro del código de instancia.
  • Los miembros estáticos pertenecen a la clase; los miembros de instancia pertenecen a objetos individuales.
  • Un método estático necesita una referencia a un objeto para utilizar miembros de instancia.
  • Corrige los errores de contexto estático proporcionando el objeto adecuado cuando el estado pertenece a un objeto.
  • Un campo public static final suele proporcionar una constante compartida.
4

Acceso controlado y estado válido

Una clase debería controlar los valores que pueden formar parte de su estado. Los campos privados y los métodos públicos permiten a la clase comprobar cada cambio y ofrecer operaciones útiles a quienes la utilizan. Elegirás niveles de acceso, escribirás métodos de acceso y modificadores cuando sean útiles y protegerás reglas como un rango de temperatura permitido. También verás por qué una lista privada puede seguir expuesta mediante un método de acceso y cómo impedir que el código externo eluda las comprobaciones de la clase.

4.1 Estado privado e interfaz pública

La encapsulación mantiene el estado de un objeto junto al código que lo controla. Los campos suelen ser privados, mientras que quienes utilizan el objeto recurren a operaciones públicas. Esto oculta los detalles de almacenamiento y da a la clase un lugar donde aplicar sus reglas.

Elige un nivel de acceso
AccesoQuién puede utilizar el miembro
privateCódigo de la misma clase
Sin modificadorCódigo del mismo paquete
protectedEl mismo paquete, además del acceso de las subclases según las reglas de Java
publicCódigo que puede acceder al tipo que lo declara

Un paquete agrupa clases. Los ejemplos sencillos sitúan clases habituales juntas sin una declaración package, en un mismo paquete sin nombre. El acceso de paquete se obtiene al omitir el modificador; no hay ninguna palabra clave de acceso package que añadir a un campo.

Empieza con el acceso útil más restringido. Haz que los campos sean privados, que los constructores y operaciones que necesitan otras clases sean públicos y que los métodos auxiliares internos sean privados. Las constantes públicas son una excepción habitual para los campos.

Deja que la clase controle los cambios

Si Pet declara private int age;, una clase Main independiente no puede asignar luna.age = -5;. El compilador rechaza el acceso. Una llamada como luna.setAge(-5) puede permitirse, porque el método público pertenece a Pet y puede decidir si almacena el argumento.

Un campo público permitiría a cualquiera que lo utilice eludir ese método. Cuando todos los cambios pasan por el código de la clase, las comprobaciones pueden estar en un solo lugar. El acceso privado por sí solo no valida una edad; los métodos también deben realizar las comprobaciones.

Mantén estable la interfaz pública

Supón que un reloj almacena inicialmente los campos hours y minutes por separado. Quienes lo utilizan llaman a getHours() y getMinutes(). Más tarde sustituyes los campos por un único valor minutesSinceMidnight:

public int getHours() {
    return this.minutesSinceMidnight / 60;
}

public int getMinutes() {
    return this.minutesSinceMidnight % 60;
}

Con 615 minutos almacenados, la división devuelve 10 y el resto devuelve 15. El reloj indica 10:15 mediante los mismos métodos. No es necesario reescribir el código que los llama porque dependía de las operaciones públicas, no de cómo se almacenaba el estado. Esta libertad para cambiar los detalles internos es la ocultación de información.

El acceso pertenece a las clases, no a los objetos individuales

El código dentro de Pet puede leer un campo privado de otro objeto Pet. El permiso de acceso procede de estar dentro de la clase. Una clase de nivel superior distinta no obtiene permiso por tener una referencia a una mascota o por estar en la misma carpeta.

Para las declaraciones habituales de clases de nivel superior, las opciones de acceso son public o ningún modificador. Un private class Pet de nivel superior no está permitido. Una clase pública de nivel superior va en el archivo de código fuente correspondiente, y un archivo de código fuente habitual contiene como máximo una clase pública de nivel superior.

Los campos privados son un límite de diseño del programa. Impiden que otro código ordinario dependa directamente de los campos internos, pero no son una forma de mantener un valor en secreto frente a una persona que pueda inspeccionar el programa. Utilízalos para controlar la interfaz de tu clase y proteger su estado.

Puntos clave

  • Los campos privados y las operaciones públicas mantienen el control del estado dentro de la clase.
  • Omitir un modificador de acceso da acceso de paquete.
  • Los métodos públicos permiten cambiar el almacenamiento interno sin cambiar el código que los llama.
  • El acceso privado se comprueba por clase, por lo que el código dentro de la clase puede acceder a los campos de otra instancia.
  • Las clases habituales de nivel superior utilizan acceso público o de paquete.

4.2 Métodos de acceso adecuados para la clase

Una clase puede permitir que quienes la utilizan lean o cambien determinadas partes de su estado mediante métodos. Decide qué operaciones debería ofrecer la clase antes de crear métodos de acceso y modificadores para cada campo.

Devuelve valores con métodos de acceso

Un método de acceso, también llamado método de consulta, devuelve información sin modificar el objeto. Un método de acceso habitual lee un campo:

public String getName() {
    return this.name;
}

public int getAge() {
    return this.age;
}

El tipo devuelto coincide con la información proporcionada. Un método de acceso que imprime y devuelve void no puede proporcionar un valor a una expresión como pet.getAge() + 1. Quien lo llama debería recibir el valor y decidir cómo utilizarlo.

Cambia determinados valores con métodos modificadores

Un método modificador, también llamado método de mutación, recibe un valor nuevo y actualiza el estado:

public void setName(String name) {
    this.name = name;
}

this.name es el campo. name es el parámetro. Escribir name = name; compila, pero no cambia ningún campo. Aquí no hay valor de retorno porque la función de este método modificador es realizar un cambio.

Para una mascota de 3 años:

luna.setName("Lulu");
System.out.println(luna.getName() + " is " + luna.getAge());
// Lulu is 3

Quien llama a los métodos nunca toca un campo directamente. El método modificador cambia el nombre y los métodos de acceso devuelven los valores actuales para que quien los llama los combine.

Sigue los nombres que espera otro código Java
CampoMétodo de accesoMétodo modificador, si es necesario
pageCountgetPageCount()setPageCount(int pageCount)
namegetName()setName(String name)
lentOutisLentOut()setLentOut(boolean lentOut)

Escribe en mayúscula la primera letra del nombre del campo después de get, set o el prefijo booleano habitual is. Un nombre como getname() puede compilar, pero no sigue la convención que esperan las herramientas y otros programadores.

Expón solo operaciones útiles

El título de un libro puede tener un método de acceso y ningún método modificador porque quienes lo utilizan deberían poder leerlo, pero no sustituirlo. Un campo privado final puede garantizar que se asigne una sola vez. Un contador interno puede no necesitar ninguno de los dos métodos si solo lo utiliza la clase.

Un método modificador sin comprobaciones puede aceptar el mismo valor perjudicial que un campo público. Añade comprobaciones cuando un cambio tenga restricciones. Considera también un método de acción: celebrateBirthday() expresa el aumento de la edad en uno y lend() expresa un intento de préstamo. lend() puede inspeccionar el estado actual y rechazar un segundo préstamo. Un método modificador también puede realizar esa comprobación; el nombre de la acción hace que la operación resulte más clara para quien la llama.

Un método de acceso puede calcular una respuesta. getArea() puede devolver el producto de la anchura y la altura sin ningún campo area.

Puntos clave

  • Un método de acceso devuelve información; un método modificador cambia un valor determinado.
  • Sigue el patrón de nombres getName, setName y el habitual isName para booleanos.
  • Da a los campos solo los métodos de acceso y modificadores que necesite quien los utiliza.
  • Los métodos de acción como lend expresan claramente el comportamiento permitido y pueden comprobar las transiciones.

4.3 Reglas que se mantienen tras cada cambio

Una invariante es una regla sobre el estado de un objeto que se cumple después de la construcción y después de cada operación pública. Un termostato podría permitir temperaturas objetivo de 5 °C a 30 °C. Todas las formas de establecer su temperatura objetivo deben conservar ese rango.

Decide qué significa una entrada no válida

Tres respuestas útiles son conservar el valor anterior, limitar el valor a un extremo permitido o lanzar una excepción. Tienen efectos distintos, así que elige una respuesta clara para cada operación.

RespuestaResultado de una llamada no válida
IgnorarEl valor anterior del campo permanece sin cambios
Limitar al rangoSe almacena el extremo permitido más cercano
Lanzar una excepciónLa llamada se detiene con un error como IllegalArgumentException

Para una edad no negativa de una mascota, un método modificador que ignora valores no válidos puede asignar solo dentro de if (age >= 0). Si la edad almacenada es 4, setAge(-2) la mantiene en 4. No convierte la edad en 0.

Pon las comprobaciones del rango dentro de la clase
public static final int MIN_CELSIUS = 5;
public static final int MAX_CELSIUS = 30;
private int target;

public void setTarget(int celsius) {
    if (celsius < MIN_CELSIUS) {
        this.target = MIN_CELSIUS;
    } else if (celsius > MAX_CELSIUS) {
        this.target = MAX_CELSIUS;
    } else {
        this.target = celsius;
    }
}

public void raise() {
    setTarget(this.target + 1);
}

Partiendo de 20 °C, setTarget(35) almacena 30. setTarget(-3) almacena 5. Después de setTarget(29), una llamada a raise() almacena 30; otra llamada sigue almacenando 30. Reutilizar el método modificador impide que raise() sobrepase el límite superior.

Las comprobaciones solo en Main no pueden proteger el termostato. Otro código que lo llame podría olvidarlas. La clase debe comprobar la entrada en el lugar donde controla el almacenamiento.

Valida también el primer estado
public Thermostat(int target) {
    setTarget(target);
}

Con este constructor que limita al rango permitido, new Thermostat(50) termina en 30 °C. Asignar directamente this.target = target; permitiría el estado inicial no válido de 50. Un método privado de comprobación compartido es otra forma de reutilizar la validación en constructores y métodos posteriores.

Lanzar una excepción es útil cuando un argumento no válido señala un error de programación. Por ejemplo, un constructor puede rechazar un recuento negativo con throw new IllegalArgumentException("count must not be negative");. El objeto no se construye correctamente, por lo que no se entrega a quien lo llamó ningún objeto terminado en un estado no válido. Ignorar la entrada es más sencillo, pero silencioso; quien llama al método puede necesitar otra forma de advertir que nada ha cambiado.

Mantén los datos internos mutables dentro de la clase

Una lista privada puede seguir expuesta si un método de acceso devuelve la lista original. Quien lo llama podría entonces añadir o eliminar entradas sin utilizar los métodos de la clase, eludiendo una regla de tamaño máximo.

Para un campo ArrayList<String> llamado tracks, return new ArrayList<>(this.tracks); proporciona una lista independiente que contiene las mismas cadenas. Cambiar esa lista devuelta no modifica la lista interna. Operaciones como addTrack(String title) pueden mantener los cambios de la lista dentro de la clase y comprobar sus reglas antes de aceptarlos.

Puntos clave

  • Una invariante se cumple después de la construcción y después de cada operación pública.
  • Ignorar, limitar al rango permitido y lanzar una excepción son respuestas distintas a una entrada no válida.
  • Cada constructor y cada método que cambia el estado deben mantener las mismas reglas.
  • Reutiliza la validación para que las distintas vías de cambio no puedan eludirla.
  • Devolver una lista interna mutable permite a quienes la reciben eludir las comprobaciones de la clase.
  • Una copia de una lista interna de cadenas protege la lista original de cambios externos.
5

Objetos legibles y clases completas

Las descripciones legibles de los objetos facilitan el uso y la comprobación de una clase. Un método toString proporciona ese texto cuando se imprime un objeto o se une a una cadena. Escribirás el método con la cabecera correcta y después reunirás campos, constructores, métodos y validación en una clase completa. A partir de una breve descripción, convertirás los requisitos en código y probarás llamadas normales, acciones repetidas y casos límite.

5.1 Una descripción del objeto devuelta como texto

Un objeto útil debería ser fácil de inspeccionar como texto. El método incorporado toString() de Java devuelve un nombre de clase, un signo @ y un código hash hexadecimal, como Pet@8bcc55f. Un paquete con nombre pasa a formar parte del nombre de la clase. El texto del hash varía y suele decir poco sobre el estado del objeto.

Devuelve una descripción legible
@Override
public String toString() {
    return this.name + " (" + this.species + ", "
        + this.age + " years)";
}

Para Luna, una gata de 3 años, la cadena devuelta es "Luna (cat, 3 years)". El método lee los campos y construye una cadena. No modifica la mascota ni envía salida a la pantalla.

@Override pide al compilador que compruebe que la declaración realmente sustituye un método existente. Eso detecta errores como escribir tostring con una s minúscula. No da formato al resultado por ti; la expresión de retorno determina el texto.

Reconoce las llamadas automáticas
System.out.println(luna);
String label = "Pet: " + luna;
String details = luna.toString();

La primera línea imprime la descripción legible de la mascota. La segunda construye "Pet: Luna (cat, 3 years)". La tercera solicita explícitamente la misma descripción y la almacena sin imprimirla. Cuando se imprime un ArrayList de mascotas, su presentación utiliza el toString() de cada mascota dentro de los corchetes de la lista.

La conversión automática explica por qué es importante devolver texto. Quien llama al método puede estar imprimiendo, almacenando o uniendo el resultado. Un método que solo imprime no puede proporcionar la cadena que necesitan todos esos usos.

Haz coincidir la cabecera del método
DeclaraciónQué ocurre
public String toString()Proporciona la descripción del objeto
public String tostring()Declara un método distinto; @Override lo detecta
public String toString(int width)Declara un método sobrecargado; la impresión automática no lo llama
public void toString()Falla: entra en conflicto con el método existente que devuelve String

Utiliza la cabecera exacta sin argumentos e incluye la anotación. Un método sobrecargado con parámetros puede ser un método independiente válido, pero imprimir un objeto no proporciona esos parámetros.

Da formato a los valores sin cambiarlos

Un reloj necesita horas y minutos de dos dígitos. Dentro de su método toString():

return String.format(java.util.Locale.US,
    "%02d:%02d", this.hours, this.minutes);

Para 9 horas y 5 minutos, la cadena es "09:05". Cada %02d da formato a un entero en al menos dos posiciones, utilizando un cero inicial cuando es necesario. String.format(java.util.Locale.US, "%.2f", 7.5) devuelve "7.50". Proporcionar la configuración regional hace que la salida con punto decimal sea independiente de la configuración regional de formato predeterminada del equipo.

Mantén la descripción lo bastante breve para leerla durante la depuración. Puede revelar un estado sin inicializar: unir un campo de nombre null a una cadena produce el texto "null", lo que te lleva de vuelta a la inicialización. La descripción debería informar del estado actual, por lo que imprimir el objeto dos veces no debería hacer avanzar un reloj ni celebrar un cumpleaños.

Puntos clave

  • Escribe public String toString() sin parámetros y devuelve texto legible.
  • La anotación @Override permite al compilador comprobar que el método sustituye uno existente.
  • Imprimir un objeto y unirlo a una cadena utilizan toString automáticamente.
  • Mantén toString libre de cambios de estado y deja que quien lo llama decida adónde va el texto.
  • String.format puede rellenar valores y utilizar una configuración regional explícita para producir una salida predecible.

5.2 De un requisito a una clase probada

Empieza por lo que debe recordar un objeto, lo que quienes lo utilizan pueden pedirle que haga y qué estados deben permitirse. Después convierte cada parte en campos, constructores y métodos.

Extrae el diseño de una descripción

«Un libro de biblioteca tiene un título, un autor y un número de páginas. Se puede prestar y devolver. No se puede volver a prestar mientras ya esté prestado».

Utiliza LibraryBook como nombre de la clase. Almacena los tres valores descriptivos y un estado de préstamo booleano. El título, el autor y el número de páginas permanecen fijos, así que hazlos final. El estado de préstamo cambia mediante acciones con una función clara. No es necesario un método modificador para cada campo.

Construye la clase completa
public class LibraryBook {
    private final String title;
    private final String author;
    private final int pages;
    private boolean lentOut;

    public LibraryBook(String title, String author, int pages) {
        this.title = title;
        this.author = author;
        this.pages = pages;
        this.lentOut = false;
    }

    public boolean isLentOut() {
        return this.lentOut;
    }

    public boolean lend() {
        if (this.lentOut) {
            return false;
        }
        this.lentOut = true;
        return true;
    }

    public void giveBack() {
        this.lentOut = false;
    }

    @Override
    public String toString() {
        String status = this.lentOut ? "lent out" : "available";
        return this.title + " by " + this.author + ", "
            + this.pages + " pages, " + status;
    }
}

El constructor establece que el libro está disponible. lend() comprueba el estado existente antes de cambiarlo; devolver false en un segundo préstamo mantiene la regla. giveBack() hace que el libro esté disponible. El método de acceso informa del estado y toString() describe todo el libro sin modificarlo.

Escribe los resultados esperados antes de ejecutar las llamadas

En el método main clásico de una clase Main independiente:

LibraryBook b = new LibraryBook("Night Train", "A. Rivera", 320);
System.out.println(b);        // Night Train by A. Rivera, 320 pages, available
System.out.println(b.lend()); // true
System.out.println(b.lend()); // false
b.giveBack();
System.out.println(b.lend()); // true

El segundo intento de préstamo es esencial: un método que siempre active el indicador y devuelva true superaría la primera llamada, pero fallaría esta comprobación. Prueba el estado y los valores de retorno con isLentOut(). Prueba también un segundo libro para comprobar que un préstamo de un libro deja disponible al otro.

Prueba los puntos donde cambia el comportamiento

Cubre todos los constructores y métodos públicos. Para las comprobaciones de rango, prueba ambos extremos y valores justo fuera de ellos. Para un reloj, comprueba el cambio de minuto y el paso a medianoche. Para un método modificador que rechaza o ignora valores, comprueba que la entrada no válida tiene el efecto previsto sobre el estado anterior. Escribe primero el resultado esperado para que una salida inesperada no se convierta silenciosamente en tu nueva expectativa.

Construye en pequeños incrementos: primero los campos y el constructor, después una descripción legible y luego una operación y sus comprobaciones cada vez. Guarda las clases públicas habituales en los archivos correspondientes, compílalas juntas y ejecuta la clase que contiene main. Mantener las operaciones del modelo separadas de la salida en pantalla permite que la misma clase sirva para distintos programas.

Puntos clave

  • Convierte los datos que se recuerdan en campos privados y las acciones permitidas en métodos.
  • Escribe las reglas del estado antes de implementar constructores y cambios.
  • Utiliza final para valores fijos de cada objeto y evita métodos modificadores innecesarios.
  • Prueba los valores de retorno y el estado resultante, incluidas llamadas repetidas y objetos separados.
  • Comprueba los valores límite y las entradas no válidas, además de los valores habituales.
  • Construye y prueba una parte útil cada vez.
  • Mantén el comportamiento del modelo separado del programa principal que imprime los resultados.

Ejemplos de Clases y objetos en Java

5 Lecciones Interactivas

1

De una clase a dos mascotas

10 pasos
Crea objetos, identifica su estado y sigue las llamadas mediante referencias separadas y compartidas.
  1. 1
    IntroducciónUn tipo para las cosas de tu programa

    Un programa de mascotas necesita recordar juntos un nombre, una especie y una edad. Una clase da un tipo a ese grupo y ofrece métodos que lo utilizan. Cada objeto es una mascota creada a partir de esa definición.

    Sigue la diferencia entre la definición y las mascotas que produce. Después sigue el estado de dos objetos de mascotas y observa por qué una variable adicional no siempre significa un objeto adicional.

  2. 2
    AnalogíaUn plano y sus instancias

    Piensa en una clase como el plano de una construcción y en sus objetos como las casas construidas a partir de él. El plano describe lo que contiene cada casa, pero no es una casa en sí.

    Una definición de Pet puede describir muchas mascotas. Cada una tiene campos para los mismos tipos de datos, pero una puede ser una gata de tres años y otra un perro de cinco años. Definir los campos crea el plano; la creación de objetos proporciona instancias reales.

  3. 3
    EjemploIdentifica el estado y el comportamiento

    Los campos name, species y age de una mascota almacenan su estado. Para Luna, sus valores son "Luna", "cat" y 3.

    El método celebrateBirthday() proporciona comportamiento. Su cuerpo puede ejecutar this.age++;, cambiando la edad de la mascota receptora. El método forma parte de la definición de la clase; el valor que cambia pertenece a una mascota concreta.

  4. 4
    Desafío¿Una definición crea una mascota?

    Un programador escribe una clase Pet con tres campos y un método de cumpleaños. No se ha ejecutado ninguna expresión de creación de objetos. ¿Cuántos objetos de mascotas ha creado esa definición?

    ---reveal---

    Ninguno. La clase define un tipo y su estado y comportamiento disponibles. Los objetos aparecen cuando el programa los crea, por ejemplo con new Pet(...). Tres declaraciones de campos describen tres datos por mascota, no tres mascotas.

  5. 5
    EjemploCrea el primer objeto

    Lee Pet luna = new Pet("Luna", "cat", 3); por partes. Pet luna declara una variable. new Pet(...) crea una mascota y ejecuta el constructor con los tres argumentos proporcionados.

    El constructor almacena los valores iniciales. La referencia producida por new se asigna a luna, lo que da al código posterior una forma de acceder a este objeto. Declarar Pet luna; por sí solo no proporcionaría ningún objeto sobre el que llamar a un método.

  6. 6
    ExplicaciónLlama a un método sobre su receptor

    En luna.celebrateBirthday(), la expresión anterior al punto identifica el objeto receptor. El método de cumpleaños se ejecuta para Luna y aumenta su edad de 3 a 4.

    int age = luna.getAge(); recupera después la edad actual para utilizarla en otro lugar. Pet.celebrateBirthday() no puede elegir una mascota: es un método de instancia y un nombre de clase no proporciona ningún objeto concreto.

  7. 7
    EjemploCrea una segunda mascota

    Ahora ejecuta Pet max = new Pet("Max", "dog", 5);. Una segunda expresión new crea un segundo objeto con sus propios campos.

    Después de otra llamada a luna.celebrateBirthday(), Luna tiene 5 años y Max sigue teniendo 5. Que tengan la misma edad no hace que sean el mismo objeto. Un cumpleaños posterior de Luna solo cambia la edad de Luna porque la edad de Max se guarda en los campos de otro objeto.

  8. 8
    Idea claveUna asignación puede compartir un objeto

    Pet other = luna; copia la referencia de Luna. No llama a un constructor ni copia sus campos en otra mascota.

    Los nombres other y luna acceden ahora al mismo objeto. Llamar a other.celebrateBirthday() cambia la edad de esa mascota de 5 a 6, y luna.getAge() también ve 6. Cuenta las creaciones de objetos por separado de las declaraciones de variables.

  9. 9
    DesafíoSigue una referencia compartida y una mascota independiente

    Dos mascotas distintas empiezan con edades de 1 para a y 8 para b. El código ejecuta Pet c = a; c.celebrateBirthday(); b.celebrateBirthday();. ¿Qué edades indican a y b?

    ---reveal---

    Indican 2 y 9. c comparte el objeto de a, por lo que su llamada aumenta la edad de esa mascota. La llamada mediante b aumenta la de la mascota independiente. La clase proporciona el mismo comportamiento de cumpleaños a ambas, pero cada llamada tiene su propio receptor.

  10. 10
    RepasoLee con atención la creación y las llamadas

    Distingue tres funciones: la clase define el tipo, cada objeto almacena su propio estado de instancia y una variable de referencia permite acceder a un objeto.

    Al leer código, marca cada new, haz coincidir sus argumentos con el constructor e identifica el receptor antes de seguir una llamada a un método. Una simple asignación de referencia añade una vía de acceso a un objeto existente. Un segundo new crea un objeto independiente con su propio estado.

2

Construye correctamente el estado inicial

14 pasos
Elige campos, sigue las asignaciones del constructor y utiliza sobrecargas sin crear objetos adicionales.

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

  1. 1
    IntroducciónDa datos útiles a cada objeto nuevo
  2. 2
    ExplicaciónElige el estado duradero
  3. 3
    ExplicaciónDistingue los valores predeterminados de los valores útiles
  4. 4
    DesafíoElige un campo o una variable local
  5. 5
    ExplicaciónLee la cabecera del constructor
  6. 6
    EjemploTransfiere los parámetros a los campos
  7. 7
    ConsejoUna asignación válida que no almacena nada
  8. 8
    DesafíoIdentifica el campo que no cambia
  9. 9
    ConexiónCrea también los objetos internos
  10. 10
    EjemploOfrece una vía predeterminada útil
  11. 11
    ExplicaciónSigue un objeto a través de la cadena
  12. 12
    DesafíoDiagnostica una expresión new engañosa
  13. 13
    DesafíoCombina un campo fijo con una sobrecarga
  14. 14
    RepasoComprueba cada vía hasta un objeto terminado
3

Sigue los cambios de estado y el estado compartido

13 pasos
Trabaja con un rectángulo y un reloj, y después distingue el comportamiento de instancia de los miembros estáticos.

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

  1. 1
    IntroducciónSigue lo que cambia cada llamada
  2. 2
    ExplicaciónDevuelve un valor a quien llama al método
  3. 3
    EjemploEscala las dimensiones almacenadas
  4. 4
    DesafíoComprueba el resultado devuelto después de un cambio
  5. 5
    EjemploAvanza hasta pasar la medianoche
  6. 6
    ConsejoCorrige un reinicio que no cambia el estado
  7. 7
    Idea claveTen en cuenta cada retorno normal
  8. 8
    ExplicaciónDistingue dos cuentas de un contador compartido
  9. 9
    EjemploProporciona un objeto en código estático
  10. 10
    ConexiónReconoce una constante
  11. 11
    DesafíoSigue los dos tipos de estado
  12. 12
    DesafíoDescubre por qué objetos distintos cambian juntos
  13. 13
    RepasoRelaciona cada operación con su propietario
4

Protege cada vía de cambio del estado

14 pasos
Utiliza métodos de acceso, validación y copias de listas para mantener un objeto dentro de sus reglas.

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

  1. 1
    IntroducciónHaz que la clase sea responsable de sus reglas
  2. 2
    ExplicaciónOculta el campo tras una operación
  3. 3
    ConsejoElige el acceso útil más restringido
  4. 4
    DesafíoDetermina dónde funciona el acceso privado
  5. 5
    EjemploDevuelve información con un método de acceso
  6. 6
    ConexiónOfrece acciones adecuadas para el objeto
  7. 7
    DesafíoLee sin permitir la sustitución
  8. 8
    ExplicaciónElige una política para entradas no válidas
  9. 9
    EjemploSigue la limitación en el límite superior
  10. 10
    EjemploComprueba el primer estado
  11. 11
    DesafíoSigue la opción de ignorar en lugar de limitar
  12. 12
    Idea claveUna lista privada todavía puede quedar expuesta
  13. 13
    DesafíoCierra ambas vías sin comprobaciones
  14. 14
    RepasoRevisa la interfaz pública
5

Construye y prueba una clase útil

12 pasos
Escribe una descripción legible, implementa una regla de préstamo y prueba una clase completa de libro de biblioteca.

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

  1. 1
    IntroducciónConvierte un requisito en comportamiento visible
  2. 2
    ExplicaciónUtiliza la cabecera exacta de la descripción
  3. 3
    EjemploDevuelve el estado actual como texto
  4. 4
    ConsejoDa formato a un reloj sin cambiarlo
  5. 5
    DesafíoCorrige la impresión automática
  6. 6
    EjemploElige los campos a partir del requisito
  7. 7
    EjemploEstablece el estado inicial
  8. 8
    EjemploImplementa la regla antes del cambio
  9. 9
    DesafíoPredice una secuencia antes de ejecutarla
  10. 10
    ConexiónPrueba los casos que una ejecución sencilla pasa por alto
  11. 11
    DesafíoDistingue informar de modificar
  12. 12
    RepasoConstruye con una garantía pública comprobable

Ejercicios de Clases y objetos en Java

40 Preguntas de Práctica en 4 sets

Set 1: Comprueba la inicialización y los cambios controlados10 Preguntas de Práctica

Pregunta 1 de 10

Un Locker tiene un campo occupied y un método release(). ¿Qué parte de esta clase describe un comportamiento?
  1. El campo occupied.
  2. El valor actual true.
  3. El método release.
  4. El tipo de variable Locker.
Mostrar respuestaOcultar respuesta
Respuesta: C
Las operaciones proporcionan comportamiento, por lo que release() describe algo que puede hacer una taquilla. El valor actual de occupied pertenece a su estado. Cambiar el estado puede ser el efecto de un comportamiento, pero el indicador almacenado es un dato, no una operación.

Pregunta 2 de 10

Un Panel almacena la anchura y la altura. Su anchura puede cambiar y getArea() siempre debe indicar el área actual. ¿Cómo debería representarse el área?
  1. Calcular anchura por altura en getArea.
  2. Guardar el área del constructor en un campo final.
  3. Guardar el área una vez en un campo estático compartido.
  4. Guardar la última área en una variable local del constructor.
Mostrar respuestaOcultar respuesta
Respuesta: A
Calcular el producto utiliza las dimensiones actuales cada vez que se ejecuta el método de acceso. Un área final calculada durante la construcción quedaría desactualizada cuando cambiara la anchura. Un campo compartido también mezclaría los paneles, y una variable local del constructor no puede proporcionar estado duradero a llamadas posteriores.

Pregunta 3 de 10

Una clase solo declara public Sensor(int channel). Quien la utiliza llama a new Sensor(). ¿Por qué no compila?
  1. El parámetro debe llamarse sensor.
  2. Toda clase debe declarar un constructor void.
  3. Java exige que channel sea un campo estático.
  4. No hay un constructor sin argumentos disponible.
Mostrar respuestaOcultar respuesta
Respuesta: D
Declarar Sensor(int channel) hace que Java deje de proporcionar un constructor predeterminado. Quien llama al constructor debe proporcionar un argumento compatible o la clase debe ofrecer explícitamente una sobrecarga sin argumentos que resulte útil. Java no completa el valor con 0 solo porque un campo entero tendría ese valor predeterminado.

Pregunta 4 de 10

Un medidor almacena private int reading = 9;. Su método es public void reset() { int reading = 0; }. ¿Qué indica un método de acceso que devuelve this.reading después de reset()?
  1. 0
  2. 9
  3. null
  4. Un error de compilación por falta de retorno.
Mostrar respuestaOcultar respuesta
Respuesta: B
La declaración dentro de reset() crea una variable local y le asigna 0. Nunca escribe en el campo, por lo que el método de acceso sigue indicando 9. Eliminar el tipo y asignar this.reading = 0; reiniciaría el estado duradero del objeto.

Pregunta 5 de 10

Un método convierte un número de minutos proporcionado en horas utilizando solo su parámetro. No necesita campos de un objeto. ¿Qué tipo de método puede cumplir esta función?
  1. Un campo de instancia.
  2. Un método estático.
  3. Una sobrecarga de constructor.
  4. Una variable local final.
Mostrar respuestaOcultar respuesta
Respuesta: B
Un método estático puede realizar un cálculo basado por completo en los parámetros proporcionados porque no necesita un objeto actual. Un método de instancia es adecuado cuando una operación depende del estado de un objeto concreto. Esta conversión no requiere tal receptor.

Pregunta 6 de 10

Un reloj sustituye los campos separados de horas y minutos por un solo campo minutesSinceMidnight, mientras sus métodos de acceso siguen devolviendo los mismos valores. ¿Por qué quienes lo utilizan pueden conservar sus llamadas a esos métodos?
  1. Los campos privados se vuelven públicos para los usuarios.
  2. El compilador sustituye las llamadas a getters por campos.
  3. Cada usuario recibe una copia de los campos anteriores.
  4. El comportamiento público sigue siendo coherente.
Mostrar respuestaOcultar respuesta
Respuesta: D
Quienes utilizan la clase recurren a los métodos de acceso en lugar de depender de la estructura de almacenamiento, por lo que una interfaz pública coherente permite cambiar el diseño interno. Los campos privados no se vuelven públicos durante un cambio. La clase debe implementar los métodos de acceso usando la nueva representación y manteniendo los resultados prometidos.

Pregunta 7 de 10

Un medidor comienza en 16. Su método modificador limita las entradas al rango de 10 a 20, incluidos ambos extremos. Después de setValue(25) y luego setValue(8), ¿cuáles son los valores almacenados tras cada llamada?
  1. 20 y después 10.
  2. 16 y después 16.
  3. 25 y después 8.
  4. 20 y después 20.
Mostrar respuestaOcultar respuesta
Respuesta: A
La limitación al rango permitido sustituye cada valor solicitado fuera del rango por el extremo permitido más cercano. El valor alto pasa a 20 y el bajo pasa a 10. Mantener 16 en ambas llamadas describiría la opción de ignorar entradas no válidas, una política distinta de la indicada.

Pregunta 8 de 10

Un Group debe contener como máximo cuatro nombres. addName aplica el límite, pero Group(ArrayList<String> names) almacena directamente la lista proporcionada y getNames() devuelve la lista interna. ¿Qué diseño mantiene el límite frente a cambios realizados por quienes utilizan la clase?
  1. Validar y copiar la entrada; devolver una copia.
  2. Solo copiar la entrada; devolver la lista interna.
  3. Solo validar la entrada; devolver la lista interna.
  4. Solo copiar al devolver; almacenar cualquier lista de entrada.
Mostrar respuestaOcultar respuesta
Respuesta: A
Valida el tamaño inicial y copia la entrada para que quien la proporciona no pueda modificar después la lista almacenada. Devuelve también una copia para impedir cambios mediante el método de acceso. Copiar solo al devolver deja que la referencia de entrada original eluda addName, mientras que copiar solo la entrada deja abierta la vía del método de acceso.

Pregunta 9 de 10

¿Qué tipo de retorno debe utilizar el método toString() sin argumentos?
  1. void
  2. Object[]
  3. String
  4. boolean
Mostrar respuestaOcultar respuesta
Respuesta: C
El método de descripción estándar devuelve un String que puede utilizarse para imprimir, concatenar o en otra llamada. Un método void que imprime texto no puede sustituir ese método que devuelve String. El compilador rechaza un toString() sin argumentos con el tipo de retorno incompatible void.

Pregunta 10 de 10

Un pase empieza sin usar. use() devuelve false si ya se ha usado; de lo contrario, activa el indicador y devuelve true. reset() desactiva el indicador. toString() solo informa de él. ¿Qué devuelven las llamadas a use en use(); println(pass); use(); reset(); use(); use();?
  1. true, true, true, false
  2. true, false, false, false
  3. true, false, true, true
  4. true, false, true, false
Mostrar respuestaOcultar respuesta
Respuesta: D
El primer uso tiene éxito y la impresión deja el indicador de uso sin cambios, por lo que el segundo uso falla. El reinicio desactiva el indicador y permite que el tercer uso tenga éxito; el cuarto falla. Considerar que la última llamada tiene éxito supone olvidar el cambio de estado realizado por la llamada correcta anterior.
Set 2: Crea objetos y lee su estado10 Preguntas de Práctica

Guarda esta unidad para ver todas las respuestas y practicar cada set.

Pregunta 1 de 10

Un programa de reparto utiliza una clase Parcel para crear varios paquetes. ¿Cómo se llama un paquete creado mientras se ejecuta el programa?
  1. Una definición de Parcel.
  2. Una instancia de Parcel.
  3. Un método de Parcel.
  4. Un parámetro de Parcel.
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 2 de 10

Un objeto Bell se almacena en bell, y ring() es un método de instancia. ¿Cómo debería el programa hacer sonar esa campana?
  1. Bell.ring(bell);
  2. ring(bell);
  3. new bell.ring();
  4. bell.ring();
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 3 de 10

Una clase declara private int attempts; sin inicializador ni asignación en el constructor. ¿Qué valor almacena un objeto nuevo en este campo?
  1. 0
  2. 1
  3. null
  4. Ningún valor; la compilación falla.
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 4 de 10

Después de new Badge("Visitor"), getText() devuelve null. El campo es private String text;, el método de acceso lo devuelve y el cuerpo del constructor es text = text;. ¿Cómo debería el constructor almacenar su parámetro text?
  1. text = this.text;
  2. String text = "Visitor";
  3. this.text = text;
  4. this.text = this.text;
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 5 de 10

Una clase tiene Ticket() { this(4); } y Ticket(int zones) { this.zones = zones + 1; }. ¿Qué valor almacena new Ticket() en zones?
  1. 0
  2. 4
  3. 5
  4. 8
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 6 de 10

Un MinuteClock almacena horas de 0 a 23 y minutos de 0 a 59. advance() añade un minuto y gestiona el cambio de hora y el paso a medianoche. isMidnight() comprueba si son las 00:00. A partir de las 23:57, ejecuta advance(); advance(); advance(); if (isMidnight()) { advance(); } advance();. ¿Cuál es la hora final?
  1. 00:01
  2. 00:02
  3. 00:00
  4. 23:59
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 7 de 10

Cada Lamp debe recordar su propio estado de encendido o apagado. ¿Cómo debería declararse ese estado?
  1. private boolean on;
  2. private static boolean on;
  3. public static final boolean ON = false;
  4. Una variable local boolean on dentro de switchOn().
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 8 de 10

Un campo de Bin se escribe como int capacity; sin modificador de acceso. ¿Qué acceso tiene?
  1. Acceso privado.
  2. Acceso protegido.
  3. Acceso público.
  4. Acceso de paquete.
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 9 de 10

Una Session tiene private boolean active;. Quienes la utilizan necesitan comprobarlo, pero solo pueden cambiarlo mediante start() y stop(). ¿Qué método de acceso se ajusta a este diseño?
  1. public void isActive() { System.out.println(this.active); }
  2. public boolean setActive(boolean value) { return value; }
  3. public void setActive(boolean value) { this.active = value; }
  4. public boolean isActive() { return this.active; }
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 10 de 10

Un objeto define public String toString() { return "Ready"; }. ¿Qué texto produce System.out.println("State: " + object);?
  1. Ready
  2. State: Ready
  3. State: Object@Ready
  4. State: toString()
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Set 3: Sigue el estado de los objetos en secuencias más largas10 Preguntas de Práctica

Guarda esta unidad para ver todas las respuestas y practicar cada set.

Pregunta 1 de 10

Un Marker tiene un nombre y setName. Ejecuta Marker a = new Marker("One"); Marker b = a; a = new Marker("Two"); b.setName("Three");. ¿Qué devuelven a.getName() y b.getName()?
  1. "Three" y "Three".
  2. "Three" y "Two".
  3. "Two" y "One".
  4. "Two" y "Three".
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 2 de 10

Una clase declara private int level = 6;. Su constructor es public Beacon(int level) { this.level = level + 2; }. ¿Qué almacena new Beacon(3)?
  1. 6
  2. 5
  3. 3
  4. 8
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 3 de 10

Una clase ya tiene public Tag(String text). ¿Qué constructor adicional proporciona una sobrecarga distinta?
  1. public Tag(String text, int count)
  2. public Tag(String label)
  3. public Tag(String value)
  4. public Tag(String name)
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 4 de 10

Un rectángulo tiene una anchura de 2,0 cm y una altura de 5,0 cm. scale(k) multiplica ambas dimensiones por k; area() devuelve su producto. Ejecuta scale(2); double saved = area(); scale(0.5);. ¿Cuáles son saved y el resultado final de area()?
  1. 20,0 y 10,0 centímetros cuadrados.
  2. 40,0 y 20,0 centímetros cuadrados.
  3. 40,0 y 10,0 centímetros cuadrados.
  4. 10,0 y 10,0 centímetros cuadrados.
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 5 de 10

Una clase Dial recién iniciada tiene el campo de instancia value y el campo estático created, ambos inicialmente en 0. Cada constructor incrementa created una vez; turn() solo incrementa el valor de su receptor. Ejecuta Dial a = new Dial(); Dial b = a; b.turn(); b = new Dial(); b.turn(); b.turn();. ¿Cuáles son los valores de a.value, b.value y created?
  1. 1, 2 y 2.
  2. 2, 2 y 2.
  3. 1, 3 y 2.
  4. 1, 2 y 3.
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 6 de 10

Un método de acceso debe proporcionar una etiqueta almacenada en private String label;. ¿Qué implementación proporciona ese valor a quien lo llama?
  1. public void getLabel() { System.out.println(this.label); }
  2. public void getLabel(String label) { this.label = label; }
  3. public String getLabel() { return "label"; }
  4. public String getLabel() { return this.label; }
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 7 de 10

setLevel limita un nivel al rango de 0 a 10, incluidos ambos extremos. El constructor llama a ese método modificador y raise() llama a setLevel(this.level + 1). Partiendo de new Level(13), ejecuta raise(); setLevel(-2); raise(); raise();. ¿Cuál es el nivel final?
  1. 10
  2. 2
  3. 1
  4. 0
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 8 de 10

Dentro de la descripción de un reloj, ¿qué cadena devuelve String.format(java.util.Locale.US, "%02d:%02d", 4, 7)?
  1. "4:7"
  2. "40:70"
  3. "04:07"
  4. "04.07"
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 9 de 10

Un Seat debe rechazar una segunda reserva hasta que se cancele la anterior. Una prueba solo comprueba actualmente la primera llamada correcta a reserve(). ¿Qué llamada posterior comprueba mejor la regla de rechazo?
  1. Llamar de nuevo a reserve sin cancelar.
  2. Cancelar primero y llamar de nuevo a reserve.
  3. Crear otro asiento y reservar ese.
  4. Cancelar la reserva sin reservar de nuevo.
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 10 de 10

Un termostato acepta temperaturas objetivo de 6 °C a 28 °C, incluidos ambos extremos. ¿Qué par de entradas prueba los valores situados justo fuera de ambos límites?
  1. 6 °C y 28 °C.
  2. 5 °C y 29 °C.
  3. 7 °C y 27 °C.
  4. 17 °C y 18 °C.
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Set 4: Diagnostica diseños y combina las reglas10 Preguntas de Práctica

Guarda esta unidad para ver todas las respuestas y practicar cada set.

Pregunta 1 de 10

Un programa gestiona varias bombas. Cada una tiene un identificador y una velocidad actual diferentes, y puede arrancar o detenerse. ¿Cómo debería representar esto una clase Pump?
  1. Campos estáticos para identificador y velocidad; métodos para arrancar y detener.
  2. Campos de instancia para identificador y velocidad; métodos para arrancar y detener.
  3. Campos de instancia para arrancar y detener; identificadores locales en cada llamada.
  4. Velocidades locales en arranque y parada; una constante de identificador compartida.
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 2 de 10

Un Token tiene un valor numérico y addOne() lo incrementa. Ejecuta Token a = new Token(2); Token b = a; b.addOne(); Token c = new Token(7); a = c; a.addOne(); b.addOne();. ¿Qué valores indican a, b y c?
  1. 4, 4 y 8.
  2. 8, 8 y 8.
  3. 8, 4 y 8.
  4. 8, 3 y 7.
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 3 de 10

Un contador debe conservar un total entre llamadas. Su método add(int amount) declara actualmente int total = amount; dentro del método. ¿Qué cambio de almacenamiento permite acumular valores?
  1. Declarar un campo de instancia total y actualizarlo en add.
  2. Declarar de nuevo el mismo total local en el método de acceso.
  3. Declarar amount como variable local solo en el constructor.
  4. Declarar un total local final en cada llamada a add.
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 4 de 10

Un Bucket declara private int capacity = 2; y una lista de instancia vacía items. Su constructor es Bucket(int capacity) { capacity = capacity; }. add devuelve false sin añadir nada cuando el tamaño de la lista es al menos la capacidad almacenada; de lo contrario, añade el elemento y devuelve true. snapshot() devuelve new ArrayList<>(this.items).

Ejecuta Bucket b = new Bucket(4); b.add("A"); ArrayList<String> view = b.snapshot(); view.add("B");. Después llama a b.add("C") y b.add("D"). ¿Qué devuelven estas dos últimas llamadas y cuántos elementos contiene finalmente b?

  1. true, true; 3 elementos.
  2. false, false; 2 elementos.
  3. true, false; 3 elementos.
  4. true, false; 2 elementos.
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 5 de 10

Un Route tiene el campo final code y el campo estático created, inicialmente en 0. Sus constructores son Route() { this("local"); created++; } y Route(String code) { this.code = code; created++; }. Después de una llamada a cada constructor, created debería contar dos objetos. ¿Qué corrección da el recuento correcto?
  1. Eliminar created++ del constructor con String.
  2. Sustituir this("local") por new Route("local").
  3. Mantener ambos incrementos y hacer created un campo de instancia.
  4. Eliminar created++ del constructor sin argumentos.
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 6 de 10

Un temporizador comienza a las 22:59. advance() añade un minuto y gestiona el cambio al completar 24 horas. getHour() lee la hora actual. Ejecuta int h = getHour(); advance(); if (getHour() != h) { advance(); } advance();. ¿Cuáles son h y la hora final?
  1. 23 y 23:02.
  2. 22 y 23:01.
  3. 22 y 23:02.
  4. 23 y 23:01.
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 7 de 10

Un método auxiliar solo debe poder llamarse desde código dentro de su propia clase de nivel superior. ¿Qué modificador de acceso es adecuado?
  1. public
  2. private
  3. protected
  4. Ningún modificador.
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 8 de 10

Un rectángulo cambia sus dimensiones mediante scale. Su método de acceso devuelve this.width * this.height y no hay ningún campo de área. ¿Por qué es válido este diseño del método de acceso?
  1. Devuelve información actual sin exponer el almacenamiento.
  2. Todo método de acceso debe devolver un campo con su mismo nombre exacto.
  3. El método crea un campo de área cada vez que se llama.
  4. El producto se convierte en constante después de la primera llamada.
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 9 de 10

Un Album tiene un límite de tres leyendas. Su constructor rechaza más de tres leyendas y almacena new ArrayList<>(captions). getCaptions() devuelve otra copia; addCaption rechaza adiciones cuando se alcanza el límite. Una persona proporciona dos leyendas, añade una tercera a la lista original, añade una leyenda al resultado del método de acceso y después llama una vez a addCaption. ¿Cuántas leyendas contiene el Album?
  1. 2
  2. 4
  3. 5
  4. 3
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

Pregunta 10 de 10

Una Door debe rechazar open() cuando está bloqueada. Una puerta nueva empieza cerrada y desbloqueada. lock() solo tiene éxito mientras está cerrada; unlock() quita el bloqueo. ¿Qué secuencia de prueba comprueba el rechazo mientras está bloqueada y después una apertura correcta tras desbloquearla?
  1. lock; open → false; unlock; open → true.
  2. open → true; lock; open → false; unlock.
  3. lock; unlock; open → true; open → false.
  4. open → true; unlock; lock; open → false.
Mostrar respuestaOcultar respuesta

Guarda esta unidad para ver esta respuesta.

¿Te atascas con una pregunta tuya? Ayuda con Tareas

Términos Clave

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

A

Acceso de paquete
El acceso de paquete permite que las clases del mismo paquete utilicen una declaración y se selecciona omitiendo un modificador de acceso.
El acceso de paquete en int count; permite que otra clase del mismo paquete sin nombre lea o cambie el campo.
Anotación @Override
La anotación @Override pide al compilador que verifique que una declaración de método sustituye un método ya proporcionado por un supertipo.
@Override encima de tostring() revela el error de mayúsculas en lugar de dejar la impresión automática sin cambios.
Argumento
Un argumento es un valor proporcionado en una llamada a un método o constructor, que se corresponde con un parámetro por su posición y su tipo compatible.
En new Clock(9, 5), los argumentos 9 y 5 proporcionan la hora y el minuto del nuevo reloj.

Á

Ámbito (alcance)
El ámbito de una variable es la región del texto del programa donde su nombre puede utilizarse para hacer referencia a esa variable.
El ámbito de una variable local declarada dentro de un bloque if termina en ese bloque, por lo que el código posterior no puede utilizar su nombre.

C

Campo estático
Un campo estático es una única variable de toda la clase compartida por todas las instancias, por lo que su valor no pertenece a un objeto concreto.
Un campo estático llamado created puede aumentar cada vez que el constructor inicializa una nueva instancia de la clase.
Campo final
Un campo final solo puede recibir una asignación, por lo que es útil para datos de cada objeto que no deberían sustituirse después de la inicialización.
El título de un libro puede ser un campo final asignado en su constructor, sin ofrecer un método modificador a quienes lo utilizan.
Clase
Una clase define un tipo de Java al describir los datos que almacenan sus objetos y los métodos que proporcionan su comportamiento.
Una clase Pet reúne un nombre, una especie y una edad con operaciones como celebrateBirthday().
Comportamiento
El comportamiento de un objeto es el conjunto de operaciones que ofrecen sus métodos para leer su estado, calcular a partir de él o modificarlo.
Un libro ofrece los comportamientos lend() y giveBack() para que quienes lo utilizan puedan gestionar su estado de préstamo.
Constante
Una constante es un valor fijo que suele proporcionarse mediante un campo static final, a menudo público y con nombre en UPPER_SNAKE_CASE.
Una constante MAX_CELSIUS proporciona el mismo límite superior de temperatura a la clase y a quienes la utilizan.
Constructor
Un constructor es una declaración con el nombre de la clase y sin tipo de retorno que inicializa un objeto durante su creación.
El constructor de Pet copia el nombre, la especie y la edad proporcionados en los campos de la nueva mascota.
Constructor predeterminado
Un constructor predeterminado es el constructor sin argumentos que Java proporciona cuando una clase no declara ningún constructor.
Añadir un constructor de tres argumentos elimina el constructor predeterminado, por lo que new Pet() necesita una sobrecarga explícita.
Constructor sin argumentos
Un constructor sin argumentos no acepta valores de entrada; puede declararse explícitamente o ser proporcionado por Java como constructor predeterminado.
Un constructor explícito sin argumentos Clock() puede delegar con this(0, 0) para iniciar el reloj a medianoche.
Contexto estático
Un contexto estático es código sin una instancia actual, como el cuerpo de un método estático, donde no se puede acceder a miembros de instancia solo por su nombre.
En un contexto estático, un campo age sin calificar no tiene una mascota actual; pet.getAge() proporciona en cambio un objeto concreto.
Copia defensiva
Una copia defensiva es un objeto de datos independiente que se devuelve o almacena para impedir que otra parte del programa cambie directamente los datos internos.
Una copia defensiva creada mediante new ArrayList<>(this.tracks) evita que los cambios externos de la lista afecten a la lista interna.

D

Desarrollo incremental
El desarrollo incremental construye y comprueba una parte útil cada vez, lo que facilita localizar errores antes de añadir más código.
Con el desarrollo incremental, comprueba primero el constructor y después añade y prueba una operación cada vez.
Diseño de clases
El diseño de clases elige el estado, las vías de construcción, las operaciones y las reglas que permiten a una clase representar un tipo de cosa útil.
El diseño de clases convierte la descripción de un libro de biblioteca en campos identificativos, un indicador de préstamo y operaciones que aplican las reglas de préstamo.

E

Encadenamiento de constructores
El encadenamiento de constructores ejecuta otro constructor de la misma clase sobre el mismo objeto mediante una llamada a this(...).
El encadenamiento de constructores permite que una mascota recién nacida utilice this(name, species, 0) para reutilizar la inicialización con una edad predeterminada.
Encapsulación (encapsulamiento)
La encapsulación agrupa el estado de un objeto con las operaciones que lo controlan y oculta los datos internos tras una interfaz elegida.
La encapsulación en Pet combina un campo de edad privado con métodos de comprobación que rechazan cambios no válidos.
Estado
El estado de un objeto es el conjunto actual de valores almacenados en sus campos de instancia, como la hora y el minuto de un reloj.
Después de que un reloj avance desde 08:59, su estado contiene la hora 9 y el minuto 0, listo para la siguiente llamada.
Excepción IllegalArgumentException
Una IllegalArgumentException es una excepción que un método o constructor puede lanzar para rechazar un argumento que incumple sus reglas de entrada.
Lanzar IllegalArgumentException ante una capacidad negativa impide que la construcción devuelva un objeto no válido.

I

Inicialización
La inicialización da a una variable su primer valor; la inicialización de un objeto establece los campos necesarios para un estado inicial utilizable.
La inicialización de una lista en el constructor de una lista de reproducción utiliza new ArrayList<>() para que después se puedan añadir entradas.
Instancia
Una instancia es un objeto creado a partir de una clase concreta; el término nombra la relación entre ese objeto y su tipo.
Una mascota llamada Luna y otra llamada Max son dos instancias de Pet cuando se crean por separado.
Interfaz pública
La interfaz pública de una clase es el conjunto de operaciones y valores públicos disponibles para quienes pueden acceder a la clase.
La interfaz pública de un libro puede ofrecer lend(), giveBack() e isLentOut() mientras su indicador de préstamo permanece privado.
Invariante
Una invariante es una regla sobre el estado que debe cumplirse después de la construcción y de cada operación pública sobre un objeto.
La invariante de un termostato mantiene su temperatura objetivo dentro del rango permitido incluso cuando se solicitan 40 °C.
Invocación recursiva de constructores
Una invocación recursiva de constructores es una cadena de constructores no permitida que termina llamando a un constructor que ya está en esa cadena.
Java indica una invocación recursiva de constructores cuando Box() y Box(int) delegan el uno en el otro.

L

Limitación al rango permitido (clamping)
La limitación al rango permitido sustituye un valor fuera de un rango permitido por el extremo más cercano y conserva los valores que ya están dentro del rango.
Al limitar al rango de 5–30 °C, una solicitud de 34 °C almacena 30 °C, mientras que una solicitud de 18 °C almacena 18 °C.

M

Método de acceso (getter)
Un método de acceso devuelve información sobre un objeto sin modificarlo, a menudo con un nombre que comienza por get.
El método de acceso getArea() puede calcular la anchura por la altura aunque la clase no almacene un campo de área separado.
Método de acción
Un método de acción nombra una operación con una función clara que el objeto puede realizar y suele controlar un cambio con más precisión que un método modificador directo.
El método de acción lend() comprueba el estado actual de préstamo, rechaza un préstamo repetido e indica si el préstamo tuvo éxito.
Método de instancia
Un método de instancia es un método no estático que se ejecuta para un objeto concreto y puede utilizar los campos de instancia de ese objeto.
scale es un método de instancia: a.scale(2) cambia el rectángulo a, mientras que otro rectángulo permanece sin cambios.
Método estático
Un método estático pertenece a una clase y no tiene un objeto actual; necesita una referencia a un objeto para utilizar miembros de instancia.
Un método estático como main puede crear Counter c y después llamar a c.increment() mediante esa referencia.
Método modificador (setter)
Un método modificador acepta un valor nuevo para una parte del estado de un objeto y puede comprobar esa entrada antes de almacenarla.
El método modificador setAge(int age) puede ignorar entradas negativas para que la edad almacenada permanezca en su rango permitido.
Método String.format
El método String.format construye texto a partir de una cadena de formato y valores, y permite establecer anchuras fijas y posiciones decimales.
String.format con java.util.Locale.US y "%.2f" da formato a 7,5 como "7.50" independientemente de la configuración regional predeterminada.
Método toString
El método toString() devuelve la representación textual de un objeto, lo que permite mostrar detalles legibles al imprimir y concatenar cadenas.
El toString de un reloj devuelve "09:05", por lo que imprimir el reloj muestra una hora útil en lugar del texto de clase y hash.
Modificador de acceso
Un modificador de acceso es una palabra clave de Java como private, protected o public que controla el acceso a una declaración.
El modificador de acceso private mantiene un método auxiliar disponible dentro de su clase sin exponerlo a otras clases.
Modificador private
El modificador private restringe el acceso a un miembro al código dentro de su clase, de modo que el código independiente de nivel superior debe usar la interfaz pública.
Un campo de edad privado hace que pet.age = -1; falle en una clase Main independiente y exige una llamada a un método controlado.
Modificador protected
El modificador protected permite el acceso desde el mismo paquete y un acceso restringido desde código de subclases fuera de ese paquete.
Un método auxiliar protegido puede ser llamado por otra clase de su paquete aunque esa clase no sea una subclase.
Modificador public
El modificador public permite acceder a un miembro dondequiera que sea accesible el tipo que lo declara, para que quienes lo utilizan puedan llamar a la operación expuesta.
Un método público lend() permite que Main solicite un préstamo mientras el campo de estado privado del libro permanece oculto.

N

Notación de punto
La notación de punto nombra un miembro mediante un receptor, como una referencia a un objeto seguida de un punto y una llamada a un método.
La notación de punto en luna.getAge() indica a Java que la mascota a la que se accede mediante luna debe proporcionar su edad.
null
El valor de Java null significa que una referencia no tiene ningún objeto al que acceder; los campos de tipos de clase lo reciben salvo que se inicialicen de otra forma.
Llamar a add sobre un campo de lista que sigue en null falla porque no se creó ningún objeto de lista.

O

Objeto
Un objeto es una instancia de una clase que existe mientras se ejecuta un programa y tiene sus propios valores actuales de campos de instancia.
Dos objetos de mascotas creados con expresiones new separadas pueden tener edades distintas y seguir una misma definición de clase.
Objeto receptor
El objeto receptor es aquel sobre el que se invoca un método de instancia, identificado por la expresión anterior al punto en una llamada.
En book.lend(), el receptor es el libro al que se accede mediante book, cuyo indicador de préstamo comprueba el método.
Ocultación de información
La ocultación de información hace que quienes utilizan un objeto dependan de sus operaciones disponibles en lugar de los detalles de su almacenamiento interno.
La ocultación de información permite que un reloj cambie sus campos y mantenga getHours() coherente para quienes ya lo utilizan.
Ocultación de nombres (shadowing)
La ocultación de nombres ocurre cuando una declaración más próxima, como un parámetro, oculta el nombre simple de un campo en una parte del código.
La ocultación por el parámetro hace útil this.age = age;: el lado izquierdo es el campo y el derecho es la entrada.

P

Palabra clave new
La palabra clave de Java new inicia una expresión de creación de objetos que ejecuta un constructor y produce una referencia al objeto.
new Pet("Luna", "cat", 3) crea una mascota y proporciona tres argumentos para su estado inicial.
Palabra clave this
La palabra clave this hace referencia al objeto actual dentro del código de instancia y distingue sus campos de parámetros con el mismo nombre.
Dentro del método modificador de una mascota, this.name = name; escribe la nueva entrada en el campo de esa mascota concreta.
Parámetro
Un parámetro es una entrada con nombre declarada en la cabecera de un método o constructor que recibe un argumento en cada llamada.
El parámetro age de un constructor de mascotas recibe 3 cuando la llamada proporciona 3 en esa posición.
Propiedad de solo lectura
Una propiedad de solo lectura es información expuesta para que quienes utilizan la clase puedan leerla sin ofrecerles una operación que la sustituya directamente.
Un título fijo es una propiedad de solo lectura cuando quienes utilizan la clase tienen getTitle() pero ninguna operación para sustituir el título.
Prueba de valores límite
Una prueba de valores límite comprueba el comportamiento en un límite permitido o cerca de él, donde una condición cambia si una operación debe tener éxito.
Una prueba de valores límite de un método modificador de temperatura objetivo prueba un extremo permitido y un valor justo fuera de él para comprobar la comparación.

R

Referencia
Una referencia es un valor que identifica un objeto y permite que una variable o expresión acceda a él cuando sea necesario.
Después de Pet other = luna;, las dos variables contienen referencias a la misma mascota, por lo que cualquiera de ellas puede acceder a ella.
Resultado esperado
Un resultado esperado es el valor de retorno, la salida o el estado que se predice a partir de un requisito antes de ejecutar una prueba.
El resultado esperado de una segunda llamada a lend() sobre un libro prestado es false, con el indicador de préstamo sin cambios.

S

Seguimiento del estado
Un seguimiento del estado registra los valores de los campos después de cada sentencia para mostrar cómo una secuencia de operaciones cambia objetos concretos.
Un seguimiento del estado con columnas para dos mascotas muestra que un cumpleaños de la primera deja a la segunda sin cambios.
Sobrecarga de constructores
La sobrecarga de constructores declara varios constructores para una clase mediante distintas listas de parámetros para diferentes entradas de creación.
La sobrecarga de constructores permite que un reloj acepte horas y minutos o comience a medianoche sin argumentos.

T

Tiempo de vida
El tiempo de vida de una variable es el periodo de ejecución durante el que existe su almacenamiento; un campo de instancia dura lo mismo que su objeto.
El tiempo de vida de un campo de recuento se extiende entre llamadas de incremento, mientras que una variable local temporal solo existe durante su llamada.

V

Validación
La validación comprueba una entrada o un cambio propuesto frente a las reglas de la clase antes de aceptarlo como parte del estado del objeto.
La validación compartida en un constructor y un método modificador impide que cualquiera de las dos vías almacene un estado no válido.
Valor de retorno
Un valor de retorno es el resultado que un método devuelve a quien lo llamó, donde puede almacenarse, imprimirse o utilizarse en una expresión.
El valor de retorno de r.area() puede duplicarse en una expresión; imprimir el área por sí solo no proporciona ningún valor.
Valor predeterminado
Un valor predeterminado es el valor inicial que Java asigna a un campo sin inicializador explícito, como 0, false o null.
Un campo booleano tiene el valor predeterminado false; a una variable booleana local se le debe asignar un valor antes de poder leerla.
Variable de instancia
Una variable de instancia, también llamada campo de instancia, almacena parte del estado de un objeto y existe mientras exista ese objeto.
La variable de instancia age de cada mascota conserva su propio valor, aunque la declaración aparece una sola vez en Pet.
Variable local
Una variable local se declara en un bloque dentro de un método o constructor y proporciona almacenamiento temporal para esa ejecución.
La variable local total dentro de un método de cálculo guarda trabajo temporal que el objeto no necesita recordar.
void
El tipo de retorno void declara que un método no proporciona ningún valor de retorno, aunque cambie el estado o imprima una salida.
Un método de cumpleaños puede devolver void porque su función es aumentar la edad almacenada de su receptor.

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