Java

Tutorial Java: La clase Scanner

Java scanner: entrada de datos. Domina el uso de la clase Scanner en Java para la entrada de datos con ejemplos detallados.

Aprende Java y certifícate

Fundamentos y construcción de objetos Scanner

La clase Scanner sirve para procesar la entrada de datos desde diversas fuentes. Se encuentra en el paquete java.util y proporciona métodos para leer y analizar datos primitivos y cadenas de texto. Esta clase actúa como un puente entre las fuentes de datos y nuestro programa, permitiendo la lectura estructurada de información.

Para utilizar la clase Scanner en cualquier programa Java, primero se debe importar:

import java.util.Scanner;

La clase Scanner utiliza un concepto llamado tokenización, que consiste en dividir la entrada en unidades lógicas llamadas tokens, utilizando delimitadores. Por defecto, el delimitador es el espacio en blanco, pero se puede personalizar según las necesidades específicas del programa.

Constructores de Scanner

La versatilidad de Scanner se refleja en sus múltiples constructores, que permiten crear objetos a partir de diferentes fuentes de datos:

  • Entrada estándar (teclado):
Scanner teclado = new Scanner(System.in);
  • Cadenas de texto:
String texto = "Hola mundo 123";
Scanner lector = new Scanner(texto);
  • Archivos:
import java.io.File;
import java.io.FileNotFoundException;

try {
    File archivo = new File("datos.txt");
    Scanner lectorArchivo = new Scanner(archivo);
} catch (FileNotFoundException e) {
    System.err.println("No se encontró el archivo");
}
  • Streams personalizados:
import java.io.ByteArrayInputStream;

String datos = "10 20 30";
ByteArrayInputStream stream = new ByteArrayInputStream(datos.getBytes());
Scanner lectorStream = new Scanner(stream);

Configuración del delimitador

El comportamiento de tokenización se puede personalizar mediante la configuración de delimitadores. Por defecto, Scanner utiliza espacios en blanco, pero se puede modificar usando el método useDelimiter():

String csvData = "Juan,25,Madrid";
Scanner csvScanner = new Scanner(csvData);
csvScanner.useDelimiter(",");

// Ahora cada valor separado por coma será un token diferente
String nombre = csvScanner.next();  // "Juan"
int edad = csvScanner.nextInt();    // 25
String ciudad = csvScanner.next();  // "Madrid"

También se pueden utilizar expresiones regulares como delimitadores para casos más complejos:

Scanner scanner = new Scanner("datos: 15; valor: 23");
scanner.useDelimiter("[;:]\\s*");  // Delimita por ";" o ":" seguidos de espacios opcionales

String palabra1 = scanner.next();  // "datos"
int numero1 = scanner.nextInt();   // 15
String palabra2 = scanner.next();  // "valor"
int numero2 = scanner.nextInt();   // 23

Configuración de la localización

La interpretación de números y formatos puede variar según la región. Scanner permite configurar la localización para adaptarse a diferentes formatos regionales:

import java.util.Locale;

// Crear un Scanner con localización española
Scanner scannerES = new Scanner(System.in);
scannerES.useLocale(new Locale("es", "ES"));

// Crear un Scanner con localización estadounidense
Scanner scannerUS = new Scanner(System.in);
scannerUS.useLocale(Locale.US);

// Importante: en español se usa coma para decimales, en inglés punto
// 3,14 (español) vs 3.14 (inglés)

Verificación de disponibilidad de datos

Antes de leer datos, es recomendable verificar si existen tokens disponibles del tipo esperado para evitar excepciones:

Scanner scanner = new Scanner("texto 123 true");

// Verificar si hay un String disponible
if (scanner.hasNext()) {
    String texto = scanner.next();
    System.out.println("Texto: " + texto);
}

// Verificar si hay un entero disponible
if (scanner.hasNextInt()) {
    int numero = scanner.nextInt();
    System.out.println("Número: " + numero);
}

// Verificar si hay un booleano disponible
if (scanner.hasNextBoolean()) {
    boolean valor = scanner.nextBoolean();
    System.out.println("Booleano: " + valor);
}

Ejemplo práctico: Creación de un formulario simple

Veamos un ejemplo completo que integra varios conceptos para crear un formulario básico de entrada de datos:

import java.util.Scanner;

public class Formulario {
    public static void main(String[] args) {
        // Crear Scanner para leer desde el teclado
        Scanner entrada = new Scanner(System.in);
        
        // Solicitar y leer nombre (String)
        System.out.print("Introduce tu nombre: ");
        String nombre = entrada.nextLine();
        
        // Solicitar y leer edad (int)
        System.out.print("Introduce tu edad: ");
        int edad = 0;
        
        // Verificar que se ingrese un entero válido
        if (entrada.hasNextInt()) {
            edad = entrada.nextInt();
            entrada.nextLine(); // Limpiar el buffer
        } else {
            System.out.println("Edad no válida, se asignará 0");
            entrada.nextLine(); // Limpiar el buffer
        }
        
        // Solicitar y leer si tiene experiencia (boolean)
        System.out.print("¿Tienes experiencia previa? (true/false): ");
        boolean experiencia = false;
        
        if (entrada.hasNextBoolean()) {
            experiencia = entrada.nextBoolean();
        }
        
        // Mostrar resumen de datos
        System.out.println("\n--- Datos del formulario ---");
        System.out.println("Nombre: " + nombre);
        System.out.println("Edad: " + edad);
        System.out.println("Experiencia previa: " + experiencia);
        
        // Cerrar el Scanner
        entrada.close();
    }
}

Consideraciones sobre el ciclo de vida

Es importante gestionar adecuadamente el ciclo de vida de los objetos Scanner. Cuando ya no se necesita un Scanner, se debe cerrar para liberar los recursos asociados:

Scanner scanner = new Scanner(System.in);
// Operaciones con el scanner
scanner.close();

Sin embargo, hay que tener precaución al cerrar Scanner cuando se utiliza con System.in, ya que cerrar el Scanner también cerrará la entrada estándar, lo que podría afectar a otros Scanner que se creen posteriormente. En aplicaciones complejas, se suele crear un único Scanner para System.in que se mantiene durante toda la ejecución del programa.

Integración con buffers y streams

Scanner se integra perfectamente con el sistema de entrada/salida de Java, permitiendo la lectura de datos desde cualquier objeto que implemente la interfaz Readable o cualquier InputStream:

import java.io.BufferedReader;
import java.io.StringReader;

// Crear un BufferedReader y pasarlo a Scanner
BufferedReader buffer = new BufferedReader(new StringReader("línea 1\nlínea 2\nlínea 3"));
Scanner scannerBuffer = new Scanner(buffer);

