Saltar a contenido

9. Entrada de información: java.lang.IO

UD1 — Fundamentos de Programación

IES Thiar · Curso 2026 - 2027

Aspecto Valor
Resultado de aprendizaje RA1 — Reconoce la estructura de un programa informático, identificando y relacionando los elementos propios del lenguaje de programación utilizado.
Criterios de evaluación cubiertos CE 1.5 — Se ha modificado el código de un programa para crear y utilizar variables (en este caso, leyéndolas desde la entrada estándar).
Marco normativo RD 405/2023 · RD 450/2010 · RD 659/2023
Tecnología base Java 25 LTS — java.lang.IO (JEP 512), java.util.Scanner (Java 5+).

Introducción de la sección

Hasta ahora tus programas solo producían salida: System.out.println mostraba texto en la consola. Pero un programa interesante necesita recibir información del usuario: un nombre, una edad, dos números para sumarlos... En esta sección vas a aprender a leer lo que el usuario escribe por teclado usando la nueva clase java.lang.IO de Java 25, que simplifica muchísimo la entrada/salida. Verás también una breve introducción a Scanner, una clase más clásica que sigue siendo útil y que encontrarás en cualquier código heredado.

Suponemos que ya dominas la conversión de tipos (Sección 8: Integer.parseInt, Double.parseDouble, Boolean.parseBoolean), porque la entrada por teclado siempre devuelve texto y muchas veces necesitas convertirlo a número. La gestión de errores (qué pasa si el usuario escribe "abc" cuando esperas un número) requiere try-catch, que se ve en UD3 Sección 8; por ahora nos quedamos en el "camino feliz" y verás dos técnicas sencillas para ir tirando sin excepciones.


9.1 La clase java.lang.IO (JEP 512, Java 25)

Qué es y de dónde viene

java.lang.IO es una clase nueva introducida en Java 25 como parte de la JEP 512 (Compact Source Files and Instance Main Methods). Su objetivo es simplificar la entrada/salida estándar para que no tengas que escribir System.out.println(...) ni instanciar un Scanner a mano para leer una línea.

1
2
3
4
5
6
7
8
9
// Java clásico (pre-Java 25)
System.out.println("Hola");
java.util.Scanner sc = new java.util.Scanner(System.in);
String nombre = sc.nextLine();
sc.close();

// Java 25 con java.lang.IO
IO.println("Hola");
String nombre = IO.readln("¿Cómo te llamas? ");

IO es una clase con métodos estáticos: no necesitas crear un objeto, llamas directamente a IO.algo(). Al estar en el paquete java.lang (como String, System o Math), no requiere import: la usas directamente.

Métodos principales

Método Qué hace Devuelve
IO.println() Imprime una línea vacía void
IO.println(Object x) Imprime x y salta de línea void
IO.print(Object x) Imprime x sin saltar de línea void
IO.readln() Lee una línea del teclado String
IO.readln(String prompt) Imprime prompt y lee una línea String

Las dos sobrecargas de readln (con y sin prompt) son las que usarás siempre. La versión con prompt es muy cómoda: imprime el mensaje y lee en una sola llamada.

Por qué readln siempre devuelve String

El teclado siempre produce texto. Aunque el usuario pulse 2 3 Enter, lo que Java recibe es la cadena "23", no el número 23. Si necesitas un int, convierte el String con Integer.parseInt(...) (Sección 8). En la subsección 9.3 verás este patrón en detalle.


9.2 Leer texto: nombre, ciudad, etc.

Lo más sencillo es leer un String y usarlo tal cual:

1
2
3
4
5
6
public class Saludar {
    public static void main(String[] args) {
        String nombre = IO.readln("¿Cómo te llamas? ");
        IO.println("Hola, " + nombre + ", encantado.");
    }
}

Ejecución:

¿Cómo te llamas? Ana
Hola, Ana, encantado.

Fíjate en el detalle: el prompt (el texto "¿Cómo te llamas? ") lo pasas como argumento a readln, no con print aparte. Es un atajo que ahorra una línea y queda muy legible.

