Saltar a contenido

2. Clases y objetos en la API de Java

Campo Contenido
Unidad UD3, Programación Orientada a Objetos con la API de Java
RA RA2, Escribe y prueba programas sencillos, reconociendo y aplicando los fundamentos de la programación orientada a objetos
CE cubiertos CE 2.5 (se han escrito llamadas a métodos estáticos), CE 2.7 (se han incorporado y utilizado librerías de objetos) y CE 2.9 (se ha utilizado el entorno integrado de desarrollo) de forma directa; refuerzo de CE 2.2, CE 2.3, CE 2.4, CE 2.6 y CE 2.8

En esta sección aprenderás:

  • Cómo está organizado el API de Java —miles de clases listas para usar— en módulos, paquetes y clases, y cómo traerlas a tus programas con import.
  • A leer la documentación oficial (Javadoc) de Java 25 como un profesional: buscar clases, interpretar firmas, parámetros, valores de retorno y anotaciones como Since o Deprecated.
  • La diferencia completa y definitiva entre miembros de instancia y estáticos: la promesa que la sección 1 dejó citada para hoy.
  • Qué es un constructor, cómo leerlos en la documentación y qué es la sobrecarga (que llevas usando desde la UD1 sin saberlo).
  • A explorar el API desde IntelliJ con el autocompletado y su documentación integrada, programando «con el manual abierto».

Necesitas tener fresco (de unidades y secciones anteriores):

  • De la sección 1 de esta unidad: clase frente a objeto, los tres pasos de new, referencias y null, el operador punto, y la presentación informal de los miembros de instancia y estáticos.
  • De la UD1: Integer.parseInt() y Math.sqrt() como primeras llamadas estáticas; la clase IO; formatted(); los comentarios Javadoc /** */.
  • De la UD2: los arrays (new int[5], .length, recorrido con bucles y operaciones manuales) y los atajos básicos de IntelliJ (Shift+F10, Alt+Enter, el depurador).

2.1 El API de Java: la caja de herramientas ya abierta

En la sección 1 te pedí que hicieras Ctrl+clic sobre la palabra String y pasearas por su código fuente. Si lo hiciste, descubriste algo que conviene convertir en convicción: String no es magia del lenguaje, sino una clase escrita por personas —con sus campos y sus métodos— que vive en un fichero dentro del JDK. Hoy generalizamos esa idea hasta su verdadera escala: el JDK que tienes instalado no es solo el compilador y la máquina virtual que conociste en la UD1; es también una biblioteca gigante de clases listas para usar, escrita, probada y documentada por los ingenieros de Java a lo largo de casi tres décadas. A esa biblioteca se la llama el API de Java (Application Programming Interface: la interfaz que la plataforma ofrece al programador para construir sobre ella).

El tamaño es difícil de exagerar. Hablamos de miles de clases: clases para texto, para matemáticas, para fechas, para colecciones de datos, para leer ficheros, para hablar por red. Ninguna persona del planeta se la sabe entera, y ninguna necesita sabérsela: los profesionales de Java trabajan a diario con una fracción mínima del API y consultan la documentación para todo lo demás. Por eso esta sección no intenta enseñarte clases: intenta enseñarte a orientarte en el almacén.

Imagínate que te mudas a un piso nuevo y el anterior inquilino —un carpintero jubilado— te deja el trastero lleno de herramientas: miles, ordenadas por estanterías, y cada una con su ficha de instrucciones pegada al mango. Fabricar tú un destornillador sería una tontería; lo razonable es aprender qué estanterías hay y cómo leer las fichas. En esta metáfora, las estanterías son los paquetes del API y las fichas son el Javadoc que leeremos en la sección 2.2. Vamos con las estanterías.

Paquetes: el apellido de cada clase

Un paquete (package) es una agrupación con nombre de clases relacionadas: funciona como el apellido de una clase. La clase Random no se llama solo Random a efectos del compilador: se llama java.util.Random, donde java.util es su paquete. Ese apellido evita colisiones en un mundo donde miles de programadores escriben clases: el JDK, por ejemplo, tiene dos clases llamadas Date —java.util.Date y java.sql.Date— que coexisten sin pelearse precisamente porque viven en paquetes distintos. El nombre completo con paquete se llama nombre cualificado (fully qualified name).

Para esta unidad te interesan cuatro estanterías, todas con clases que usarás en las próximas semanas:

Paquete Qué guarda Clases que ya conoces (o conocerás) Cuándo en el curso
java.lang El equipamiento básico del lenguaje String, Math, Integer, IO, todas las excepciones Lo usas desde la UD1; toda la unidad
java.util Utilidades generales Arrays (UD2), Scanner (UD1), Random (sección 5) Toda la unidad; colecciones en UD5
java.math Aritmética de precisión arbitraria BigInteger, BigDecimal Pinceladas puntuales cuando la precisión importe
java.time Fechas, horas y duraciones LocalDate, LocalTime, Duration Sección 6 de esta unidad

La trampa más clásica del tema

Math no vive en java.math. La clase Math está en java.lang, junto a String e Integer, y por eso nunca has necesitado importarla. El paquete java.math es otro barrio: el de BigInteger y BigDecimal, la aritmética de precisión ilimitada que se usa cuando un long se queda corto o cuando el dinero no admite errores de redondeo. Confundir los dos es el despiste clásico de examen («¿en qué paquete está Math?») y una pregunta habitual en entrevistas juniors.

Por encima de los paquetes hay un nivel más de organización que te va a aparecer escrito en varios sitios, así que mejor presentárselo ya: los módulos. Un módulo es un cajón con nombre que agrupa paquetes, y java.base es el cajón maestro: el núcleo esencial de la plataforma, que contiene los cuatro paquetes de la tabla anterior y muchos más. Del sistema completo de módulos no nos ocuparemos en el ciclo; te basta con reconocer la palabra, porque la verás en la URL de la documentación (sección 2.2) y en algún mensaje del compilador.

Use mouse to pan and zoom
flowchart LR
    jdk["JDK 25 · el API de Java (miles de clases)"]
    base["java.base · el módulo núcleo"]
    lang["java.lang\n(el bolsillo: siempre contigo)"]
    util["java.util\n(la caja de herramientas general)"]
    math["java.math\n(la aritmética de precisión)"]
    time["java.time\n(la relojería del API)"]
    c1["String · Math · Integer · IO\ny todas las excepciones"]
    c2["Arrays · Random · Scanner"]
    c3["BigInteger · BigDecimal"]
    c4["LocalDate · LocalTime · Duration"]
    jdk --> base
    base --> lang
    base --> util
    base --> math
    base --> time
    lang --> c1
    util --> c2
    math --> c3
    time --> c4
    style jdk fill:#2e7d32,color:#ffffff,stroke:#1b5e20
    style base fill:#1b5e20,color:#ffffff,stroke:#0d330f
    style lang fill:#c8e6c9,color:#000000,stroke:#2e7d32
    style util fill:#c8e6c9,color:#000000,stroke:#2e7d32
    style math fill:#c8e6c9,color:#000000,stroke:#2e7d32
    style time fill:#c8e6c9,color:#000000,stroke:#2e7d32

El import: un apodo para el compilador

¿Y cómo se usa una clase de otro paquete? Tienes dos opciones. La primera es escribir su nombre cualificado completo cada vez: java.util.Random dado = new java.util.Random();. Funciona, pero es insufrible y nadie lo hace. La segunda es declarar al principio del fichero un import:

import java.util.Random;