// Leer línea por línea
while (scannerBuffer.hasNextLine()) {
    System.out.println(scannerBuffer.nextLine());
}
scannerBuffer.close();

Lectura de diferentes tipos de datos

La clase Scanner puede leer y convertir automáticamente diferentes tipos de datos desde una fuente de entrada. Esta versatilidad permite procesar información variada sin necesidad de realizar conversiones manuales, lo que simplifica considerablemente el código de entrada de datos en aplicaciones Java.

Métodos de lectura básicos

La clase Scanner proporciona métodos específicos para cada tipo de dato primitivo y para cadenas de texto. Cada método se encarga de leer el siguiente token disponible y convertirlo al tipo correspondiente:

import java.util.Scanner;

public class LecturaDatos {
    public static void main(String[] args) {
        String entrada = "42 3.14 true Hola";
        Scanner scanner = new Scanner(entrada);
        
        int entero = scanner.nextInt();         // Lee "42" como entero
        double decimal = scanner.nextDouble();  // Lee "3.14" como double
        boolean logico = scanner.nextBoolean(); // Lee "true" como boolean
        String texto = scanner.next();          // Lee "Hola" como String
        
        System.out.println("Entero: " + entero);
        System.out.println("Decimal: " + decimal);
        System.out.println("Lógico: " + logico);
        System.out.println("Texto: " + texto);
        
        scanner.close();
    }
}

Lectura de líneas completas

Cuando se necesita capturar una línea entera, incluyendo espacios, se utiliza el método nextLine():

Scanner scanner = new Scanner(System.in);

System.out.print("Introduce una frase: ");
String lineaCompleta = scanner.nextLine();
System.out.println("Has escrito: " + lineaCompleta);

nextLine() consume el salto de línea pendiente, lo que puede causar comportamientos inesperados cuando se combina con otros métodos de lectura:

Scanner scanner = new Scanner(System.in);

System.out.print("Introduce tu edad: ");
int edad = scanner.nextInt();
// El salto de línea queda en el buffer

scanner.nextLine(); // Se consume el salto de línea pendiente

System.out.print("Introduce tu nombre completo: ");
String nombre = scanner.nextLine();
System.out.println("Nombre: " + nombre + ", Edad: " + edad);

Lectura de tipos numéricos

La clase Scanner ofrece métodos específicos para cada tipo numérico en Java:

Scanner scanner = new Scanner("10 20 30.5 40.75 9223372036854775807 3.4028235E38");

byte valorByte = scanner.nextByte();       // Lee un byte (8 bits)
short valorShort = scanner.nextShort();    // Lee un short (16 bits)
float valorFloat = scanner.nextFloat();    // Lee un float
double valorDouble = scanner.nextDouble(); // Lee un double
long valorLong = scanner.nextLong();       // Lee un long (64 bits)
float valorFloatMax = scanner.nextFloat(); // Lee un float grande

System.out.println("byte: " + valorByte);
System.out.println("short: " + valorShort);
System.out.println("float: " + valorFloat);
System.out.println("double: " + valorDouble);
System.out.println("long: " + valorLong);
System.out.println("float máximo: " + valorFloatMax);

Lectura con formato específico

Para situaciones donde los datos tienen un formato particular, se puede utilizar el método findInLine() junto con expresiones regulares:

String datos = "Código: ABC-123, Precio: 29.99€";
Scanner scanner = new Scanner(datos);

// Buscar un patrón de código (letras-números)
String codigo = scanner.findInLine("[A-Z]+-\\d+");
System.out.println("Código encontrado: " + codigo);

// Buscar un valor numérico seguido de símbolo de euro
scanner.findInLine("Precio: ");
double precio = scanner.nextDouble();
System.out.println("Precio: " + precio);

Lectura de datos con validación

En aplicaciones reales, es común necesitar validar los datos de entrada. Se puede combinar los métodos hasNext con la lectura para crear entradas robustas:

import java.util.Scanner;

public class EntradaValidada {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int edad = 0;
        boolean entradaValida = false;
        
        while (!entradaValida) {
            System.out.print("Introduce tu edad (1-120): ");
            
            if (scanner.hasNextInt()) {
                edad = scanner.nextInt();
                
                if (edad >= 1 && edad <= 120) {
                    entradaValida = true;
                } else {
                    System.out.println("La edad debe estar entre 1 y 120.");
                }
            } else {
                System.out.println("Por favor, introduce un número válido.");
                scanner.next(); // Descartar la entrada inválida
            }
        }
        
        System.out.println("Edad registrada: " + edad);
        scanner.close();
    }
}

Lectura de datos desde archivos CSV

La clase Scanner es útil para procesar archivos con formato estructurado como CSV:

import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;

public class LectorCSV {
    public static void main(String[] args) {
        try {
            File archivo = new File("datos.csv");
            Scanner scanner = new Scanner(archivo);
            
            // Configurar coma como delimitador
            scanner.useDelimiter(",|\\n");
            
            // Leer y procesar encabezados
            String nombre = scanner.next();
            String edadHeader = scanner.next();
            String salarioHeader = scanner.next();
            
            System.out.println("Datos de empleados:");
            System.out.printf("%-15s %-5s %-10s%n", nombre, edadHeader, salarioHeader);
            System.out.println("------------------------------");
            
            // Procesar cada línea de datos
            while (scanner.hasNext()) {
                String nombreEmpleado = scanner.next();
                int edadEmpleado = scanner.nextInt();
                double salarioEmpleado = scanner.nextDouble();
                
                System.out.printf("%-15s %-5d %.2f€%n", 
                                 nombreEmpleado, edadEmpleado, salarioEmpleado);
            }
            
            scanner.close();
        } catch (FileNotFoundException e) {
            System.err.println("No se pudo encontrar el archivo: " + e.getMessage());
        }
    }
}

Lectura de datos binarios

Aunque Scanner está principalmente diseñado para texto, también puede leer datos binarios utilizando métodos como nextByte() junto con la configuración adecuada:

import java.io.FileInputStream;
import java.io.IOException;
import java.util.Scanner;

public class LecturaBinaria {
    public static void main(String[] args) {
        try (FileInputStream fis = new FileInputStream("datos.bin");
             Scanner scanner = new Scanner(fis)) {
            
            // Configurar para leer bytes individuales
            scanner.useRadix(16); // Base hexadecimal
            
            System.out.println("Contenido del archivo en hexadecimal:");
            while (scanner.hasNextInt()) {
                int byteLeido = scanner.nextInt();
                System.out.printf("%02X ", byteLeido);
            }
            
        } catch (IOException e) {
            System.err.println("Error al leer el archivo: " + e.getMessage());
        }
    }
}

