Java 关闭 Scanner 的最佳实践

1、简介

当使用 Java 的 Scanner 类读取 System.in(标准输入)输入时,部分 IDE 会提示可能存在资源泄漏。

例如,若未显式关闭 Scanner,可能收到警告:“Resource leak: ‘scanner’ is never closed”。但关闭关联 System.inScanner 需谨慎处理,以避免意外问题。

本文将带你了解关闭 Scanner 的重要性及在 Java 中的正确操作方式。

2、理解 IDE 提示的异常

在 Java 中,使用文件、网络连接(Socket)和输入流等资源后应关闭资源以释放系统内存。Scanner 类从文件或 System.in 等源读取原始值和字符串输入,由于实现了 Closeable 接口,因此它持有的资源需要在不使用时及时释放。

部分 IDE(如 EclipseVisual Studio Code)若检测到未正确关闭 Scanner 对象,会显示如下资源泄漏警告:

IDE 提示资源泄露

3、通过 close() 方法关闭 Scanner

在 Java 中,可通过 close() 方法关闭 Scanner 以释放系统资源,这在读取文件或 System.in 时尤为重要。

建议在 finally 代码块中关闭 Scanner,确保即使发生异常也能正确释放资源,避免泄漏:

@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();
}

关闭资源是良好的实践,但关闭关联 System.inScanner 可能导致问题。System.in 流代表标准输入流(通常为键盘输入),若关闭此流,程序将无法再读取用户输入,且在同一程序执行期间无法重新打开。

此外,若为 System.in 创建多个 Scanner 实例,关闭其中任意一个都会使整个程序的输入流终止,导致后续读取输入时抛出异常:

Scanner scanner = new Scanner(System.in);

System.out.print("Enter your name: ");
String name = scanner.nextLine();
System.out.println("Hi, " + name + " Welcome to Baeldung!");

scanner.close();

System.out.print("Enter your age: ");
int age = scanner.nextInt();

System.out.println("Your age is: " + age);

上例中,当尝试读取 age 时,由于之前关闭了 Scanner,导致抛出 IllegalStateException: Scanner closed 异常:

Enter your name: Anees
Hi, Anees Welcome to Baeldung!
Enter your age: Exception in thread "main" java.lang.IllegalStateException: Scanner closed
    at java.base/java.util.Scanner.ensureOpen(Scanner.java:1150)
    at java.base/java.util.Scanner.next(Scanner.java:1573)
    at java.base/java.util.Scanner.nextInt(Scanner.java:2258)
    at java.base/java.util.Scanner.nextInt(Scanner.java:2212)

为避免 Scanner 引发的问题,应为 System.in 使用单例的 Scanner,并仅在不再需要输入时关闭。

4、使用 try-with-resources 语句关闭 Scanner

可使用 try-with-resources 自动在代码块结束时关闭 Scanner(即使发生异常)。此方法无需 finally 块,能自动关闭资源,降低泄漏风险:

@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);
}

try 代码块执行完毕时,Java 会自动关闭 Scanner 实例,使代码更简洁可靠。因此,try-with-resources 是推荐方式,它能确保正确清理资源。

5、总结

本文介绍了关闭 Scanner 的重要性及其正确操作方式。妥善管理资源可防止泄漏并确保程序顺畅运行。

读取文件时必须关闭 Scanner,但处理 System.in 需格外谨慎。使用单一 Scanner 实例并仅在无需输入时关闭,可避免问题发生。

try-with-resources 方式能自动确保资源关闭,简化管理流程,使代码更简洁可靠。


Ref:https://www.baeldung.com/java-scanner-close