Con esa línea le dices al compilador: «en lo que queda de fichero, cuando escriba Random, me refiero a java.util.Random». Y aquí conviene desmontar un malentendido frecuente, porque afecta a cómo piensas sobre la memoria: importar no es cargar. El import no copia código a tu programa, no consume memoria y no hace nada en tiempo de ejecución; es puro azúcar para el compilador, un apodo que escribes una vez para no repetir el apellido veinte veces. Tu programa usaría exactamente la misma memoria con import que con nombres cualificados a mano.

Hay una regla que ya conoces de la sección 1 y que ahora queda integrada en el sistema completo: java.lang se importa siempre de forma automática en todo programa Java. Por eso String, Math, Integer y IO nunca han necesitado una línea de import en ningún programa tuyo. Todo lo demás —java.util, java.time, java.math— está a un import de distancia.

Sobre la forma del import, dos matices de estilo. Primero, existe la variante «comodín»: import java.util.*;, que importa bajo demanda todas las clases del paquete. Es legal y la verás en código ajeno, pero en este curso no la usaremos: con el import de una sola clase, cualquiera que lea tu fichero ve de un vistazo qué clases foráneas usa el programa, y se evitan sorpresas cuando dos paquetes exportan clases con el mismo nombre. Segundo, los imports se colocan al principio del fichero, después de la eventual declaración de paquete (en seguida la verás) y antes de cualquier código.

La verdad completa sobre los import (JEP 512)

En la sección 1 te dije que había que «decirle al compilador dónde vive» la clase Random, y por eso escribimos import java.util.Random;. Ha llegado el momento de completar el relato, porque los ficheros compactos que usamos en el curso (los de void main() sin clase visible, JEP 512) reciben un regalo: todo el módulo java.base viene preimportado automáticamente, como si el fichero empezara con una línea invisible import module java.base; (la importación de módulos, JEP 511, es otra novedad de Java 25). En consecuencia, en nuestros programas el import java.util.Random; es en realidad opcional: el compilador encontraría Random igualmente.

¿Y entonces por qué lo seguimos escribiendo? Tres razones. Documenta de un vistazo qué clases foráneas usa el programa (los imports son el «equipaje declarado» del fichero). Evita ambigüedades si algún día dos paquetes traen clases homónimas. Y te acostumbra al Java «de verdad» que escribirás desde la UD4, donde las clases propias viven en paquetes con nombre y el import deja de ser opcional. Un buen hábito a coste cero.

El pariente raro: import static

Existe una variante que verás en código profesional: import static java.lang.Math.PI;, que importa miembros concretos de una clase (ese campo, esos métodos estáticos) para escribirlos sueltos: PI a pelo en vez de Math.PI. Es legal desde hace muchas versiones y puede acortar fórmulas matemáticas densas, pero oculta de dónde viene cada nombre, así que en este curso no la usaremos. Si te la cruzas leyendo código, ya sabes qué significa.

El paquete sin nombre: donde viven (por ahora) tus programas

Una última pieza del puzle: ¿en qué paquete viven nuestros ficheros compactos? En ninguno con nombre: viven en el paquete sin nombre (unnamed package), la zona de tránsito que Java reserva para programas pequeños. Para tus ejercicios de una pantalla es perfecta, pero el código profesional huye de ella: sin paquete no hay apellido, y en un proyecto con cientos de clases los choques de nombres están garantizados. Cuando en la UD4 empieces a escribir clases propias, dejarán de ser ficheros compactos y pasarán a vivir en paquetes con nombre; en este módulo usaremos jams.programacion.ud3 para la unidad actual. Así se verá esa cabecera el día que cruces esa puerta (es un adelanto de cabecera: el cuerpo de la clase llegará con la UD4):

1
2
3
4
5
6
7
8
package jams.programacion.ud3;

import java.util.Random;

public class GeneradorDados {
    // En la UD4 escribirás clases completas con paquete, import y clase explícita.
    // Hoy este cuerpo está vacío a propósito: solo estamos enseñando la cabecera.
}

Fíjate en el orden de las dos primeras líneas, porque es invariable en todo Java: primero package (el apellido de TUS clases), luego los import (los apodos de las clases ajenas que vas a usar), y después el código. Ese orden lo has estado viendo invertido en los ficheros compactos —donde no hay package y los import van solos al principio— y así seguirá siendo hasta la UD4.

2.2 Leer la Javadoc: el manual de instrucciones del API

Volvamos a la metáfora del trastero: cada herramienta tiene su ficha de instrucciones pegada al mango. En el API de Java, ese papel se llama Javadoc: la documentación oficial, uniforme y generada automáticamente para todas las clases de la plataforma. Y antes de entrar en cómo se lee, instala la actitud correcta: nadie memoriza el API; nadie necesita hacerlo. El profesional de Java consulta la documentación varias veces al día, sin la menor vergüenza, igual que un cocinero consulta su libro de recetas. La destreza que separa a quien «googlea a ciegas y copia lo primero que encuentra» de quien resuelve el problema de verdad es, precisamente, saber leer esta documentación. Es la herramienta que más vas a usar durante toda la unidad, y hoy la abrimos por primera vez con criterio.

Dónde está y cómo se organiza

La referencia del JDK 25 vive en la web de Oracle:

https://docs.oracle.com/en/java/javase/25/docs/api/

Guárdala en la barra de marcadores ahora mismo: es la dirección oficial y la versión que corresponde exactamente a tu JDK. Nada de tutoriales de terceros cuando dudes: la fuente de verdad siempre es esta. La página se organiza exactamente igual que el mapa de la sección 2.1: primero módulos, dentro paquetes, dentro clases. De hecho, la propia URL de una clase te lo delata. Si abres la página de String, su dirección es .../docs/api/java.base/java/lang/String.html: módulo java.base, paquete java.lang, clase String. La documentación es el mapa del API escrito en forma de URL.

En la columna de navegación encontrarás enlaces como Index (todas las clases de la A a la Z), Tree (la jerarquía de herencia completa —sí, la misma herencia que conociste en la sección 1—), Deprecated (las piezas marcadas en desuso) y, sobre todo, la caja de búsqueda, que es tu punto de entrada natural: escribe Random, pulsa Enter y aterrizas directamente en su página.

Anatomía de la página de una clase

Todas las páginas de clase del API siguen el mismo esquema, que es además el esquema de cualquier clase Java bien documentada. Aprender a moverte por una es aprender a moverte por todas:

Zona de la página Qué encontrarás Para qué te sirve
Declaración de la clase Firma completa: modificadores, clase padre, interfaces que implementa Saber si es final, de quién desciende, qué contrato firma
Descripción Varios párrafos explicando qué representa la clase y sus reglas de uso Entender el propósito antes de tocar un solo método
Constructor Summary Los constructores que ofrece, con descripción de una línea Elegir cómo nace tu objeto (sección 2.4)
Method Summary El repertorio completo de métodos: firma y descripción de una línea Ojear todo lo que sabe hacer en 30 segundos
Method Detail Cada método a fondo: Parameters, Returns, Throws, Since La ficha técnica exacta antes de usarlo

La estrategia de lectura que te recomiendo para una clase nueva es esa, de arriba abajo: descripción (¿qué es esto?), constructor summary (¿cómo se fabrica?), method summary (¿qué sabe hacer?) y, solo para los métodos que vayas a usar, el detail. Leer el detail de los ochenta métodos de String sería una locura; leerlo del único método que necesitas son treinta segundos bien invertidos.

Cómo se lee un método: la ficha de nextInt