Lectura de datos con formato específico de localización

Para aplicaciones internacionales, es crucial manejar correctamente los formatos numéricos según la localización:

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

public class LecturaInternacional {
    public static void main(String[] args) {
        // Datos con formato español (coma decimal)
        String datosES = "3,14 2,718 1,618";
        Scanner scannerES = new Scanner(datosES);
        scannerES.useLocale(new Locale("es", "ES"));
        
        // Datos con formato inglés (punto decimal)
        String datosEN = "3.14 2.718 1.618";
        Scanner scannerEN = new Scanner(datosEN);
        scannerEN.useLocale(Locale.US);
        
        System.out.println("Leyendo números con formato español:");
        while (scannerES.hasNextDouble()) {
            System.out.println(scannerES.nextDouble());
        }
        
        System.out.println("\nLeyendo números con formato inglés:");
        while (scannerEN.hasNextDouble()) {
            System.out.println(scannerEN.nextDouble());
        }
        
        scannerES.close();
        scannerEN.close();
    }
}

Manejo de errores y excepciones

Al trabajar con la clase Scanner en Java, el manejo adecuado de errores y excepciones es fundamental para crear aplicaciones robustas que puedan responder correctamente ante situaciones imprevistas. Cuando se procesan datos de entrada, especialmente aquellos proporcionados por usuarios o fuentes externas, es común encontrarse con valores que no cumplen con el formato esperado.

La clase Scanner puede lanzar diversas excepciones durante su operación, siendo las más comunes:

  • InputMismatchException: Se produce cuando el token leído no coincide con el patrón esperado para el tipo de dato solicitado.
  • NoSuchElementException: Ocurre al intentar leer más allá del final de la entrada.
  • IllegalStateException: Se lanza si el scanner ha sido cerrado antes de realizar una operación.
  • FileNotFoundException: Aparece cuando se intenta crear un Scanner con un archivo que no existe.

Captura de InputMismatchException

Uno de los errores más frecuentes al utilizar Scanner es intentar leer un tipo de dato cuando la entrada contiene un formato incompatible:

import java.util.InputMismatchException;
import java.util.Scanner;

public class ManejoInputMismatch {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        
        System.out.print("Introduce tu edad: ");
        
        try {
            int edad = scanner.nextInt();
            System.out.println("Tu edad es: " + edad);
        } catch (InputMismatchException e) {
            System.out.println("Error: Debes introducir un número entero.");
            // La entrada inválida permanece en el buffer
            String entradaInvalida = scanner.next(); // Limpia el buffer
            System.out.println("Entrada rechazada: " + entradaInvalida);
        }
        
        scanner.close();
    }
}

Cuando se produce una InputMismatchException, el token que causó el error permanece en el buffer de entrada. Por ello, es necesario consumirlo explícitamente con scanner.next() para evitar un bucle infinito de excepciones si se intenta leer nuevamente.

Manejo de NoSuchElementException

Esta excepción se produce cuando intentamos leer más allá del final de la entrada disponible:

import java.util.NoSuchElementException;
import java.util.Scanner;

public class ManejoNoSuchElement {
    public static void main(String[] args) {
        Scanner scanner = new Scanner("Solo hay una línea de texto");
        
        try {
            // Leemos la primera línea correctamente
            String primeraLinea = scanner.nextLine();
            System.out.println("Primera línea: " + primeraLinea);
            
            // Intentamos leer una segunda línea que no existe
            String segundaLinea = scanner.nextLine();
            System.out.println("Segunda línea: " + segundaLinea);
        } catch (NoSuchElementException e) {
            System.out.println("Error: No hay más líneas para leer.");
        } finally {
            scanner.close();
        }
    }
}

Para evitar esta excepción, se recomienda utilizar los métodos hasNext(), hasNextLine() o similares antes de intentar leer datos:

Scanner scanner = new Scanner("Datos limitados");

while (scanner.hasNextLine()) {
    String linea = scanner.nextLine();
    System.out.println("Línea leída: " + linea);
}

scanner.close();

Manejo de IllegalStateException

Esta excepción ocurre cuando se intenta utilizar un Scanner que ya ha sido cerrado:

import java.util.IllegalStateException;
import java.util.Scanner;

public class ManejoIllegalState {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        
        System.out.println("Escribe algo: ");
        String entrada = scanner.nextLine();
        System.out.println("Has escrito: " + entrada);
        
        scanner.close(); // Cerramos el scanner
        
        try {
            System.out.println("Escribe algo más: ");
            String masEntrada = scanner.nextLine(); // Esto lanzará IllegalStateException
        } catch (IllegalStateException e) {
            System.out.println("Error: El scanner ya ha sido cerrado.");
            System.out.println("Mensaje de error: " + e.getMessage());
        }
    }
}

Para evitar este problema, se debe mantener el Scanner abierto mientras sea necesario y cerrarlo solo cuando ya no se vaya a utilizar más.

Manejo de FileNotFoundException

Cuando se crea un Scanner para leer un archivo, es obligatorio manejar la posible FileNotFoundException:

import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;

public class ManejoFileNotFound {
    public static void main(String[] args) {
        try {
            File archivo = new File("datos_inexistentes.txt");
            Scanner scanner = new Scanner(archivo);
            
            while (scanner.hasNextLine()) {
                System.out.println(scanner.nextLine());
            }
            
            scanner.close();
        } catch (FileNotFoundException e) {
            System.out.println("Error: No se pudo encontrar el archivo.");
            System.out.println("Ruta: " + e.getMessage());
            
            // Ofrecer alternativas al usuario
            System.out.println("¿Deseas crear el archivo? (s/n)");
            Scanner consola = new Scanner(System.in);
            String respuesta = consola.nextLine();
            
            if (respuesta.equalsIgnoreCase("s")) {
                // Código para crear el archivo
                System.out.println("Implementación de creación de archivo...");
            }
            
            consola.close();
        }
    }
}

Patrón de validación con bucle

Un enfoque común para manejar errores de entrada es implementar un bucle de validación que continúe solicitando datos hasta recibir una entrada válida:

import java.util.InputMismatchException;
import java.util.Scanner;

public class ValidacionConBucle {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int numero = 0;
        boolean entradaValida = false;
        
        while (!entradaValida) {
            System.out.print("Introduce un número entero: ");
            
            try {
                numero = scanner.nextInt();
                entradaValida = true; // Si llegamos aquí, la entrada es válida
            } catch (InputMismatchException e) {
                System.out.println("Error: Debes introducir un número entero.");
                scanner.next(); // Limpiar la entrada inválida
            }
        }
        
        System.out.println("Has introducido: " + numero);
        scanner.close();
    }
}

Validación con métodos específicos