Con formatted y text blocks

Si quieres saludar con un formato más vistoso, combinas readln con lo aprendido en la Sección 5:

public class FichaAlumno {
    public static void main(String[] args) {
        String nombre = IO.readln("Nombre: ");
        int edad = Integer.parseInt(IO.readln("Edad: "));

        String ficha = """
                +------------------------+
                |  Nombre:  %s
                |  Edad:    %d años
                +------------------------+
                """.formatted(nombre, edad);

        IO.println(ficha);
    }
}

9.3 Leer números: texto + conversión

El patrón básico para leer un número es:

  1. Leer el texto con IO.readln(...) (devuelve String).
  2. Convertir el String al tipo numérico con Integer.parseInt, Double.parseDouble, etc. (Sección 8).
public class Sumar {
    public static void main(String[] args) {
        String s1 = IO.readln("Primer número: ");
        String s2 = IO.readln("Segundo número: ");

        int n1 = Integer.parseInt(s1);
        int n2 = Integer.parseInt(s2);

        IO.println("La suma es " + (n1 + n2));
    }
}

Ejecución:

1
2
3
Primer número: 14
Segundo número: 28
La suma es 42

La trampa del formato estricto

Integer.parseInt es estricto: solo admite dígitos (y un signo + o - opcional al inicio). Cualquier otra cosa —espacios, comas, letras— hace que el programa termine con NumberFormatException.

1
2
3
4
5
Integer.parseInt("42");      // 42  ✓
Integer.parseInt("  42  ");   // CRASH: tiene espacios
Integer.parseInt("3.14");     // CRASH: no es entero
Integer.parseInt("abc");      // CRASH: no son dígitos
Integer.parseInt("1.000");    // CRASH: el punto no vale

La trampa del separador decimal en double

Para double el asunto es más delicado porque en España el separador decimal es la coma, pero Java espera el punto:

1
2
3
Double.parseDouble("3.14");   // 3.14 ✓
Double.parseDouble("3,14");   // CRASH: coma, no punto
Double.parseDouble("3,14".replace(",", "."));  // 3.14 ✓ (workaround)

En la Sección 8 ya vimos que se puede forzar el locale con String.format(Locale.US, ...), pero para entrada lo más sencillo es reemplazar la coma por punto antes de parsear. Lo verás en el ejemplo integrador (9.6).

Por qué no lo gestionamos aquí

Si el usuario escribe "abc" cuando esperas un número, el programa termina con un error. La forma profesional de gestionarlo —mostrar un mensaje y volver a pedir el dato— requiere try-catch, que se ve en UD3 Sección 8. Por ahora asumimos el "camino feliz" (el usuario escribe lo que le pides). En la subsección 9.5 verás dos técnicas de validación sin excepciones para ir tirando hasta entonces.

Boolean.parseBoolean: el que no lanza nunca

1
2
3
4
String resp = IO.readln("¿Estás seguro (true/false)? ");
boolean seguro = Boolean.parseBoolean(resp);
// parseBoolean devuelve true solo si la cadena es "true" (ignorando mayúsculas)
// cualquier otra cosa devuelve false —no lanza excepción nunca.

Es el único parseXxx que no revienta nunca: devuelve false para cualquier cosa que no sea "true" (case-insensitive). Útil, pero ten cuidado: eso significa que "no", "sí" y " abc " todos se interpretan como false. Para pedir confirmaciones tipo S/N necesitas comparar el String a mano con .equalsIgnoreCase("s").


9.4 Scanner: la alternativa clásica (introducción breve)

Antes de Java 25, la forma estándar de leer entrada era la clase java.util.Scanner, introducida en Java 5. La verás en cualquier código heredado, en tutoriales antiguos y en la mayoría de ejemplos de Internet, así que conviene que la reconozcas y sepas usarla a nivel básico.

Uso básico

import java.util.Scanner;

