1. Introducción
Cuando utilizamos la clase Scanner de Java para leer la entrada desde System.in, algunos entornos de desarrollo (IDE) pueden advertir sobre una posible fuga de recursos. Por ejemplo, si no cerramos explícitamente un Scanner, podemos enfrentar una advertencia como “Resource leak: ‘scanner’ is never closed”. Sin embargo, cerrar un Scanner conectado a System.in requiere un manejo cuidadoso para evitar problemas inesperados.
En este tutorial, aprenderemos por qué es importante cerrar un Scanner y cómo hacerlo correctamente en Java.
2. Entendiendo la advertencia
En Java, debemos cerrar recursos como archivos, conexiones de red y flujos de entrada después de su uso para liberar memoria del sistema. La clase Scanner lee entradas, como valores primitivos y cadenas, desde diversas fuentes, incluyendo archivos y System.in. Como implementa la interfaz Closeable, retiene recursos que deben liberarse una vez que ya no son necesarios.
Algunos IDEs, como Eclipse y Visual Studio Code, muestran advertencias si no cerramos adecuadamente un objeto Scanner. Por ejemplo, si no cerramos explícitamente un objeto Scanner, el IDE mostrará la advertencia sobre la fuga de recursos:
3. Cerrando un Scanner con el método close()
En Java, podemos cerrar un Scanner usando el método close()
, que ayuda a liberar recursos del sistema. Esto es especialmente importante cuando leemos desde un archivo o System.in.
Es recomendable cerrar un Scanner en el bloque finally para asegurar un cierre adecuado incluso si ocurre una excepción. Esto ayuda a prevenir fugas de recursos:
@Test
void givenUserName_whenGetGreetingMessage_thenReturnsWelcomeMessage() {
String input = "Anees\n";
ByteArrayInputStream inputStream = new ByteArrayInputStream(input.getBytes());
Scanner scanner = new Scanner(inputStream);
ScannerClose example = new ScannerClose();
String result = example.getGreetingMessage(scanner);
assertEquals("Hi, Anees Welcome to Baeldung", result);
scanner.close();
}
Cerrar los recursos es una buena práctica. Sin embargo, cerrar un Scanner ligado a System.in puede causar problemas. System.in representa el flujo de entrada estándar, generalmente el teclado. Si cerramos este flujo, el programa ya no podrá leer entradas del usuario, y no podemos reopen System.in dentro de la misma ejecución del programa.
Además, si creamos múltiples instancias de Scanner para System.in, cerrar cualquiera de ellos cerrará el flujo de entrada para todo el programa. Como resultado, podemos encontrar excepciones al intentar leer entradas de nuevo:
Scanner scanner = new Scanner(System.in);
System.out.print("Introduce tu nombre: ");
String name = scanner.nextLine();
System.out.println("¡Hola, " + name + " Bienvenido a Baeldung!");
scanner.close();
System.out.print("Introduce tu edad: ");
int age = scanner.nextInt();
System.out.println("Tu edad es: " + age);
Aquí, intentamos leer la edad, pero el Scanner se cerró anteriormente. Como resultado, encontramos una excepción IllegalStateException: Scanner closed:
Introduce tu nombre: Anees
¡Hola, Anees Bienvenido a Baeldung!
Introduce tu edad: Exception in thread "main" java.lang.IllegalStateException: Scanner closed
en java.base/java.util.Scanner.ensureOpen(Scanner.java:1150)
en java.base/java.util.Scanner.next(Scanner.java:1573)
en java.base/java.util.Scanner.nextInt(Scanner.java:2258)
en java.base/java.util.Scanner.nextInt(Scanner.java:2212)
Para evitar problemas con Scanner, debemos utilizar una única instancia de System.in y, si es posible, evitar cerrarla o cerrar solo cuando ya no necesitemos más entradas.
4. Cerrando un Scanner utilizando try-with-resources
Podemos usar try-with-resources para cerrar el Scanner automáticamente al final del bloque, incluso si ocurre una excepción. Este enfoque elimina la necesidad de un bloque finally y cierra automáticamente los recursos, lo que reduce el riesgo de fugas de recursos:
@Test
void givenUserName_whenGetGreetingMessage_thenReturnsWelcomeMessage() {
String input = "Anees\n";
ByteArrayInputStream inputStream = new ByteArrayInputStream(input.getBytes());
String result;
try (Scanner scanner = new Scanner(inputStream)) {
ScannerTryWithResources example = new ScannerTryWithResources();
result = example.getGreetingMessage(scanner);
}
assertEquals("Hi, Anees Welcome to Baeldung", result);
}
Cuando el bloque try finaliza su ejecución, Java cierra automáticamente la instancia de Scanner, lo que hace que el código sea más limpio y confiable. Por lo tanto, se recomienda el enfoque try-with-resources, ya que asegura una limpieza adecuada al cerrar un Scanner.
5. Conclusión
En este artículo, aprendimos por qué es importante cerrar un Scanner y cómo hacerlo correctamente. La gestión adecuada de recursos ayuda a prevenir fugas y asegura una ejecución fluida del programa.
Si bien es necesario cerrar un Scanner al leer desde archivos, manejar System.in requiere una precaución adicional. Usar una única instancia de Scanner y cerrarla solo cuando ya no necesitemos entradas ayuda a evitar problemas.
El enfoque try-with-resources simplifica la gestión de recursos al asegurar el cierre automático, lo que hace que el código sea más limpio y más confiable.
Recuerda, es fundamental seguir estas prácticas al trabajar con recursos en Java para garantizar el correcto funcionamiento de tu aplicación y prevenir errores en tiempo de ejecución.