Para casos más complejos, se pueden crear métodos de validación específicos que encapsulen la lógica de manejo de errores:

import java.util.InputMismatchException;
import java.util.Scanner;

public class ValidacionMetodos {
    private static Scanner scanner = new Scanner(System.in);
    
    public static void main(String[] args) {
        int edad = leerEnteroEnRango("Introduce tu edad: ", 0, 120);
        double altura = leerDecimalEnRango("Introduce tu altura (en metros): ", 0.5, 2.5);
        String nombre = leerTextoNoVacio("Introduce tu nombre: ");
        
        System.out.println("\nDatos registrados:");
        System.out.println("Nombre: " + nombre);
        System.out.println("Edad: " + edad + " años");
        System.out.println("Altura: " + altura + " metros");
        
        scanner.close();
    }
    
    /**
     * Lee un entero dentro de un rango específico.
     * @param mensaje Mensaje para solicitar el dato
     * @param min Valor mínimo aceptable
     * @param max Valor máximo aceptable
     * @return Entero validado dentro del rango
     */
    public static int leerEnteroEnRango(String mensaje, int min, int max) {
        int valor = 0;
        boolean entradaValida = false;
        
        while (!entradaValida) {
            System.out.print(mensaje);
            
            try {
                valor = scanner.nextInt();
                scanner.nextLine(); // Limpiar buffer
                
                if (valor >= min && valor <= max) {
                    entradaValida = true;
                } else {
                    System.out.printf("Error: El valor debe estar entre %d y %d.%n", min, max);
                }
            } catch (InputMismatchException e) {
                System.out.println("Error: Debes introducir un número entero.");
                scanner.nextLine(); // Limpiar la entrada inválida
            }
        }
        
        return valor;
    }
    
    /**
     * Lee un decimal dentro de un rango específico.
     * @param mensaje Mensaje para solicitar el dato
     * @param min Valor mínimo aceptable
     * @param max Valor máximo aceptable
     * @return Decimal validado dentro del rango
     */
    public static double leerDecimalEnRango(String mensaje, double min, double max) {
        double valor = 0;
        boolean entradaValida = false;
        
        while (!entradaValida) {
            System.out.print(mensaje);
            
            try {
                valor = scanner.nextDouble();
                scanner.nextLine(); // Limpiar buffer
                
                if (valor >= min && valor <= max) {
                    entradaValida = true;
                } else {
                    System.out.printf("Error: El valor debe estar entre %.2f y %.2f.%n", min, max);
                }
            } catch (InputMismatchException e) {
                System.out.println("Error: Debes introducir un número decimal.");
                scanner.nextLine(); // Limpiar la entrada inválida
            }
        }
        
        return valor;
    }
    
    /**
     * Lee una cadena de texto no vacía.
     * @param mensaje Mensaje para solicitar el dato
     * @return Texto validado no vacío
     */
    public static String leerTextoNoVacio(String mensaje) {
        String texto = "";
        boolean entradaValida = false;
        
        while (!entradaValida) {
            System.out.print(mensaje);
            texto = scanner.nextLine().trim();
            
            if (!texto.isEmpty()) {
                entradaValida = true;
            } else {
                System.out.println("Error: El texto no puede estar vacío.");
            }
        }
        
        return texto;
    }
}

Manejo de recursos con try-with-resources

Para garantizar que los recursos se cierren correctamente incluso en caso de excepción, se recomienda utilizar el patrón try-with-resources introducido en Java 7:

import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;

public class TryWithResourcesScanner {
    public static void main(String[] args) {
        File archivo = new File("datos.txt");
        
        try (Scanner scanner = new Scanner(archivo)) {
            while (scanner.hasNextLine()) {
                String linea = scanner.nextLine();
                System.out.println(linea);
            }
            // No es necesario cerrar el scanner, se cierra automáticamente
        } catch (FileNotFoundException e) {
            System.out.println("Error: No se pudo encontrar el archivo.");
            e.printStackTrace();
        }
    }
}

Manejo de errores en aplicaciones interactivas

En aplicaciones interactivas más complejas, es útil implementar un sistema de manejo de errores que proporcione retroalimentación clara al usuario:

import java.util.InputMismatchException;
import java.util.Scanner;

public class AplicacionInteractiva {
    public static void main(String[] args) {
        try (Scanner scanner = new Scanner(System.in)) {
            boolean ejecutando = true;
            
            while (ejecutando) {
                mostrarMenu();
                
                try {
                    System.out.print("Selecciona una opción: ");
                    int opcion = scanner.nextInt();
                    scanner.nextLine(); // Limpiar buffer
                    
                    switch (opcion) {
                        case 1:
                            procesarDatosPersonales(scanner);
                            break;
                        case 2:
                            calcularEstadisticas(scanner);
                            break;
                        case 3:
                            System.out.println("Saliendo del programa...");
                            ejecutando = false;
                            break;
                        default:
                            System.out.println("Opción no válida. Intenta de nuevo.");
                    }
                } catch (InputMismatchException e) {
                    System.out.println("Error: Debes introducir un número.");
                    scanner.nextLine(); // Limpiar la entrada inválida
                } catch (Exception e) {
                    System.out.println("Error inesperado: " + e.getMessage());
                    e.printStackTrace();
                }
                
                System.out.println(); // Línea en blanco para separar iteraciones
            }
        } // Scanner se cierra automáticamente
    }
    
    private static void mostrarMenu() {
        System.out.println("=== MENÚ PRINCIPAL ===");
        System.out.println("1. Introducir datos personales");
        System.out.println("2. Calcular estadísticas");
        System.out.println("3. Salir");
    }
    
    private static void procesarDatosPersonales(Scanner scanner) {
        System.out.println("\n-- Formulario de Datos Personales --");
        
        try {
            String nombre = leerTextoNoVacio(scanner, "Nombre: ");
            int edad = leerEnteroPositivo(scanner, "Edad: ");
            
            System.out.println("\nDatos registrados correctamente:");
            System.out.println("Nombre: " + nombre);
            System.out.println("Edad: " + edad);
        } catch (Exception e) {
            System.out.println("Error al procesar datos personales: " + e.getMessage());
        }
    }
    
