在Java开发中,控制台输入是很多基础程序、测试程序常用的交互方式,Scanner类配合System.in是最典型的实现组合,但如果使用不当,很容易出现资源泄露或者输入功能异常的问题,需要掌握正确的使用和资源管理方法。

Scanner与System.in的基础使用
System.in是Java标准输入流,默认对应控制台输入,而Scanner是Java提供的方便的输入解析工具,可以把输入流的内容按照指定类型解析。基础的使用示例如下:
import java.util.Scanner;
public class ScannerBasicDemo {
public static void main(String[] args) {
// 创建Scanner绑定System.in
Scanner scanner = new Scanner(System.in);
System.out.println("请输入一个整数:");
// 读取整数
int num = scanner.nextInt();
System.out.println("你输入的整数是:" + num);
System.out.println("请输入一行文本:");
// 读取一行文本,注意nextLine和nextInt的衔接问题
scanner.nextLine(); // 消耗nextInt留下的换行符
String line = scanner.nextLine();
System.out.println("你输入的文本是:" + line);
// 关闭Scanner
scanner.close();
}
}这里需要注意,nextInt()、nextDouble()这类方法只会读取对应类型的内容,不会消耗输入流末尾的换行符,如果后续要调用nextLine()读取整行,需要先额外调用一次nextLine()消耗掉残留的换行符,否则会直接读取到空字符串。
常见的资源管理误区
很多开发者会在使用完Scanner后直接调用close()方法关闭资源,这其实存在隐患:Scanner的close()方法会同时关闭它绑定的输入流,也就是System.in。一旦System.in被关闭,后续如果再尝试创建新的Scanner绑定System.in,就会抛出异常,因为标准输入流已经被关闭无法再使用。
比如下面的错误示例:
import java.util.Scanner;
public class ScannerWrongDemo {
public static void main(String[] args) {
Scanner scanner1 = new Scanner(System.in);
System.out.println("请输入第一个数:");
int a = scanner1.nextInt();
scanner1.close(); // 关闭scanner1的同时关闭了System.in
// 再次创建Scanner绑定System.in,会抛出异常
Scanner scanner2 = new Scanner(System.in);
System.out.println("请输入第二个数:");
int b = scanner2.nextInt();
System.out.println("两数之和:" + (a + b));
}
}运行上述代码,在输入第一个数之后,程序会抛出java.lang.IllegalStateException: Scanner closed异常,就是因为System.in已经被关闭,新的Scanner无法正常工作。
正确的资源管理方法
1. 避免在程序中关闭Scanner
对于控制台程序来说,System.in是全局的标准输入流,正常不需要主动关闭,因此如果程序生命周期内只需要使用一次输入读取,可以不调用close()方法,让程序结束的时候由JVM自动回收相关资源,这样就不会出现System.in被提前关闭的问题。
2. 使用try-with-resources语法
如果确实需要管理Scanner的资源,比如在某个局部代码块中临时使用输入读取,可以使用Java 7引入的try-with-resources语法,不过需要注意不要关闭System.in。如果Scanner是绑定System.in的,不建议用这种方式关闭,除非明确知道后续不会再使用标准输入。如果是绑定其他输入流比如文件流,try-with-resources是很好的选择。
如果是绑定其他输入流的示例:
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.Scanner;
public class ScannerFileDemo {
public static void main(String[] args) {
// 绑定文件输入流,使用try-with-resources自动关闭
try (Scanner scanner = new Scanner(new FileInputStream("test.txt"))) {
while (scanner.hasNextLine()) {
System.out.println(scanner.nextLine());
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}
// 这里scanner已经自动关闭,且不会影响System.in
}
}3. 统一输入管理工具类
如果需要多次读取控制台输入,可以封装一个统一的输入工具类,避免重复创建Scanner,也不用担心关闭的问题:
import java.util.Scanner;
public class InputUtil {
// 全局唯一的Scanner实例,绑定System.in,不主动关闭
private static final Scanner SCANNER = new Scanner(System.in);
// 读取整数
public static int readInt() {
return SCANNER.nextInt();
}
// 读取一行文本,自动处理换行符问题
public static String readLine() {
return SCANNER.nextLine();
}
// 如果确实需要关闭,提供单独的方法,且标注风险
public static void closeScanner() {
SCANNER.close();
}
}使用这个工具类的时候,直接调用InputUtil.readInt()或者InputUtil.readLine()即可,不需要重复创建Scanner,也不会出现提前关闭System.in的问题,除非明确知道程序后续不再需要输入,才调用closeScanner()方法。
常见问题总结
nextLine()读取不到内容的问题,通常是前面的nextInt()等方法残留了换行符,需要先消耗掉换行符再读取。- 不要随意关闭绑定System.in的Scanner,避免标准输入流被关闭导致后续输入失败。
- 如果是读取文件等其他输入流,优先使用try-with-resources语法管理Scanner资源,避免资源泄露。
- 多次需要控制台输入的场景,建议使用单例的Scanner实例,减少重复创建的开销。