public class SumarConScanner {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        System.out.print("Primer número: ");
        int n1 = sc.nextInt();

        System.out.print("Segundo número: ");
        int n2 = sc.nextInt();

        System.out.println("La suma es " + (n1 + n2));
    }
}

Diferencia clave con IO.readln + parseInt

Operación Con java.lang.IO Con Scanner
Leer un entero int n = Integer.parseInt(IO.readln("n: ")); int n = sc.nextInt();
Leer un double double d = Double.parseDouble(IO.readln("d: ")); double d = sc.nextDouble();
Leer una línea String s = IO.readln("s: "); String s = sc.nextLine();
¿Crea objetos? No, son métodos estáticos Sí, hay que instanciar Scanner
¿Depende del locale? No (trabajas con String) (afecta al separador decimal)
¿Hay que cerrarlo? No En principio, sc.close()

La gran ventaja de Scanner es que combina lectura y conversión en un solo método (nextInt lee y convierte). La gran ventaja de IO es que es más simple y no tiene efectos de locale: tú haces la conversión, así que controlas el formato.

Trampa 1: el locale en Scanner

Scanner usa el locale del sistema para interpretar números. En un equipo configurado en español, sc.nextDouble() espera la coma como separador decimal:

1
2
3
4
Scanner sc = new Scanner(System.in);
double d = sc.nextDouble();
// Si el usuario escribe "3.14" en un sistema español → CRASH: InputMismatchException
// Si el usuario escribe "3,14" → 3.14

Esto es fuente de confusión para principiantes (escribes el punto como has visto en tus apuntes y no funciona). Si quieres que Scanner entienda siempre el punto, fuerza el locale:

1
2
3
Scanner sc = new Scanner(System.in).useLocale(java.util.Locale.US);
double d = sc.nextDouble();
// "3.14" → 3.14 siempre, en cualquier país

Trampa 2: nextInt deja el Enter en el buffer

Cuando mezclas nextInt() (que lee un número pero deja el salto de línea sin consumir) con nextLine() (que lee hasta el próximo salto), obtienes un comportamiento sorprendente:

1
2
3
4
5
6
Scanner sc = new Scanner(System.in);
System.out.print("Edad: ");
int edad = sc.nextInt();          // lee "23" y deja \n en el buffer
System.out.print("Nombre: ");
String nombre = sc.nextLine();    // ¡Lee la línea vacía! (consume el \n que quedó)
System.out.println("Hola, " + nombre);  // imprime "Hola, "

Solución: tras un nextInt() (o nextDouble()), llama a un nextLine() "basura" para consumir el salto antes de leer texto:

1
2
3
int edad = sc.nextInt();
sc.nextLine();                   // consume el \n sobrante
String nombre = sc.nextLine();    // ahora sí lee el nombre

Con IO.readln este problema no existe porque siempre lee líneas completas.

Cuál usar

En este curso usaremos IO.readln + parseInt como estilo preferido (más limpio, sin sorpresas de locale ni buffer). Pero conviene que sepas leer Scanner porque lo verás en código de compañeros, en Stack Overflow y en la mayoría de tutoriales Java que existen en Internet. A partir de UD3, cuando veamos try-catch, verás también el patrón try (Scanner sc = new Scanner(System.in)) { ... } para cerrarlo automáticamente.


9.5 ¿Y si el usuario escribe mal? (sin try-catch)

Si el usuario teclea "abc" cuando le pides un número, Integer.parseInt("abc") lanza NumberFormatException y el programa termina. La gestión profesional de este error requiere try-catch, que se ve en UD3 Sección 8.

9.6 Ejemplo integrador: calculadora de propina

public class CalculadoraPropina {

