Saltar al contenido
entrada-por-teclado.java · devschool

Leer del teclado con Scanner

Lección 4 de 26 · 9 min de lectura · Actualizado el

En esta lección
  1. Importar y crear un Scanner
  2. Los métodos de lectura
  3. La trampa del salto de línea tras nextInt()
  4. Validar la entrada con hasNextInt()
  5. Cerrar el Scanner
  6. Programa completo: una calculadora
  7. Errores frecuentes
  8. Resumen

Hasta ahora tus programas trabajaban siempre con los mismos datos, escritos en el código. Para que sean útiles de verdad tienen que preguntar al usuario: su nombre, una cantidad, una opción de un menú. En Java la forma más sencilla de leer lo que se escribe en la consola es la clase Scanner. En esta lección aprenderás a usarla, a evitar su trampa más famosa y a validar lo que escribe el usuario.

Importar y crear un Scanner

Scanner no está disponible directamente: vive en el paquete java.util y hay que importarla al principio del archivo, antes de la clase:

import java.util.Scanner;

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

        System.out.print("¿Cómo te llamas? ");
        String nombre = teclado.nextLine();

        System.out.println("Hola, " + nombre + ". Bienvenido a DevSchool.");
    }
}
¿Cómo te llamas? Lucía Pérez
Hola, Lucía Pérez. Bienvenido a DevSchool.

Vamos por partes:

  • import java.util.Scanner; le dice al compilador dónde está la clase. Sin esta línea obtienes cannot find symbol: class Scanner. Los paquetes y los import se explican a fondo en paquetes.
  • new Scanner(System.in) crea un objeto que lee de System.in, la entrada estándar (el teclado). Es la pareja de System.out, que ya usas para escribir.
  • teclado es solo el nombre de la variable; también verás sc, entrada o in.
  • Usamos print (sin ln) para la pregunta, así el cursor se queda en la misma línea y el usuario escribe al lado.

Cuando el programa llega a nextLine(), se detiene y espera a que el usuario escriba algo y pulse Intro.

Los métodos de lectura

Scanner tiene un método para cada tipo de dato:

MétodoLeeEjemplo de entrada
nextInt()Un int25
nextLong()Un long8100000000
nextDouble()Un double19,99 o 19.99 (según idioma)
nextBoolean()Un booleantrue
next()Una palabra (hasta el primer espacio)Lucía
nextLine()Una línea entera (hasta el Intro)Lucía Pérez

Un ejemplo con números. El programa calcula el precio con IVA de un producto:

import java.util.Scanner;

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

        System.out.print("Unidades: ");
        int unidades = teclado.nextInt();

        System.out.print("Precio por unidad: ");
        double precio = teclado.nextDouble();

        double total = unidades * precio * 1.21;
        System.out.printf("Total con IVA: %.2f euros%n", total);
    }
}
Unidades: 3
Precio por unidad: 12,5
Total con IVA: 45,38 euros

printf con %.2f muestra el número con dos decimales; lo tienes explicado en String y StringBuilder.

El separador decimal

nextDouble() y printf usan el formato de números del idioma del sistema. En un ordenador configurado en español esperan y muestran coma (12,5), y si escribes 12.5 salta una InputMismatchException. En un sistema en inglés es al revés. Para que el programa acepte siempre el punto, crea el Scanner así:

Scanner teclado = new Scanner(System.in).useLocale(java.util.Locale.US);

next() frente a nextLine()

next() lee hasta el primer espacio y nextLine() hasta el final de la línea:

import java.util.Scanner;

public class NextVsNextLine {
    public static void main(String[] args) {
        Scanner teclado = new Scanner(System.in);
        System.out.print("Nombre completo: ");
        String primera = teclado.next();
        String resto = teclado.nextLine();
        System.out.println("next(): [" + primera + "]");
        System.out.println("nextLine(): [" + resto + "]");
    }
}
Nombre completo: Ana García López
next(): [Ana]
nextLine(): [ García López]

next() se quedó con la primera palabra y nextLine() recogió lo que quedaba en esa línea, espacio inicial incluido. Para nombres, direcciones o frases, usa nextLine().