Vamos a leer juntos una ficha real, la del método que ya usaste en la sección 1 con el dado. En la página de java.util.Random, bajando al Method Detail de nextInt, encuentras algo así (traducido):

int nextInt(int bound) Parameters: bound — el límite superior (exclusivo). Debe ser positivo. Returns: un valor int seudoaleatorio, uniformemente distribuido, entre 0 (incluido) y el límite indicado (excluido). Throws: IllegalArgumentException — si el límite no es positivo.

Fíjate en la estructura, porque es idéntica en los miles de métodos del API. Parameters describe cada dato que debes entregar y qué significa. Returns describe qué te devolverá la llamada, con las condiciones exactas. Throws avisa de las excepciones que puede lanzar —y que, como aprendiste en la UD2, puedes capturar con try-catch—. Ahora releamos nuestra propia línea de la sección 1 con esta ficha delante:

IO.println(dado.nextInt(6) + 1);   // tirada de dado: entre 1 y 6

Con la documentación leída, esa aritmética deja de ser un conjuro. La ficha dice que nextInt(6) devuelve valores entre 0 (incluido) y 6 (excluido), es decir, de 0 a 5. Nosotros queremos un dado de 1 a 6, así que sumamos 1. Fin del misterio: la documentación acaba de explicarte tu propio código, y a partir de aquí ya no necesitarás memorizar rangos ni fórmulas: podrás deducirlos leyendo la ficha. Este mismo mecanismo —leer la ficha, ajustar la llamada— es el que usarás con cada método nuevo de esta unidad.

Dos anotaciones más que viven en las fichas y que te van a acompañar siempre:

  • Since: es la partida de nacimiento del miembro: la versión de Java en la que se incorporó. String.repeat() luce un Since: 11; nuestra clase IO, un Since: 25: es más joven que casi todo el API —String existe desde la versión 1.0 del lenguaje—, y ahí está el detalle para demostrarlo. Cuando un método te suene a moderno, el Since te lo confirma.
  • Deprecated es la etiqueta de jubilación: marca piezas que funcionan pero que conviene no usar, normalmente porque existe una alternativa mejor. La descripción del miembro suele decir cuál es el sustituto recomendado, y esa es la parte que importa: lee qué proponen y mígrate. El API envejece con dignidad —no borra casi nada, para no romper los programas antiguos— pero tú no debes heredar sus vicios. La historia de jubilación más famosa del JDK es la de las clases de fecha antiguas, y la sección 6 de esta unidad te la contará.
¿Y las características en «preview»?

En la navegación de la Javadoc verás un enlace Preview: agrupa el API que aún está en fase de vista previa, a medio camino entre experimental y definitivo. En este curso solo usamos características estables, así que no lo necesitas; pero conviene reconocer la palabra, porque delimita qué parte del API puedes usar en un trabajo profesional sin riesgo.

Hay un detalle bonito que ata cabos con la UD1: esta documentación no la escribe un equipo aparte. Se genera automáticamente a partir de los comentarios /** ... */ que los propios ingenieros escriben dentro del código fuente del JDK —los mismos comentarios Javadoc que viste en la UD1 y que tú escribirás en tus clases a partir de la UD4—. Por eso, si haces Ctrl+clic sobre nextInt para saltar al fuente (como hiciste con String en la sección 1), verás literalmente el mismo texto de la ficha, pero tumbado en un comentario sobre el método. La doc no es un adorno pegado al código: es el código documentándose a sí mismo.

La Javadoc dentro de IntelliJ: Ctrl+Q

No hace falta abrir el navegador para consultar una ficha. Coloca el cursor sobre cualquier clase, método o campo de tu programa —por ejemplo, sobre nextInt— y pulsa Ctrl+Q (o View → Quick Documentation): IntelliJ te muestra la Javadoc renderizada, sin salir del editor. Es la misma documentación de la web, a un golpe de teclado. La combinaremos con más atajos en la sección 2.5.

Tu primera consulta oficial, paso a paso

Hazlo ahora, conmigo, una vez; las siguientes te saldrán solas:

  1. Abre https://docs.oracle.com/en/java/javase/25/docs/api/ y márcala como favorita.
  2. Escribe Random en la caja de búsqueda y pulsa Enter: aterrizarás en la página de java.util.Random.
  3. Lee las dos primeras frases de su descripción: qué es y para qué sirve.
  4. Baja al Method Summary y localiza nextInt entre la lista.
  5. Sigue el enlace de nextInt(int bound) hasta su Method Detail y relee la ficha que acabamos de comentar: Parameters, Returns, Throws.
  6. Repite el ciclo completo con otra clase que conozcas: busca String, encuentra length() en el Method Summary y lee su ficha. Nada nuevo bajo el sol —«la longitud de esta cadena»—, pero fíjate en la palabra this rondando la descripción: la doc también habla «en instancia».

Cuando este circuito de seis pasos te resulte natural, habrás adquirido la destreza que esta sección considera más importante: la autonomía para descubrir el API por tu cuenta. El resto de la unidad consistirá, precisamente, en ponerte delante de clases nuevas —Math, Random, java.time, StringBuilder, los wrappers— y dejar que la documentación trabaje para ti.

2.3 Instancia frente a estático: la promesa de la sección 1, cumplida

La sección 1 te presentó las dos formas del operador punto «de pasada» y dejó citado este momento: aquí llega la diferencia exacta. Y aunque el tema parece tener muchas aristas, la clave entera cabe en una pregunta: ¿quién está a la izquierda del punto? Ese algo —la clase o el objeto— se llama el receptor de la llamada, y decidir bien quién es el receptor correcto de cada miembro es, a efectos prácticos, la mitad de todo lo que hay que saber para usar el API con soltura.

Los dos mundos, con precisión

Un método de instancia vive definido en la clase, pero trabaja sobre un objeto concreto: su código usa el estado de ese ejemplar. length() recorre los caracteres de esta cadena; nextInt(6) consulta el estado interno de este generador. Por eso no puedes pedir length() «en general»: la pregunta ni siquiera tiene sentido sin un ejemplar — «¿longitud?... ¿de qué cadena?». Sin objeto que reciba el mensaje, no hay respuesta posible, y si lo intentas directamente sobre la clase, el compilador te lo impedirá (en seguida verás el error exacto).

Un método estático (la palabra clave es static, y ya la irás viendo en firmas y documentación) pertenece a la clase como colectivo: su ejecución no mira el estado de ningún ejemplar, y su resultado depende únicamente de los argumentos que le pases. Integer.parseInt("42") no consulta a ningún «objeto Integer» particular: convierte y listo. Math.max(3, 9) solo mira sus dos argumentos. IO.println(...) solo mira lo que le entregas. Recuerda la analogía de la sección 1: preguntarle a un taxista concreto cuánto costará un trayecto es una consulta de instancia (la respuesta sale de su taxímetro); consultar el listado de tarifas del gremio es una consulta estática (pertenece al colectivo, no a ningún coche, y no requiere que haya un solo taxi en la calle).

De ahí sale la regla mental que te servirá para leer cualquier código ajeno, y que conviene grabar a fuego:

Si la respuesta cambiaría según qué ejemplar concreto responda, el miembro es de instancia. Si cualquier ejemplar daría exactamente la misma respuesta —porque la respuesta solo depende de los argumentos—, el miembro es estático.

Míralo funcionando en un programa que solo usa cosas conocidas:

import java.util.Random;