    private static void calcularEstadisticas(Scanner scanner) {
        System.out.println("\n-- Calculadora de Estadísticas --");
        
        try {
            System.out.print("¿Cuántos números deseas introducir? ");
            int cantidad = scanner.nextInt();
            
            if (cantidad <= 0) {
                System.out.println("La cantidad debe ser positiva.");
                return;
            }
            
            double suma = 0;
            double maximo = Double.MIN_VALUE;
            double minimo = Double.MAX_VALUE;
            
            for (int i = 1; i <= cantidad; i++) {
                System.out.print("Número " + i + ": ");
                double numero = scanner.nextDouble();
                
                suma += numero;
                maximo = Math.max(maximo, numero);
                minimo = Math.min(minimo, numero);
            }
            
            double promedio = suma / cantidad;
            
            System.out.println("\nResultados:");
            System.out.printf("Promedio: %.2f%n", promedio);
            System.out.printf("Máximo: %.2f%n", maximo);
            System.out.printf("Mínimo: %.2f%n", minimo);
            
        } catch (InputMismatchException e) {
            System.out.println("Error: Formato de número inválido.");
            scanner.nextLine(); // Limpiar buffer
        } catch (Exception e) {
            System.out.println("Error en el cálculo: " + e.getMessage());
        }
    }
    
    private static String leerTextoNoVacio(Scanner scanner, String prompt) {
        String texto;
        do {
            System.out.print(prompt);
            texto = scanner.nextLine().trim();
            
            if (texto.isEmpty()) {
                System.out.println("Error: El texto no puede estar vacío.");
            }
        } while (texto.isEmpty());
        
        return texto;
    }
    
    private static int leerEnteroPositivo(Scanner scanner, String prompt) {
        int valor = 0;
        boolean entradaValida = false;
        
        while (!entradaValida) {
            try {
                System.out.print(prompt);
                valor = scanner.nextInt();
                scanner.nextLine(); // Limpiar buffer
                
                if (valor > 0) {
                    entradaValida = true;
                } else {
                    System.out.println("Error: El valor debe ser positivo.");
                }
            } catch (InputMismatchException e) {
                System.out.println("Error: Debes introducir un número entero.");
                scanner.nextLine(); // Limpiar la entrada inválida
            }
        }
        
        return valor;
    }
}

Recuperación de errores en procesamiento por lotes

Cuando se procesan grandes cantidades de datos, es importante implementar estrategias de recuperación que permitan continuar con el procesamiento incluso después de encontrar errores:

import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.InputMismatchException;
import java.util.List;
import java.util.Scanner;

public class ProcesadorPorLotes {
    public static void main(String[] args) {
        File archivo = new File("datos_numericos.txt");
        List<Integer> numerosValidos = new ArrayList<>();
        List<String> errores = new ArrayList<>();
        int lineasProcesadas = 0;
        
        try (Scanner scanner = new Scanner(archivo)) {
            while (scanner.hasNextLine()) {
                lineasProcesadas++;
                String linea = scanner.nextLine().trim();
                
                if (linea.isEmpty()) {
                    continue; // Ignorar líneas vacías
                }
                
                try {
                    Scanner lineScanner = new Scanner(linea);
                    int numero = lineScanner.nextInt();
                    
                    if (lineScanner.hasNext()) {
                        errores.add("Línea " + lineasProcesadas + ": Datos adicionales después del número");
                    } else {
                        numerosValidos.add(numero);
                    }
                    
                    lineScanner.close();
                } catch (InputMismatchException e) {
                    errores.add("Línea " + lineasProcesadas + ": No es un número válido - '" + linea + "'");
                }
            }
        } catch (FileNotFoundException e) {
            System.out.println("Error: No se pudo encontrar el archivo.");
            return;
        }
        
        // Mostrar resultados del procesamiento
        System.out.println("=== Resumen de procesamiento ===");
        System.out.println("Líneas procesadas: " + lineasProcesadas);
        System.out.println("Números válidos encontrados: " + numerosValidos.size());
        System.out.println("Errores encontrados: " + errores.size());
        
        if (!numerosValidos.isEmpty()) {
            int suma = 0;
            for (int num : numerosValidos) {
                suma += num;
            }
            double promedio = (double) suma / numerosValidos.size();
            
            System.out.println("\nEstadísticas de números válidos:");
            System.out.println("Suma: " + suma);
            System.out.printf("Promedio: %.2f%n", promedio);
        }
        
        if (!errores.isEmpty()) {
            System.out.println("\nDetalle de errores:");
            for (String error : errores) {
                System.out.println("- " + error);
            }
        }
    }
}

Buenas prácticas y patrones de uso

La implementación efectiva de la clase Scanner va más allá de su funcionalidad básica. Para desarrollar aplicaciones Java robustas y mantenibles, es fundamental seguir un conjunto de buenas prácticas que optimicen el rendimiento, mejoren la legibilidad del código y eviten problemas comunes.

Gestión adecuada de recursos

Uno de los aspectos más importantes al trabajar con Scanner es la correcta gestión de sus recursos. El patrón try-with-resources resulta ideal para este propósito:

try (Scanner scanner = new Scanner(new File("datos.txt"))) {
    // Operaciones con el scanner
    while (scanner.hasNextLine()) {
        System.out.println(scanner.nextLine());
    }
} catch (FileNotFoundException e) {
    System.err.println("No se pudo encontrar el archivo: " + e.getMessage());
}

Este enfoque garantiza que los recursos se liberen adecuadamente incluso si ocurre una excepción durante la ejecución. Para casos donde se necesita mantener el Scanner durante toda la vida de la aplicación, se recomienda:

public class GestorEntrada {
    private static final Scanner ENTRADA = new Scanner(System.in);
    
    // Métodos de utilidad para leer datos
    
    public static void cerrarRecursos() {
        if (ENTRADA != null) {
            ENTRADA.close();
        }
    }
}

Separación de responsabilidades

Es recomendable encapsular la lógica de lectura en clases o métodos específicos, siguiendo el principio de responsabilidad única:

public class LectorDatos {
    private final Scanner scanner;
    
    public LectorDatos(InputStream fuente) {
        this.scanner = new Scanner(fuente);
    }
    
    public String leerLinea(String mensaje) {
        System.out.print(mensaje);
        return scanner.nextLine();
    }
    
    public int leerEntero(String mensaje) {
        while (true) {
            try {
                System.out.print(mensaje);
                int valor = scanner.nextInt();
                scanner.nextLine(); // Limpiar buffer
                return valor;
            } catch (InputMismatchException e) {
                System.out.println("Error: Introduce un número entero válido.");
                scanner.nextLine(); // Limpiar entrada inválida
            }
        }
    }
    
    public void cerrar() {
        scanner.close();
    }
}

Este enfoque facilita la reutilización del código y simplifica las pruebas unitarias.

Patrón de validación progresiva

Para mejorar la experiencia del usuario, se recomienda implementar un patrón de validación progresiva que proporcione retroalimentación inmediata:

public String leerEmail(String mensaje) {
    String email;
    Pattern patronEmail = Pattern.compile("^[A-Za-z0-9+_.-]+@(.+)$");
    
    while (true) {
        System.out.print(mensaje);
        email = scanner.nextLine().trim();
        
        if (email.isEmpty()) {
            System.out.println("Error: El email no puede estar vacío.");
        } else if (!patronEmail.matcher(email).matches()) {
            System.out.println("Error: Formato de email inválido.");
        } else {
            return email;
        }
    }
}

Uso de delimitadores específicos

Para el procesamiento de formatos estructurados, es más eficiente configurar delimitadores específicos que realizar múltiples operaciones de análisis:

public List<Producto> cargarProductosCSV(String rutaArchivo) throws FileNotFoundException {
    List<Producto> productos = new ArrayList<>();
    
    try (Scanner scanner = new Scanner(new File(rutaArchivo))) {
        // Configurar delimitador para CSV
        scanner.useDelimiter(",|\r?\n");
        
        // Saltar encabezados
        if (scanner.hasNextLine()) {
            scanner.nextLine();
        }
        
        while (scanner.hasNext()) {
            String codigo = scanner.next();
            String nombre = scanner.next();
            double precio = scanner.nextDouble();
            int stock = scanner.nextInt();
            
            productos.add(new Producto(codigo, nombre, precio, stock));
        }
    }
    
    return productos;
}

Implementación del patrón Builder para configuración

Para casos donde se requiere una configuración compleja del Scanner, el patrón Builder proporciona una interfaz fluida y legible:

public class ScannerBuilder {
    private Scanner scanner;
    
    private ScannerBuilder(InputStream source) {
        this.scanner = new Scanner(source);
    }
    
    public static ScannerBuilder fromInputStream(InputStream source) {
        return new ScannerBuilder(source);
    }
    
    public static ScannerBuilder fromFile(String path) throws FileNotFoundException {
        return new ScannerBuilder(new FileInputStream(path));
    }
    
    public static ScannerBuilder fromString(String text) {
        return new ScannerBuilder(new ByteArrayInputStream(text.getBytes()));
    }
    
    public ScannerBuilder withDelimiter(String pattern) {
        scanner.useDelimiter(pattern);
        return this;
    }
    
    public ScannerBuilder withLocale(Locale locale) {
        scanner.useLocale(locale);
        return this;
    }
    
    public Scanner build() {
        return scanner;
    }
}

Uso del builder:

Scanner csvScanner = ScannerBuilder.fromFile("datos.csv")
                                   .withDelimiter(",")
                                   .withLocale(Locale.US)
                                   .build();

Estrategia de buffer único para System.in

Cuando se trabaja con la entrada estándar, es recomendable utilizar un único Scanner para evitar problemas con el cierre de System.in:

public class EntradaEstandar {
    private static final Scanner SCANNER = new Scanner(System.in);
    
    public static String leerLinea() {
        return SCANNER.nextLine();
    }
    
    public static int leerEntero() {
        while (true) {
            try {
                int valor = SCANNER.nextInt();
                SCANNER.nextLine(); // Limpiar buffer
                return valor;
            } catch (InputMismatchException e) {
                System.out.println("Por favor, introduce un número entero válido.");
                SCANNER.nextLine(); // Limpiar entrada inválida
            }
        }
    }
    
    // No cerrar el scanner en métodos individuales
    
    // Método para cerrar al finalizar la aplicación
    public static void cerrar() {
        SCANNER.close();
    }
}

Procesamiento por lotes con control de errores

Para el procesamiento de grandes volúmenes de datos, es recomendable implementar un sistema de control de errores que permita continuar con el procesamiento a pesar de encontrar entradas inválidas:

public class ProcesadorLotes {
    public static ProcessResult procesarArchivo(String rutaArchivo) {
        ProcessResult resultado = new ProcessResult();
        int lineaActual = 0;
        
        try (Scanner scanner = new Scanner(new File(rutaArchivo))) {
            while (scanner.hasNextLine()) {
                lineaActual++;
                String linea = scanner.nextLine().trim();
                
                if (linea.isEmpty() || linea.startsWith("#")) {
                    continue; // Ignorar líneas vacías y comentarios
                }
                
                try {
                    Registro registro = parsearLinea(linea);
                    resultado.addRegistroValido(registro);
                } catch (Exception e) {
                    resultado.addError(new ErrorProcesamiento(lineaActual, linea, e.getMessage()));
                }
            }
        } catch (FileNotFoundException e) {
            resultado.setErrorCritico("Archivo no encontrado: " + e.getMessage());
        }
        
        return resultado;
    }
    
    private static Registro parsearLinea(String linea) {
        // Implementación del parseo
        // ...
    }
}

Uso de Scanner con expresiones regulares

Para extraer información específica de textos complejos, la combinación de Scanner con expresiones regulares resulta muy efectiva:

public Map<String, String> extraerDatosEstructurados(String texto) {
    Map<String, String> datos = new HashMap<>();
    Scanner scanner = new Scanner(texto);
    
    // Buscar patrones específicos
    String patronNombre = "Nombre:\\s*([^\\n]+)";
    String patronEmail = "Email:\\s*([\\w._%+-]+@[\\w.-]+\\.[a-zA-Z]{2,})";
    String patronTelefono = "Tel[ée]fono:\\s*(\\+?\\d[\\d\\s-]{8,})";
    
    if (scanner.findWithinHorizon(patronNombre, 0) != null) {
        datos.put("nombre", scanner.match().group(1).trim());
    }
    
    if (scanner.findWithinHorizon(patronEmail, 0) != null) {
        datos.put("email", scanner.match().group(1).trim());
    }
    
    if (scanner.findWithinHorizon(patronTelefono, 0) != null) {
        datos.put("telefono", scanner.match().group(1).trim());
    }
    
    scanner.close();
    return datos;
}

Implementación de caché para operaciones repetitivas

Cuando se necesita procesar repetidamente la misma fuente de datos, es más eficiente implementar un sistema de caché:

public class LectorConfiguracion {
    private final String rutaArchivo;
    private Map<String, String> propiedades;
    
    public LectorConfiguracion(String rutaArchivo) {
        this.rutaArchivo = rutaArchivo;
        this.propiedades = null; // Se cargará bajo demanda
    }
    
    public String getPropiedad(String clave) {
        if (propiedades == null) {
            cargarPropiedades();
        }
        return propiedades.getOrDefault(clave, null);
    }
    
    private void cargarPropiedades() {
        propiedades = new HashMap<>();
        
        try (Scanner scanner = new Scanner(new File(rutaArchivo))) {
            while (scanner.hasNextLine()) {
                String linea = scanner.nextLine().trim();
                
                if (linea.isEmpty() || linea.startsWith("#")) {
                    continue;
                }
                
                int separador = linea.indexOf('=');
                if (separador > 0) {
                    String clave = linea.substring(0, separador).trim();
                    String valor = linea.substring(separador + 1).trim();
                    propiedades.put(clave, valor);
                }
            }
        } catch (FileNotFoundException e) {
            System.err.println("Archivo de configuración no encontrado: " + e.getMessage());
        }
    }
}