La trampa del salto de línea tras nextInt()

Es el error más famoso de Scanner, y le pasa a todo el mundo. Mira este programa:

import java.util.Scanner;

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

        System.out.print("Edad: ");
        int edad = teclado.nextInt();

        System.out.print("Nombre: ");
        String nombre = teclado.nextLine();

        System.out.println("Nombre: [" + nombre + "], edad: " + edad);
    }
}
Edad: 20
Nombre: Nombre: [], edad: 20

¡No deja escribir el nombre! ¿Qué ha pasado? Cuando escribes 20 y pulsas Intro, en la entrada queda 20\n. nextInt() se lleva el 20, pero deja el salto de línea \n sin consumir. Después, nextLine() lee “hasta el siguiente salto de línea”, lo encuentra inmediatamente y devuelve un texto vacío.

Hay dos soluciones habituales:

1. Consumir el salto de línea sobrante con un nextLine() extra:

import java.util.Scanner;

public class SolucionUno {
    public static void main(String[] args) {
        Scanner teclado = new Scanner(System.in);
        System.out.print("Edad: ");
        int edad = teclado.nextInt();
        teclado.nextLine(); // descarta el Intro pendiente

        System.out.print("Nombre: ");
        String nombre = teclado.nextLine();
        System.out.println("Nombre: [" + nombre + "], edad: " + edad);
    }
}
Edad: 20
Nombre: Marta Ruiz
Nombre: [Marta Ruiz], edad: 20

2. Leer siempre con nextLine() y convertir el texto a número:

import java.util.Scanner;

public class SolucionDos {
    public static void main(String[] args) {
        Scanner teclado = new Scanner(System.in);
        System.out.print("Edad: ");
        int edad = Integer.parseInt(teclado.nextLine().trim());

        System.out.print("Nombre: ");
        String nombre = teclado.nextLine();
        System.out.println("Nombre: [" + nombre + "], edad: " + edad);
    }
}

La segunda opción es la preferida en programas con muchas preguntas: todas las lecturas se comportan igual y nunca queda nada pendiente. Integer.parseInt se explica en Math y clases envoltorio.

Validar la entrada con hasNextInt()

Si el programa pide un número y el usuario escribe hola, nextInt() lanza InputMismatchException y el programa termina:

Exception in thread "main" java.util.InputMismatchException

Para evitarlo, Scanner tiene métodos que comprueban sin leer: hasNextInt(), hasNextDouble(), hasNextLine()… Devuelven true si lo siguiente que hay en la entrada es de ese tipo. Combinados con un bucle, permiten insistir hasta obtener un dato válido:

import java.util.Scanner;

public class PedirNota {
    public static void main(String[] args) {
        Scanner teclado = new Scanner(System.in);
        int nota = -1;

        while (nota < 0 || nota > 10) {
            System.out.print("Nota (0-10): ");
            if (teclado.hasNextInt()) {
                nota = teclado.nextInt();
                if (nota < 0 || nota > 10) {
                    System.out.println("Fuera de rango.");
                }
            } else {
                String basura = teclado.next(); // descarta lo que no es número
                System.out.println("'" + basura + "' no es un número entero.");
            }
        }
        System.out.println("Nota guardada: " + nota);
    }
}
Nota (0-10): siete
'siete' no es un número entero.
Nota (0-10): 12
Fuera de rango.
Nota (0-10): 7
Nota guardada: 7

Es importante descartar la entrada incorrecta con next(). Si no, hasNextInt() vería una y otra vez el mismo siete y el bucle sería infinito. Los bucles while se explican en bucles.

Consejo: otra forma de validar es leer con nextLine() y capturar NumberFormatException al convertir con Integer.parseInt. La verás en excepciones.

Cerrar el Scanner

Un Scanner usa un recurso del sistema, y los recursos se cierran cuando ya no hacen falta:

teclado.close();

Con System.in hay un matiz importante: cerrar el Scanner cierra también System.in, y ya no se puede volver a abrir. Si otro método crea después su propio Scanner sobre el teclado, obtendrá NoSuchElementException. Por eso:

  • Crea un solo Scanner para todo el programa (en main) y pásalo a los métodos que lo necesiten.
  • Ciérralo al final, cuando ya no vayas a leer más.