void main() {
    Random dado = new Random();            // nace UN objeto concreto de la clase Random

    IO.println(dado.nextInt(6) + 1);       // INSTANCIA: la respuesta sale del estado
                                           // interno de ESTE objeto (su semilla)
    IO.println(Math.max(3, 9));            // ESTÁTICO: la respuesta solo mira los
                                           // argumentos; ningún objeto participa
    IO.println(Integer.parseInt("42"));    // ESTÁTICO: utilidad pura de la clase;
                                           // devuelve 42 como int
}

Tres llamadas, dos mundos. La primera necesita que dado exista: si borras la línea del new, el programa ni compila. Las otras dos funcionarían solas en la primera línea del programa: nadie ha fabricado ningún Math ni ningún Integer.

Clases de utilidad: el mundo de los «todo estático»

Hay clases cuyo repertorio es cien por cien estático: Math, Arrays, IO. Piensa un momento por qué: ¿qué guardarías dentro de «un objeto Math»? No tiene estado, no hay nada que le pertenezca a un ejemplar; sus operaciones son servicios puros del colectivo. A estas se las llama clases de utilidad, y de ellas se deriva una consecuencia práctica: jamás reciben new. Nunca verás new Math(), new Arrays() ni new IO() en código que funcione.

¿Y qué pasa si lo intentas? El propio JDK lo impide por diseño: estas clases esconden su constructor marcándolo como privado, y el compilador te lo hace saber con un mensaje del estilo Math() has private access in java.lang.Math. Ese blindaje es una decisión de encapsulación —de la que ya sabes apreciar la lógica— y en la UD4, sección 6, cuando escribas tus propias clases, lo reproducirás tal cual para tus utilidades. Por ahora, quédate con la regla de lectura: clase de utilidad = clase sin objetos = todo se pide a la clase.

Otras clases, en cambio, viven en el mundo contrario. Random es el ejemplo que ya manejas: casi todo su repertorio es de instancia, porque cada objeto guarda su propia semilla y su propia secuencia. Y las clases más ricas —como verás en seguida con String— mantienen ambos mundos abiertos a la vez.

Miembro de instancia Miembro estático
Se pide a... un objeto concreto: referencia.metodo() la clase: Clase.metodo()
Su código puede usar... el estado de ese objeto solo sus argumentos (y otros miembros estáticos)
Necesita new antes de la llamada sí no
Ejemplos que ya conoces length(), nextInt(6), toUpperCase() parseInt("42"), sqrt(2), IO.println(...)
Si lo pides al receptor equivocado... Clase.metodo() → no compila vía referencia: compila, pero es mala praxis

La última fila merece su propio desarrollo, porque los errores de esta frontera son los más frecuentes del primer trimestre. Vamos a verlos uno a uno.

Los tres errores de la frontera

Error 1: pedirle a la clase lo que solo un objeto sabe. Escribe String.length() y el compilador responde con el clásico de los clásicos:

1
2
3
4
void main() {
    IO.println(String.length());   // ¡ERROR de compilación!
    // non-static method length() cannot be referenced from a static context
}

Léelo despacio, porque es un mensaje que verás cientos de veces en tu vida: «el método no estático length() no puede ser referenciado desde un contexto estático». Traducción a tu modelo mental: le pediste la longitud a la plantilla, y la plantilla no tiene caracteres; solo las instancias los tienen. La plantilla ni siquiera sabría por dónde empezar a contestar. Solución: fabrica (o localiza) una cadena concreta y pídeselo a ella: IO.println("hola".length());.

Error 2: fabricar lo infabricable. Escribe new Math() y el compilador te recuerda que el constructor de Math es privado (has private access). Ya sabes la lectura: clase de utilidad, sin objetos por diseño. No hay aquí truco que valer: si un miembro es estático, se pide a la clase.

Error 3 (el cobarde): llamar a un estático a través de un objeto. Java, por razones históricas, permite invocar un método estático usando una referencia como receptor:

String frase = "java";
String resultado = frase.format("%d puntos", 42);   // compila, pero NO lo hagas

Eso compila y funciona —devuelve "42 puntos"—, pero fíjate en la aberración: el resultado ignora por completo a frase; la llamada se ejecuta como si hubieras escrito String.format(...). Es una puerta que Java dejó abierta y que solo siembra confusión: quien lee frase.format(...) entiende que la respuesta depende de frase, y es mentira. IntelliJ lo subraya en cuanto lo escribes, avisando que estás accediendo a un método estático a través de una instancia. La regla de estilo es absoluta: el estático se pide siempre a la clase, con su nombre: String.format(...). (El método format llega a fondo en la sección 3; hoy quédate únicamente con la regla.)

Campos estáticos: los datos del colectivo

La distinción instancia/estático no es exclusiva de los métodos: los campos también se reparten en los dos mundos. Un campo estático es un dato que pertenece a la clase como colectivo y del que existe una sola copia compartida, en lugar de una copia por objeto. El caso de uso rey en el API son las constantes de clase: valores fijos, con nombre, que la clase publica para que los uses sin reescribirlos jamás.

Ya conoces dos, y van a acompañarte toda la unidad: Math.PI (3.141592653589793, la constante con más siglos de servicio del cálculo) e Integer.MAX_VALUE (2147483647, el mayor int representable —un adelanto de la sección 8, donde estudiaremos los wrappers—). Junto a ellas, Integer.MIN_VALUE, Math.E y muchas otras siguen el mismo patrón: se leen con la clase como receptor, sin new alguno, y sus nombres van en MAYUSCULAS_CON_BARRAS_BAJAS —la convención de constantes que conociste en la UD1—.

1
2
3
4
5
6
7
8
9
void main() {
    IO.println(Math.PI);              // campo ESTÁTICO de la clase Math: una única
                                      // copia para todo el programa
    IO.println(Integer.MAX_VALUE);    // otra constante de clase: el techo de int

    String saludo = "hola";
    IO.println(saludo.length());      // frente a length(): un valor que pertenece
                                      // a CADA objeto, no a la clase
}

Contrasta las dos columnas de ese programa: Math.PI es del colectivo (una copia, compartida, se lee en la clase); la longitud de saludo es de ese objeto concreto (cada String tiene la suya). Campos de clase frente a campos de objeto: el mismo reparto que ya hicimos con los métodos.

La joya de la corona: diseccionar System.out.println

Llega el momento de desmontar la llamada más famosa de Java, esa que escribiste mil veces en la UD1 antes de conocer a IO. Seguro que la recuerdas:

System.out.println("hola");

Hoy puedes leerla completa, eslabón a eslabón, porque ya dominas todos los conceptos que intervienen:

1
2
3
4
5
6
7
System.out.println("hola");
//   ^      ^      ^
//   |      |      método de INSTANCIA de PrintStream: se ejecuta sobre
//   |      |      el objeto que hay en out, y usa su estado (su consola)
//   |      campo ESTÁTICO de la clase System: guarda UN objeto de tipo
//   |      PrintStream que ya existe; es la puerta a la consola
//   la CLASE System, que vive en java.lang

Es una cadena de puntos de dos eslabones que atraviesa los dos mundos: el primero (System.out) baja de la clase a un campo estático, que resulta guardar un objeto; el segundo (.println(...)) llama a un método de instancia de ese objeto. Un campo estático puede guardar una referencia —recuerda la sección 1: una flecha hacia el montón—, y sobre esa flecha se puede seguir encadenando. La mecánica es idéntica a la del encadenado frase.strip().toUpperCase() que ya analizaste; solo que aquí el primer eslabón es un campo de clase.