Uso de Scanner en aplicaciones multihilo

En entornos concurrentes, es importante asegurar que el acceso a los objetos Scanner sea thread-safe:

public class LectorConcurrente {
    private final Scanner scanner;
    private final Object lock = new Object();
    
    public LectorConcurrente(InputStream fuente) {
        this.scanner = new Scanner(fuente);
    }
    
    public String leerLinea() {
        synchronized (lock) {
            if (scanner.hasNextLine()) {
                return scanner.nextLine();
            }
            return null;
        }
    }
    
    public void cerrar() {
        synchronized (lock) {
            scanner.close();
        }
    }
}

Integración con el patrón Observer

Para aplicaciones que requieren notificación de eventos durante la lectura, se puede integrar Scanner con el patrón Observer:

public class LectorObservable {
    private final Scanner scanner;
    private final List<LecturaListener> listeners = new ArrayList<>();
    
    public LectorObservable(InputStream fuente) {
        this.scanner = new Scanner(fuente);
    }
    
    public void agregarListener(LecturaListener listener) {
        listeners.add(listener);
    }
    
    public void iniciarLectura() {
        int numeroLinea = 0;
        
        while (scanner.hasNextLine()) {
            numeroLinea++;
            String linea = scanner.nextLine();
            
            for (LecturaListener listener : listeners) {
                listener.onLineaLeida(numeroLinea, linea);
            }
        }
        
        for (LecturaListener listener : listeners) {
            listener.onFinLectura(numeroLinea);
        }
        
        scanner.close();
    }
    
    public interface LecturaListener {
        void onLineaLeida(int numeroLinea, String contenido);
        void onFinLectura(int totalLineas);
    }
}

Optimización de rendimiento

Para mejorar el rendimiento al procesar grandes volúmenes de datos, se pueden aplicar varias técnicas:

public List<Dato> procesarArchivoGrande(String rutaArchivo) throws FileNotFoundException {
    List<Dato> resultados = new ArrayList<>(10000); // Preasignar capacidad
    
    // Usar BufferedInputStream para mejorar rendimiento
    try (Scanner scanner = new Scanner(
            new BufferedInputStream(
                new FileInputStream(rutaArchivo), 8192), 
            StandardCharsets.UTF_8.name())) {
        
        // Configurar para procesamiento eficiente
        scanner.useDelimiter("\n");
        
        while (scanner.hasNext()) {
            String linea = scanner.next();
            // Procesar la línea y añadir a resultados
            resultados.add(parsearLinea(linea));
        }
    }
    
    return resultados;
}

Implementación de un sistema de logging para depuración

Para facilitar la depuración, es útil implementar un sistema de logging que registre las operaciones realizadas con Scanner:

public class ScannerConLog {
    private final Scanner scanner;
    private final Logger logger;
    
    public ScannerConLog(InputStream fuente, Logger logger) {
        this.scanner = new Scanner(fuente);
        this.logger = logger;
    }
    
    public String nextLine() {
        try {
            String linea = scanner.nextLine();
            logger.fine("Leída línea: " + linea);
            return linea;
        } catch (NoSuchElementException e) {
            logger.warning("Intento de leer línea cuando no hay más disponibles");
            throw e;
        }
    }
    
    public int nextInt() {
        try {
            int valor = scanner.nextInt();
            logger.fine("Leído entero: " + valor);
            return valor;
        } catch (InputMismatchException e) {
            String tokenInvalido = scanner.next();
            logger.warning("Token inválido para entero: " + tokenInvalido);
            throw new InputMismatchException("No se pudo convertir a entero: " + tokenInvalido);
        }
    }
    
    // Implementar métodos similares para otros tipos
}

Uso de Scanner en aplicaciones interactivas con menús

Para aplicaciones con interfaces de usuario basadas en texto, se recomienda implementar un sistema de menús que utilice Scanner de manera eficiente:

public class MenuInteractivo {
    private final Scanner scanner;
    private final Map<Integer, MenuOpcion> opciones;
    
    public MenuInteractivo(Scanner scanner) {
        this.scanner = scanner;
        this.opciones = new HashMap<>();
    }
    
    public void agregarOpcion(int numero, String descripcion, Runnable accion) {
        opciones.put(numero, new MenuOpcion(descripcion, accion));
    }
    
    public void mostrarMenu() {
        System.out.println("\n=== MENÚ PRINCIPAL ===");
        
        for (Map.Entry<Integer, MenuOpcion> entry : opciones.entrySet()) {
            System.out.printf("%d. %s%n", entry.getKey(), entry.getValue().getDescripcion());
        }
        
        System.out.print("\nSelecciona una opción: ");
    }
    
    public void procesarEntrada() {
        try {
            int seleccion = scanner.nextInt();
            scanner.nextLine(); // Limpiar buffer
            
            MenuOpcion opcion = opciones.get(seleccion);
            if (opcion != null) {
                opcion.ejecutar();
            } else {
                System.out.println("Opción no válida. Intenta de nuevo.");
            }
        } catch (InputMismatchException e) {
            System.out.println("Por favor, introduce un número válido.");
            scanner.nextLine(); // Limpiar entrada inválida
        }
    }
    
    private static class MenuOpcion {
        private final String descripcion;
        private final Runnable accion;
        
        public MenuOpcion(String descripcion, Runnable accion) {
            this.descripcion = descripcion;
            this.accion = accion;
        }
        
        public String getDescripcion() {
            return descripcion;
        }
        
        public void ejecutar() {
            accion.run();
        }
    }
}
Aprende Java online

Otros ejercicios de programación de Java

Evalúa tus conocimientos de esta lección La clase Scanner con nuestros retos de programación de tipo Test, Puzzle, Código y Proyecto con VSCode, guiados por IA.

Streams: match

Test

Gestión de errores y excepciones

Código

CRUD en Java de modelo Customer sobre un ArrayList

Proyecto

Clases abstractas

Test

Listas

Código

Métodos de la clase String

Código

Streams: reduce()

Test

API java.nio 2

Puzzle

Polimorfismo

Código

Pattern Matching

Código

Streams: flatMap()

Test

Llamada y sobrecarga de funciones

Puzzle

Métodos referenciados

Test

Métodos de la clase String

Código

Representación de Fecha

Puzzle

Operadores lógicos

Test

Inferencia de tipos con var

Código

Tipos de datos

Código

Estructuras de iteración

Puzzle

