Leer del teclado con Scanner
En esta lección
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 obtienescannot find symbol: class Scanner. Los paquetes y losimportse explican a fondo en paquetes.new Scanner(System.in)crea un objeto que lee deSystem.in, la entrada estándar (el teclado). Es la pareja deSystem.out, que ya usas para escribir.tecladoes solo el nombre de la variable; también verássc,entradaoin.- Usamos
print(sinln) 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étodo | Lee | Ejemplo de entrada |
|---|---|---|
nextInt() | Un int | 25 |
nextLong() | Un long | 8100000000 |
nextDouble() | Un double | 19,99 o 19.99 (según idioma) |
nextBoolean() | Un boolean | true |
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 capturarNumberFormatExceptional convertir conInteger.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
Scannerpara todo el programa (enmain) 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
| Problema | Causa | Solución |
|---|---|---|
cannot find symbol: class Scanner | Falta el import | import java.util.Scanner; |
nextLine() devuelve texto vacío | Salto de línea que dejó nextInt() | nextLine() extra o leer todo con nextLine() |
InputMismatchException | El usuario escribió algo que no es del tipo pedido | Validar con hasNextInt() |
InputMismatchException con decimales | Punto frente a coma según el idioma | useLocale(Locale.US) |
NoSuchElementException | Se cerró System.in o no hay más entrada | Un solo Scanner, cerrado al final |
| Bucle infinito al validar | No se descarta la entrada incorrecta | teclado.next() en la rama de error |
Resumen
Scannerlee del teclado:new Scanner(System.in), conimport java.util.Scanner;.nextInt(),nextDouble(),next()leen un dato;nextLine()lee la línea entera.- Tras
nextInt()onextDouble()queda un salto de línea pendiente: consúmelo o lee siempre connextLine(). hasNextInt()y compañía comprueban sin leer; úsalos en un bucle para validar.- Crea un solo
Scannery 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
¿Te ha quedado claro? Márcala y verás tu progreso en el explorador.