Use mouse to pan and zoom
flowchart LR
    sys["System\n(la clase · java.lang)"]
    out["out\n(campo ESTÁTICO: guarda un objeto\nPrintStream ya creado)"]
    p["println(...)\n(método de INSTANCIA\nde ese PrintStream concreto)"]
    sys -- "primer punto" --> out
    out -- "segundo punto" --> p
    style sys fill:#2e7d32,color:#ffffff,stroke:#1b5e20
    style out fill:#c8e6c9,color:#000000,stroke:#2e7d32
    style p fill:#fff3e0,color:#000000,stroke:#e65100

¿Y por qué el curso usa IO.println(...) en su lugar? Precisamente para quitarte esa ceremonia los primeros meses: la clase IO de Java 25 (JEP 512) ofrece println como método estático directo —Clase.metodo() en un solo eslabón— y por dentro delega en ese mismo System.out. Ambas pintan en la misma consola; la tuya es simplemente el ascensor exprés. Pero System.out.println sigue estando en cada programa Java del planeta, así que de ahora en adelante dejará de ser un conjuro y será una frase con gramática completa.

Un detalle fino: ¿por qué println acepta lo que le eches?

Si haces Ctrl+Q sobre println, verás que su parámetro es de tipo Object: «cualquier objeto». Entonces, ¿por qué compila IO.println(42) si 42 es un int, un primitivo y no un objeto? La respuesta se llama autoboxing: Java convierte automáticamente el primitivo en su versión orientada a objetos (un objeto Integer) para que quepa en el parámetro. La sección 8 de esta unidad le dedica un análisis completo, porque ese truco tiene afectación en memoria y rendimiento. Hoy basta con que sepas que el embrague existe.

Las fábricas estáticas: el puente entre los dos mundos

Queda una última pieza del reparto, y es la más elegante. Algunas clases ofrecen métodos estáticos cuyo trabajo es fabricar objetos y devolverlos ya listos. Se llaman fábricas estáticas, y el ejemplo que te espera en la sección 6 es LocalDate.now(): una llamada al colectivo (LocalDate, la clase) que te devuelve un objeto —la fecha de hoy— sobre el que seguirás trabajando con métodos de instancia. El puente entre ambos mundos en una sola expresión: LocalDate.now().getYear() es «fábrica estática que crea el objeto; método de instancia que pregunta a ese objeto».

¿Por qué existen, si ya está new? Dos motivos que podrás apreciar de primera mano cuando lleguen. Primero, el nombre: LocalDate.now() o LocalDate.of(2026, 10, 30) dicen lo que hacen, mientras que new LocalDate(...) no dice nada. Segundo, el control: algunas clases —LocalDate entre ellas— esconden sus constructores y solo permiten nacer por fábrica, para garantizar que todos sus objetos salgan validados. En la sección 5 verás que Random nace por constructor, en la 6 que LocalDate nace por fábrica, y en la UD4 —sección 3— escribirás tus propios constructores y entenderás la elección desde el otro lado del mostrador.

¿Cómo saber, ante un miembro nuevo, cuál es cuál?

Tres vías, en orden de rapidez, y las tres a tu alcance desde hoy:

  1. La regla mental: ¿la respuesta dependería de un ejemplar concreto? Regla de bolsillo, funciona incluso leyendo código en papel.
  2. El autocompletado por receptor: en IntelliJ, lo que aparece tras escribir el punto depende de quién sea el receptor — una referencia lista los miembros de instancia; un nombre de clase lista los estáticos. Es la vía rápida mientras programas, y la protagonista de la sección 2.5.
  3. La firma en la documentación: tanto en la Javadoc web como en Ctrl+Q, la firma completa del miembro incluye, cuando procede, el modificador static. La fuente de verdad, para casos de duda.

2.4 Constructores: el menú de nacimiento de cada clase

Repasemos los tres pasos de new que establecimos en la sección 1: reserva memoria en el montón, inicializa el objeto y devuelve la referencia. El paso central —la inicialización— no es magia: lo dirige el constructor, el código de la clase que deja el objeto listo para trabajar (el que arranca la semilla interna de un Random, por ejemplo). Y aquí viene el matiz que hoy añade nada menos que vocabulario nuevo: los paréntesis que escribes junto a new no son decoración. Son la puerta de entrada del constructor: ahí decides cuál de los constructores de la clase se ejecuta y qué datos le entregas de partida.

La buena noticia es que ya sabes leerlos, porque viven exactamente donde acabas de aprender a mirar: en la Javadoc, bajo el Constructor Summary. Abre la página de java.util.Random y compruébalo: ofrece dos constructores, y la descripción de cada uno cuenta una historia distinta:

Constructor Qué hace (según su ficha) Cuándo lo quieres
Random() Crea un generador con una semilla «impredecible», distinta en cada ejecución Juegos, sorteos, simulaciones: cada ejecución, distinta
Random(long seed) Crea un generador con la semilla que TÚ le das Experimentos y pruebas: cada ejecución, idéntica (sección 5)

Mira lo que acaba de pasar: elegir constructor es elegir el nacimiento del objeto. Con new Random() compras sorpresa; con new Random(42) compras repetición. El objeto que nace de cada uno se comporta de forma radicalmente distinta a lo largo de toda su vida, y la diferencia se decidió en una sola línea, con un par de paréntesis. Compruébalo en carne propia:

import java.util.Random;

void main() {
    // Misma semilla: la secuencia que produce el objeto es SIEMPRE la misma.
    // Ejecuta el programa dos veces y compara los tres números...
    Random conSemilla = new Random(42);

    IO.println(conSemilla.nextInt(100));   // mismo valor en cada ejecución
    IO.println(conSemilla.nextInt(100));   // y el mismo segundo valor...
    IO.println(conSemilla.nextInt(100));   // y el mismo tercero (la sección 5 lo explica)
}

Ejecuta ese programa tres veces y anota los números: son siempre los mismos tres valores. Cambia la semilla a 7 y obtendrás otra secuencia distinta... pero también constante entre ejecuciones. Vuelve a new Random() (sin argumentos) y cada ejecución dará valores nuevos. Acabas de presenciar, sin necesidad de teoría adicional, para qué sirve un constructor con parámetros: fijar el estado inicial que condicionará todo el resto de la vida del objeto. La sección 5 convertirá esta curiosidad en una herramienta profesional de depuración (la reproducibilidad); hoy quédate con la mecánica.

La sobrecarga: mismo nombre, distinta lista de parámetros

Que una clase ofrezca varios constructores con el mismo nombre (el de la clase) y distinta lista de parámetros tiene nombre propio: sobrecarga (overloading). La regla es simple y el compilador la aplica con rigor: ante varias versiones del mismo nombre, elige la que encaja con los argumentos que tú has escrito. Escribe new Random() y activa el constructor sin parámetros; escribe new Random(42) y activa el que recibe un long. Misma palabra, dos vidas distintas, seleccionadas por los argumentos.

Y aquí viene la revelación bonita de la sección: llevas usando sobrecarga desde el primer mes de curso, sin que nadie te lo dijera. La clase IO —esa que creías conocer de memoria— mantiene una familia de métodos sobrecargados. Mira su repertorio completo, sacado directamente de su Javadoc:

Llamada Versión que se ejecuta Para qué
IO.println() println() — sin argumentos Imprimir una línea en blanco
IO.println("hola") println(Object obj) — un argumento Imprimir lo que le eches y saltar de línea
IO.readln() readln() — sin argumentos Leer una línea tecleada por el usuario
IO.readln("Nombre: ") readln(String prompt) — un argumento Leer una línea mostrando antes un mensaje guía