    public static void main(String[] args) {

        IO.println("=== Calculadora de propina ===");

        String sImporte = IO.readln("Importe de la cuenta (€): ");
        String sPorcentaje = IO.readln("Porcentaje de propina (%): ");

        // Reemplazamos la coma decimal por punto por si el usuario escribe "48,50"
        double importe = Double.parseDouble(sImporte.replace(",", "."));
        double porcentaje = Double.parseDouble(sPorcentaje.replace(",", "."));

        double propina = importe * porcentaje / 100;
        double total = importe + propina;

        IO.println("""
                -----------------------------
                Importe:    %.2f €
                Propina:    %.2f €  (%.1f%%)
                Total:      %.2f €
                -----------------------------
                """.formatted(importe, propina, porcentaje, total));
    }
}

Ejecución:

1
2
3
4
5
6
7
8
=== Calculadora de propina ===
Importe de la cuenta (€): 48,50
Porcentaje de propina (%): 10
-----------------------------
Importe:    48,50 €
Propina:    4,85 €  (10,0%)
Total:      53,35 €
-----------------------------

Fíjate en .replace(",", "."): como el usuario español escribirá la coma decimal pero Double.parseDouble espera el punto, este truco convierte la entrada antes de parsearla. Sencillo y suficiente para UD1.


Errores comunes

Error Síntoma Causa Solución
Asignar readln a un int "int n = IO.readln(...)" no compila readln devuelve String, no int Convierte: Integer.parseInt(IO.readln(...))
Separador decimal equivocado Double.parseDouble("3,14") revienta Java espera punto .replace(",", ".") antes de parsear
Mezclar Scanner.nextInt con nextLine El nextLine se salta El Enter del nextInt queda en el buffer sc.nextLine() "basura" después de cada nextInt
Scanner con locale español nextDouble() falla con 3.14 Espera coma decimal .useLocale(Locale.US)
No gestionar entrada inválida Programa revienta con NumberFormatException No hay validación Técnicas de 9.6 o, mejor, try-catch en UD3

Ejercicios propuestos

Ejercicio 9.1 (saludar). Escribe un programa que pida al usuario su nombre y su ciudad y muestre por pantalla "Hola, [nombre] de [ciudad]".

Ejercicio 9.2 (área rectángulo). Pide la base y la altura de un rectángulo (como double), calcula el área y muéstrala con dos decimales usando formatted. Recuerda el .replace(",", ".") si el usuario escribe con coma.

Ejercicio 9.3 (conversor de temperatura). Pide una temperatura en grados Celsius y muéstrala en Fahrenheit (F = C × 9/5 + 32). Maneja la coma decimal del input.

Ejercicio 9.4 (media de tres notas). Pide tres notas (enteros de 0 a 10), calcula la media y muestra "Aprobado" si es ≥ 5 o "Suspenso" si no, usando el operador ternario. Sin validar el rango (camino feliz).

Ejercicio 9.5 (con Scanner). Reescribe el ejercicio 9.2 usando Scanner.nextDouble() con useLocale(Locale.US). Compara cuál te resulta más cómodo.


Resumen de la sección 9

  1. java.lang.IO (JEP 512, Java 25) es la forma moderna de leer y escribir por consola: IO.println, IO.print, IO.readln(), IO.readln(prompt). Al estar en java.lang, no requiere import.
  2. IO.readln siempre devuelve String. Para obtener un número, convierte con Integer.parseInt o Double.parseDouble (Sección 8).
  3. Integer.parseInt es estricto: solo admite dígitos (y signo). Cualquier otra cosa lanza NumberFormatException.
  4. Double.parseDouble espera el punto como separador decimal. Si el usuario escribe coma, conviértela antes: s.replace(",", ".").
  5. Scanner es la alternativa clásica: nextInt(), nextDouble(), nextLine(). Combina lectura y conversión pero tiene trampas de locale y de buffer.
  6. Si mezclas nextInt con nextLine en Scanner, llama a un nextLine() "basura" después del número para consumir el salto.

Lo que verás en UD3 Sección 8. try-catch-finally, try-with-resources, excepciones propias. Aprenderás a pedir un dato al usuario y, si lo escribe mal, mostrar un mensaje y volver a pedirlo sin que el programa reviente. Es el patrón estándar para entrada robusta en cualquier aplicación.