Streams: forEach()

Test

Objetos

Puzzle

Funciones lambda

Test

Uso de Scanner

Puzzle

Tipos de variables

Puzzle

Streams: collect()

Puzzle

Operadores aritméticos

Puzzle

Arrays y matrices

Código

Clases y objetos

Código

Interfaz funcional Consumer

Test

CRUD en Java de modelo Customer sobre un HashMap

Proyecto

Interfaces

Código

Enumeraciones Enums

Código

API Optional

Test

Interfaz funcional Function

Test

Encapsulación

Test

Interfaces

Código

Uso de API Optional

Puzzle

Representación de Hora

Test

Herencia básica

Test

Clases y objetos

Código

Interfaz funcional Supplier

Puzzle

HashMap

Puzzle

Sobrecarga de métodos

Test

Polimorfismo de tiempo de ejecución

Puzzle

OOP en Java

Proyecto

Sobrecarga de métodos

Código

CRUD de productos en Java

Proyecto

Clases sealed

Código

Creación de Streams

Test

Records

Código

Encapsulación

Código

Streams: min max

Puzzle

Herencia

Código

Métodos avanzados de la clase String

Puzzle

Funciones

Código

Polimorfismo de tiempo de compilación

Test

Reto sintaxis Java

Proyecto

Conjuntos

Código

Estructuras de control

Código

Recursión

Código

Excepciones

Puzzle

Herencia avanzada

Puzzle

Estructuras de selección

Test

Uso de interfaces

Test

Operadores

Código

Variables

Código

HashSet

Test

Objeto Scanner

Test

Streams: filter()

Puzzle

Operaciones de Streams

Puzzle

Interfaz funcional Predicate

Puzzle

Streams: sorted()

Test

Configuración de entorno

Test

Uso de variables

Test

Clases

Test

Streams: distinct()

Puzzle

Streams: count()

Test

ArrayList

Test

Mapas

Código

Datos de referencia

Test

Interfaces funcionales

Puzzle

Métodos básicos de la clase String

Test

Tipos de datos

Código

Clases abstractas

Código

Instalación

Test

Funciones

Código

Excepciones

Código

Estructuras de control

Código

Herencia de clases

Código

La clase Scanner

Código

Generics

Código

Streams: map()

Puzzle

Funciones y encapsulamiento

Test

Todas las lecciones de Java

Accede a todas las lecciones de Java y aprende con ejemplos prácticos de código y ejercicios de programación con IDE web sin instalar nada.

Instalación De Java

Introducción Y Entorno

Configuración De Entorno Java

Introducción Y Entorno

Tipos De Datos

Sintaxis

Variables

Sintaxis

Operadores

Sintaxis

Estructuras De Control

Sintaxis

Funciones

Sintaxis

Recursión

Sintaxis

Arrays Y Matrices

Sintaxis

Excepciones

Programación Orientada A Objetos

Clases Y Objetos

Programación Orientada A Objetos

Encapsulación

Programación Orientada A Objetos

Herencia

Programación Orientada A Objetos

Clases Abstractas

Programación Orientada A Objetos

Interfaces

Programación Orientada A Objetos

Sobrecarga De Métodos

Programación Orientada A Objetos

Polimorfismo

Programación Orientada A Objetos

La Clase Scanner

Programación Orientada A Objetos

Métodos De La Clase String

Programación Orientada A Objetos

Excepciones

Programación Orientada A Objetos

Records

Programación Orientada A Objetos

Pattern Matching

Programación Orientada A Objetos

Inferencia De Tipos Con Var

Programación Orientada A Objetos

Enumeraciones Enums

Programación Orientada A Objetos

Generics

Programación Orientada A Objetos

Clases Sealed

Programación Orientada A Objetos

Listas

Framework Collections

Conjuntos

Framework Collections

Mapas

Framework Collections

Funciones Lambda

Programación Funcional

Interfaz Funcional Consumer

Programación Funcional

Interfaz Funcional Predicate

Programación Funcional

Interfaz Funcional Supplier

Programación Funcional

Interfaz Funcional Function

Programación Funcional

Métodos Referenciados

Programación Funcional

Creación De Streams

Programación Funcional

Operaciones Intermedias Con Streams: Map()

Programación Funcional

Operaciones Intermedias Con Streams: Filter()

Programación Funcional

Operaciones Intermedias Con Streams: Distinct()

Programación Funcional

Operaciones Finales Con Streams: Collect()

Programación Funcional

Operaciones Finales Con Streams: Min Max

Programación Funcional

Operaciones Intermedias Con Streams: Flatmap()

Programación Funcional

Operaciones Intermedias Con Streams: Sorted()

Programación Funcional

Operaciones Finales Con Streams: Reduce()

Programación Funcional

Operaciones Finales Con Streams: Foreach()

Programación Funcional

Operaciones Finales Con Streams: Count()

Programación Funcional

Operaciones Finales Con Streams: Match

Programación Funcional

Api Optional

Programación Funcional

Transformación

Programación Funcional

Reducción Y Acumulación

Programación Funcional

Mapeo

Programación Funcional

Streams Paralelos

Programación Funcional

Agrupación Y Partición

Programación Funcional

Filtrado Y Búsqueda

Programación Funcional

Api Java.nio 2

Entrada Y Salida Io

Fundamentos De Io

Entrada Y Salida Io

Leer Y Escribir Archivos

Entrada Y Salida Io

Httpclient Moderno

Entrada Y Salida Io

Clases De Nio2

Entrada Y Salida Io

Api Java.time

Api Java.time

Localtime

Api Java.time

Localdatetime

Api Java.time

Localdate

Api Java.time

Executorservice

Concurrencia

Virtual Threads (Project Loom)

Concurrencia

Future Y Completablefuture

Concurrencia

Spring Framework

Frameworks Para Java

Micronaut

Frameworks Para Java

Maven

Frameworks Para Java

Gradle

Frameworks Para Java

Lombok Para Java

Frameworks Para Java

Quarkus

Frameworks Para Java

Ecosistema Jakarta Ee De Java

Frameworks Para Java

Introducción A Junit 5

Testing

Accede GRATIS a Java y certifícate

Certificados de superación de Java

Supera todos los ejercicios de programación del curso de Java y obtén certificados de superación para mejorar tu currículum y tu empleabilidad.

En esta lección

Objetivos de aprendizaje de esta lección

  • Importar y utilizar la clase Scanner en Java
  • Entender los diferentes constructores de Scanner para varias fuentes de datos
  • Personalizar delimitadores y configuración regional
  • Leer y convertir datos a diferentes tipos
  • Gestionar el ciclo de vida de los objetos Scanner
  • Implementar validación de entradas con Scanner