Cuando en la UD1 escribías IO.println("hola") y en otro programa IO.println(42), estabas invocando siempre la misma versión (la de Object, con el primitivo autoboxeado gracias al embrague que mencionamos antes), y cuando alternabas IO.readln() con IO.readln("Dime tu edad: "), cambiabas de versión sin saberlo. Nadie te lo explicó porque no hacía falta; pero a partir de hoy, cuando veas una lista de métodos con el mismo nombre en el autocompletado o en la Javadoc, ya no te parecerá un despiste de los diseñadores: es una familia sobrecargada, y tú eliges el miembro con los argumentos.

La sobrecarga se aplicará igual a los métodos y a los constructores que escribirás en la UD4 (secciones 3 y 5, donde además aprenderás a encadenar constructores con this(...)). Por hoy, la destreza que se te exige es de lectura: ante varios constructores en la Javadoc, leer cada descripción y elegir conscientemente el nacimiento que conviene a tu programa.

El caso de las clases que esconden sus constructores

Si abres ahora la Javadoc de LocalDate y buscas su Constructor Summary, la encontrarás vacía o casi: sus constructores son privados y la clase solo permite nacer por fábricas estáticas — LocalDate.now(), LocalDate.of(2026, 10, 30) —, tal y como adelantamos en la sección 2.3. Es el patrón contrario a Random, y coexisten en el API por motivos de diseño que sabrás apreciar con la UD4 a cuestas. No te extrañe, pues, que algunas clases «no se dejen» instanciar con new: te están indicando por dónde quieren que entres.

2.5 IntelliJ: la API en la punta de los dedos

Toca la última herramienta de la sección, y no es la menor: el entorno donde todo lo anterior se vuelve fluido. IntelliJ conoce el API completo —indexó el JDK la primera vez que abriste el proyecto, en la UD1— y está diseñado para que nunca programes de memoria, sino dialogando con el editor. La destreza que define a un programador productivo con Java no es recitar métodos: es un circuito de descubrimiento que ejecuta en segundos, tantísimas veces al día que se vuelve reflejo. Vamos a montarlo por piezas.

La técnica del receptor: Ctrl+Space

Escribe generador seguido de un punto y detente un instante: IntelliJ despliega la lista de miembros de instancia del objeto — todo lo que un Random concreto sabe hacer (nextInt, nextBoolean, nextDouble...). Ahora borra y escribe Random (mayúscula, la clase) seguido de punto: la lista cambia — muestra los miembros estáticos que la clase ofrece al colectivo. Y remata el experimento con Math + punto: solo estáticos, a saco (abs, max, sqrt, PI...).

Ese es el truco del receptor, y es exactamente la vía rápida número dos de la sección 2.3: lo que aparece tras el punto lo filtra quién está antes del punto. Sin abrir ninguna documentación, el autocompletado ya te está diciendo qué puedes pedir a cada receptor. Si un día dudas de si un miembro es de instancia o estático, este experimento de quince segundos te lo responde.

El atajo oficial es Ctrl+Space (autocompletado básico), aunque IntelliJ también despliega la lista solo con escribir el punto. Acepta con Enter y observa un detalle delicioso: el IDE escribe la llamada completa —paréntesis incluidos— y coloca el cursor dentro, esperando los argumentos.

Los dos acompañantes: Ctrl+Q y Ctrl+P

El circuito se completa con dos atajos que ya puedes situar:

  • Ctrl+Q (Quick Documentation): sobre cualquier clase, método o campo, abre su ficha Javadoc renderizada —la misma de la sección 2.2— sin salir del editor. Dudas de qué hace nextInt: cursor encima, Ctrl+Q, y ahí está la ficha con sus Parameters, Returns y Throws. Y si la firma empieza por static, ya sabes en qué mundo vive.
  • Ctrl+P (Parameter Info): colócate dentro de los paréntesis de una llamada —entre los argumentos o justo tras abrir el paréntesis— y pulsa Ctrl+P: IntelliJ te muestra qué parámetros espera el método, en qué orden y de qué tipos. Es la respuesta instantánea a «¿qué le tengo que pasar a esto?», sin idas y vueltas al navegador.

Añade los dos veteranos que ya conoces y la mesa queda servida: Ctrl+B (o Ctrl+clic) salta al código fuente del JDK, y Alt+Enter propone la solución cuando algo está subrayado — por ejemplo, añadir el import que te falta cuando escribes una clase de java.util sin declararla (pruébalo: borra el import de un programa que use Random y ejecuta el rescate).

Atajo Nombre Qué te da Cuándo usarlo
Ctrl+Space Autocompletado básico Miembros aplicables al receptor Al escribir cualquier llamada
Ctrl+Q Quick Documentation Ficha Javadoc en el editor Antes de usar un miembro nuevo
Ctrl+P Parameter Info Parámetros, orden y tipos Dentro de los paréntesis de la llamada
Ctrl+B Go to Declaration El código fuente del JDK Cuando quieres ver «cómo está hecho»
Alt+Enter Intención rápida El arreglo propuesto por el IDE Cuando algo aparece subrayado

El circuito de descubrimiento, resumido

Con las cinco piezas montadas, así se ve el flujo completo que usarás el resto de la unidad —y el resto de tu carrera— cada vez que te enfrentes a un miembro del API que no conoces:

Use mouse to pan and zoom
flowchart LR
    a["Escribe el receptor\n(objeto o clase) + punto"]
    b["Ctrl+Space:\nexplora los miembros\nque ofrece ese receptor"]
    c["Elige uno\n(por su nombre y su\ndescripcion de una linea)"]
    d["Ctrl+P:\nque argumentos espera\ny en que orden"]
    e["Ctrl+Q:\nficha completa:\nque devuelve, que lanza"]
    a --> b --> c --> d --> e
    style a fill:#c8e6c9,color:#000000,stroke:#2e7d32
    style b fill:#2e7d32,color:#ffffff,stroke:#1b5e20
    style c fill:#c8e6c9,color:#000000,stroke:#2e7d32
    style d fill:#2e7d32,color:#ffffff,stroke:#1b5e20
    style e fill:#2e7d32,color:#ffffff,stroke:#1b5e20

De tecla a tecla, el circuito entero toma menos tiempo que leer su descripción. Y conviene repetirlo: usar el autocompletado no es vagancia ni trampa — es la forma natural de programar en un lenguaje con un API de miles de clases, exactamente igual que escribir con corrector activado no es trampa al escribir. El diccionario del lenguaje lo aporta el IDE; el criterio de qué pedir y a quién, lo aportas tú. Eso sí: cuando aceptes una sugerencia, acostúmbrate a echarle un ojo a su descripción de una línea (o a un Ctrl+Q veloz si el nombre no lo deja claro). Aceptar a ciegas sí sería trampa, y se paga con bugs.

¿Y si uso macOS?

Los atajos de la tabla son los de Windows/Linux, que son los que usamos en el aula. En macOS, IntelliJ traduce casi todos sustituyendo Ctrl por Cmd — la doc rápida y el autocompletado tienen además sus propias combinaciones locales. Si algún día dudas de cómo se llama un atajo en tu teclado, usa Help → Find Action (doble Shift en versiones recientes) y teclea el nombre de la acción: IntelliJ te muestra su atajo exacto y además la ejecuta si pulsas Enter. Esa paleta de acciones es, por sí sola, el atajo maestro que desbloquea todos los demás.

2.6 Práctica guiada: tu primer descubrimiento por la documentación