Muchos IDE muestran el aviso Resource leak: ‘teclado’ is never closed si no lo cierras. En programas pequeños de consola no es grave, pero acostúmbrate a cerrarlo. La forma más elegante es try-with-resources, que lo cierra solo y verás en excepciones.

Programa completo: una calculadora

Juntamos todo: menú, lectura de números, validación y switch (que verás en detalle en condicionales):

import java.util.Locale;
import java.util.Scanner;

public class Calculadora {
    public static void main(String[] args) {
        Scanner teclado = new Scanner(System.in).useLocale(Locale.US);

        System.out.print("Primer número: ");
        double a = leerNumero(teclado);
        System.out.print("Operación (+, -, *, /): ");
        String operacion = teclado.next();
        System.out.print("Segundo número: ");
        double b = leerNumero(teclado);

        String resultado = switch (operacion) {
            case "+" -> String.valueOf(a + b);
            case "-" -> String.valueOf(a - b);
            case "*" -> String.valueOf(a * b);
            case "/" -> b == 0 ? "no se puede dividir entre 0" : String.valueOf(a / b);
            default -> "operación desconocida";
        };

        System.out.println("Resultado: " + resultado);
        teclado.close();
    }

    static double leerNumero(Scanner teclado) {
        while (!teclado.hasNextDouble()) {
            System.out.print("Eso no es un número. Prueba otra vez: ");
            teclado.next();
        }
        return teclado.nextDouble();
    }
}
Primer número: 12.5
Operación (+, -, *, /): *
Segundo número: cuatro
Eso no es un número. Prueba otra vez: 4
Resultado: 50.0

Fíjate en que el Scanner se crea una vez en main y se pasa como parámetro a leerNumero, en lugar de crear uno nuevo cada vez.

Consejo: para probar un programa sin escribir a mano cada vez, puedes pasarle la entrada desde la terminal: printf "12.5\n*\n4\n" | java Calculadora.java.

Errores frecuentes

ProblemaCausaSolución
cannot find symbol: class ScannerFalta el importimport java.util.Scanner;
nextLine() devuelve texto vacíoSalto de línea que dejó nextInt()nextLine() extra o leer todo con nextLine()
InputMismatchExceptionEl usuario escribió algo que no es del tipo pedidoValidar con hasNextInt()
InputMismatchException con decimalesPunto frente a coma según el idiomauseLocale(Locale.US)
NoSuchElementExceptionSe cerró System.in o no hay más entradaUn solo Scanner, cerrado al final
Bucle infinito al validarNo se descarta la entrada incorrectateclado.next() en la rama de error

Resumen

  • Scanner lee del teclado: new Scanner(System.in), con import java.util.Scanner;.
  • nextInt(), nextDouble(), next() leen un dato; nextLine() lee la línea entera.
  • Tras nextInt() o nextDouble() queda un salto de línea pendiente: consúmelo o lee siempre con nextLine().
  • hasNextInt() y compañía comprueban sin leer; úsalos en un bucle para validar.
  • Crea un solo Scanner y ciérralo al final del programa.

Con datos del usuario ya puedes tomar decisiones: sigue con if, else y switch.

Pon a prueba lo que has aprendido

[Java] El usuario escribe 30, pulsa Intro, escribe Pedro y pulsa Intro. ¿Qué muestra el programa?
import java.util.Scanner;

Scanner sc = new Scanner(System.in);
int edad = sc.nextInt();
String nombre = sc.nextLine();
System.out.println("[" + nombre + "] " + edad);

[Java] El usuario escribe: Ana María Soto. ¿Qué guarda la variable nombre?
Scanner sc = new Scanner(System.in);
String nombre = sc.next();

[Java] ¿Para qué sirve hasNextInt()?

[Java] Un programa crea un Scanner sobre System.in en un método, lo cierra con close() y, más tarde, otro método crea un Scanner nuevo sobre System.in y llama a nextLine(). ¿Qué pasa?

¿Te ha quedado claro? Márcala y verás tu progreso en el explorador.