Cerremos la sección poniendo a trabajar todo lo aprendido en un programa de verdad. Te propongo construir el generador de apodos de guerrero: un programita que combina al azar un sustantivo y un adjetivo — «Lobo Nocturno», «Halcón Feroz» — y que además servirá de banco de pruebas para tu nuevo circuito de descubrimiento. El código base te lo doy entero; las mejoras las descubrirás tú, con la Javadoc y el autocompletado como única brújula. Esa es la gracia.

Escribe este programa en IntelliJ — en un fichero nuevo, Apodos.java, dentro del proyecto que usas desde la UD1 — y ejecútalo un par de veces:

import java.util.Random;

void main() {
    // Dos estanterias de vocabulario: arrays de String, como los de la UD2
    String[] sustantivos = { "Halcon", "Lobo", "Buho", "Gato", "Pantera" };
    String[] adjetivos  = { "Veloz", "Intrepido", "Nocturno", "Sereno", "Feroz" };

    // Un objeto Random nace con su constructor sin argumentos
    // (semilla impredecible: cada ejecucion, apodos distintos)
    Random generador = new Random();

    // nextInt(n) devuelve un valor entre 0 (incluido) y n (excluido):
    // exactamente los indices validos de un array de longitud n
    String apodo = sustantivos[generador.nextInt(sustantivos.length)]
                 + " "
                 + adjetivos[generador.nextInt(adjetivos.length)];

    IO.println("Tu apodo de guerrero: " + apodo);
    IO.println("Letras del apodo: " + apodo.length());
}

Antes de las misiones, pausa para leer el programa con las gafas nuevas, porque casi cada línea exhibe algo de hoy: el import de la sección 2.1 (opcional pero declarado, como debe ser); el constructor elegido en el new; el método de instancia nextInt recibiendo un argumento; el + de concatenación de la UD1; la regla de la ficha de nextInt funcionando como índice de array de la UD2; y el IO.println —estático— con su parámetro de tipo Object. Un programa de catorce líneas que resume la sección entera.

Ahora, las misiones. Hazlas en orden y sin saltarte la parte incómoda: intenta encontrar la respuesta antes de abrir la solución plegable.

Misión 1 — Leer la ficha de nextInt dentro del editor. Coloca el cursor sobre nextInt y pulsa Ctrl+Q. Localiza en la ficha la frase que habla del límite superior exclusivo. Con ella delante, responde: ¿por qué es garantizado que sustantivos[generador.nextInt(sustantivos.length)] nunca lanza ArrayIndexOutOfBoundsException?

Solución a la misión 1

La ficha dice que nextInt(n) devuelve valores entre 0 (incluido) y n (excluido). Los índices válidos de un array de longitud n son exactamente 0, 1, ... n-1. El rango de la ficha y el rango de índices coinciden al milímetro: el número devuelto siempre cae dentro de la zona segura del array. Esta combinación —referencia.nextInt(array.length)— es un patrón que usarás constantemente, y ahora sabes demostrar con la documentación por qué es segura, no solo que funciona. Es la misma ficha que explicó el + 1 del dado de la sección 1.

Misión 2 — Descubrir un método nuevo en la Javadoc web. Abre la Javadoc de String (caja de búsqueda, String, Enter) y baja a su Method Summary. Tu objetivo: encontrar un método que repita la cadena varias veces. Cuando lo encuentres, úsalo para rodear el apodo con una línea decorativa de 40 signos de igual.

Solución a la misión 2

En el Method Summary de String busca repeat: su ficha dice que devuelve una cadena cuyo valor es la concatenación de la original repetida el número de veces indicado, y luce un Since: 11. La línea decorativa queda así:

IO.println("=".repeat(40));   // 40 signos = seguidos: una linea decorativa

Colócala antes y después de la impresión del apodo y tendrás un cartel digno. Dos observaciones de casa: repeat es un método de instancia (la respuesta depende de ESTA cadena: la que repite es la que recibe el mensaje) — fíjate en que la regla mental de la sección 2.3 lo clasifica sin abrir la ficha; y es un adelanto de la sección 3, que estudiará String a fondo. Hoy lo has encontrado tú solo: ese es el método de trabajo.

Misión 3 — El experimento del receptor. En el programa, escribe generador + punto y observa la lista del autocompletado; ciérrala (Escape) y escribe Random + punto. Compara ambas listas y razona la diferencia. Repite ahora con Math + punto.

Solución a la misión 3

Tras generador. aparece el repertorio de instancia de Random: nextInt, nextBoolean, nextDouble, nextLong... — servicios que trabajan sobre el estado interno de un generador concreto. Tras Random. la lista es ridícula: la clase apenas ofrece miembros estáticos — casi todo lo suyo son métodos de instancia. Y tras Math. ocurre lo contrario: solo estáticos (abs, max, min, sqrt, PI, E...), porque Math es una clase de utilidad sin estado. Tres receptores, tres listas distintas, cero documentación consultada: el autocompletado es el mapa interactivo de los dos mundos.

Misión 4 — Elegir nacimiento: la semilla. Cambia el constructor a new Random(42) y ejecuta el programa dos veces completas. Anota los apodos de cada ejecución. Vuelve luego a new Random().

Solución a la misión 4

Con new Random(42), ambas ejecuciones generan exactamente los mismos apodos, y cambiar la semilla a cualquier otro número produce otra pareja fija. Acabas de verificar en tu propia pantalla lo que la tabla de constructores de la sección 2.4 prometía: el constructor con semilla fija el estado inicial del objeto, y ese estado condiciona toda su secuencia futura. La sección 5 explicará por qué eso es una herramienta profesional (reproducir un fallo en una prueba, repetir un sorteo de demostración) y no un truco de salón.

Misión 5 — Personalizar con la sobrecarga de readln. Haz que el programa pregunte el nombre del usuario antes de generar el apodo, usando la versión de readln que muestra un mensaje guía, y saluda con formato «nombre, tu apodo de guerrero es...».

Solución a la misión 5

La clave de la misión es que elijas la versión sobrecargada correcta sin mirar — y si has dudado, Ctrl+P dentro de los paréntesis te la recuerda al instante. El programa completo, con la personalización integrada, queda así:

import java.util.Random;

void main() {
    // Dos estanterias de vocabulario: arrays de String, como los de la UD2
    String[] sustantivos = { "Halcon", "Lobo", "Buho", "Gato", "Pantera" };
    String[] adjetivos  = { "Veloz", "Intrepido", "Nocturno", "Sereno", "Feroz" };

    // readln CON argumento: la sobrecarga que muestra un mensaje guia antes de leer
    String nombre = IO.readln("Como te llamas? ");

    Random generador = new Random();

    String apodo = sustantivos[generador.nextInt(sustantivos.length)]
                 + " "
                 + adjetivos[generador.nextInt(adjetivos.length)];

    IO.println(nombre + ", tu apodo de guerrero es: " + apodo);
}

IO.readln(String prompt) es la sobrecarga con mensaje guía que la tabla de la sección 2.4 documentaba. Fíjate en la elegancia del asunto: el nombre del método no cambia, la llamada no se complica, y el compilador selecciona la versión correcta solo por el hecho de que tú escribiste un argumento entre los paréntesis.

Si has completado las cinco misiones, para y valora lo que acaba de ocurrir: has aprendido dos cosas que no estaban escritas en esta sección — el comportamiento exacto de repeat y el patrón semilla-reproducibilidad — sin que nadie te las explicara. Las sacaste de la documentación oficial y del propio IDE. Esa autonomía es, exactamente, el objetivo de la sección: a partir de la siguiente, cada clase nueva que conozcas —String, Math, Random a fondo, java.time, StringBuilder— podrás anticiparla tú mismo, y las clases del curso serán confirmaciones más que revelaciones.

2.7 Resumen, glosario y checklist

Si tuvieras que quedarte con diez ideas de esta sección, que fueran estas:

  1. El API de Java son miles de clases listas para usar, organizadas en paquetes (el apellido de cada clase) que cuelgan de módulos; java.base es el módulo núcleo y contiene los cuatro paquetes estrella de la unidad: java.lang, java.util, java.math y java.time.
  2. Math vive en java.lang, no en java.math: ese paquete es el de BigInteger y BigDecimal. Trampa clásica de examen.
  3. El import es un apodo para el compilador, no una carga de código: import java.util.Random; te deja escribir Random a secas. java.lang viene preimportado en todo programa Java; y en los ficheros compactos de JEP 512, además, todo java.base viene preimportado de serie.
  4. Nadie memoriza el API: se consulta la Javadoc oficial (https://docs.oracle.com/en/java/javase/25/docs/api/), cuya página de clase se lee de arriba abajo: declaración, descripción, Constructor Summary, Method Summary y el Detail de cada miembro (Parameters, Returns, Throws, Since).
  5. Since es la versión de Java en la que nació el miembro; Deprecated avisa de piezas en desuso y suele proponer el sustituto. La doc describe el qué, nunca el cómo: es el contrato de la abstracción.
  6. La pregunta clave ante cualquier llamada: ¿quién es el receptor? Referencia → miembros de instancia; nombre de clase → miembros estáticos.
  7. Instancia: el método usa el estado de ese objeto concreto (length(), nextInt()). Estático: pertenece al colectivo y depende solo de sus argumentos (parseInt, sqrt, IO.println). Regla mental: si la respuesta cambiaría según el ejemplar, es de instancia.
  8. System.out.println es una cadena de dos puntos: clase → campo estático (out, que guarda un PrintStream) → método de instancia (println). Los campos estáticos también existen: Math.PI, Integer.MAX_VALUE, una única copia compartida.
  9. Los paréntesis de new eligen constructor; varias versiones con el mismo nombre y distintos parámetros es sobrecarga (Random() / Random(42), IO.println × 2, IO.readln × 2). Elegir constructor es elegir el nacimiento del objeto.
  10. El circuito de descubrimiento en IntelliJ: receptor + punto → Ctrl+Space → elegir → Ctrl+P (argumentos) → Ctrl+Q (ficha). Programar «con el manual abierto» es la norma profesional, no la excepción.

Glosario de la sección

Término Definición en una línea
API Biblioteca de clases listas para usar que la plataforma ofrece al programador
Paquete Agrupación con nombre de clases relacionadas: el «apellido» de una clase
Módulo Cajón que agrupa paquetes; java.base es el núcleo esencial de la plataforma
Nombre cualificado Nombre completo de una clase con su paquete: java.util.Random
import Declaración que apoda una clase para poder escribir su nombre simple
Paquete sin nombre La zona sin apellido donde viven los programas pequeños (los nuestros, por ahora)
Javadoc La documentación oficial, uniforme y generada desde los comentarios /** */ del fuente
Since Anotación Javadoc: versión de Java en la que se incorporó el miembro
Deprecated Anotación Javadoc: miembro en desuso, con sustituto recomendado
Receptor Lo que está a la izquierda del punto: el objeto o la clase que recibe la llamada
Miembro de instancia Miembro que se invoca sobre un objeto concreto y usa su estado
Miembro estático Miembro que se invoca sobre la clase, sin objeto: pertenece al colectivo
Clase de utilidad Clase con repertorio cien por cien estático y sin objetos (Math, Arrays, IO)
Campo estático Dato de la clase con una única copia compartida (Math.PI)
Constante de clase Campo estático de valor fijo, escrito en MAYUSCULAS_CON_BARRAS_BAJAS
Constructor Código de inicialización que new ejecuta sobre el objeto recién creado
Sobrecarga Varias versiones de un mismo nombre con distinta lista de parámetros
Fábrica estática Método estático cuyo trabajo es crear y devolver objetos (LocalDate.now())
Autocompletado Asistente del IDE que lista los miembros aplicables al receptor (Ctrl+Space)

Checklist: al terminar, debes ser capaz de...

  • [ ] Explicar cómo se organiza el API en módulos, paquetes y clases, y poner un ejemplo de cada nivel (CE 2.7).
  • [ ] Decir en qué paquete vive Math — y por qué no es java.math — sin mirar ningún apunte (CE 2.7).
  • [ ] Importar una clase de java.util y explicar qué hace exactamente el import, y por qué IO nunca lo necesitó (CE 2.7, CE 2.5).
  • [ ] Localizar un método en la Javadoc web y extraer sus parámetros, su valor de retorno y sus excepciones (CE 2.7, CE 2.9).
  • [ ] Aplicar la regla mental para clasificar un miembro del API como de instancia o estático, y justificar la clasificación (CE 2.4, CE 2.5).
  • [ ] Predecir qué lista mostrará el autocompletado tras una referencia y tras un nombre de clase, antes de probarlo (CE 2.9).
  • [ ] Diseccionar System.out.println nombrando clase, campo estático y método de instancia (CE 2.1, CE 2.4).
  • [ ] Elegir entre dos constructores de una clase leyendo sus descripciones en la Javadoc (CE 2.8, CE 2.3).
  • [ ] Ejecutar el circuito Ctrl+Space → Ctrl+P → Ctrl+Q sin ayuda y describir qué aporta cada paso (CE 2.9).

2.8 Autoevaluación de la sección

¿Esto no era la sección 11?

Igual que en la sección anterior: el test oficial de repaso de la unidad vive en la sección 11, dividido por secciones para el repaso de conjunto. Este bloque es una autoevaluación rápida para hacer justo después de leer, sin material y sin presión: si fallas algo, vuelve a la subsección correspondiente antes de continuar. El orden de las preguntas sigue el orden de la sección.

#

Pregunta 1

¿En qué paquete vive la clase Math?

#

Pregunta 2

¿Por qué IO, String o Integer nunca han necesitado una línea import en tus programas?

#

Pregunta 3

¿Qué hace exactamente la línea import java.util.Random; en un programa?

#

Pregunta 4

La Javadoc de nextInt(int bound) dice que devuelve un valor «between 0 (inclusive) and the specified bound (exclusive)». ¿Qué puede devolver entonces nextInt(10)?

#

Pregunta 5

¿Cuál de estas afirmaciones sobre Integer.parseInt("42") es la correcta?

#

Pregunta 6

En la llamada System.out.println("hola"), ¿qué es exactamente out?

#

Pregunta 7

¿Qué diferencia real hay entre new Random() y new Random(42)?

#

Pregunta 8

¿Por qué String.length() no compila?

#

Pregunta 9

IO.readln() e IO.readln("Escribe tu nombre: ") comparten nombre pero reciben distinto número de parámetros. ¿Cómo se llama esta característica del lenguaje?

#

Pregunta 10

Estás escribiendo un programa y no recuerdas qué ofrece la clase Math. ¿Cuál es la vía MÁS rápida para verlo sin salir de IntelliJ?

Cuando las diez preguntas te salgan sin dudar, tienes el equipamiento completo para el resto de la unidad: sabes dónde viven las clases, cómo leer sus fichas y cómo preguntarles a la clase o al objeto según corresponda. Estás listo para la sección 3 — La clase String en profundidad, donde por fin abriremos de par en par la clase que más has usado en todo el curso: su inmutabilidad, el famoso pool de cadenas que asoma desde la UD1, y el repertorio completo de métodos de búsqueda, transformación y comparación.