Java Scanner资源泄漏问题:关闭触发NoSuchElementException求解
影院座位管理代码的Scanner异常与优化方案
问题概述
刚学习二维数组,编写了影院座位管理代码:用6×10的布尔矩阵标记座位状态,true(显示为d)表示可用,false(显示为r)表示已占用。当前问题是:如果在getAsientos()方法中关闭新建的Scanner对象,会抛出java.util.NoSuchElementException异常,报错指向main方法的input.nextInt()处;虽然不关闭时代码功能正常,但存在资源泄漏风险,需要解决该问题并理解异常原因,同时希望得到代码优化建议。
原代码
import java.util.Scanner; public class Ayudantia { public static void main(String[] args) { System.out.println("¡Bienvenido a CineHoyts!"); System.out.println("Asientos: "); boolean[][] asientos = new boolean[6][10]; for (int i = 0; i<6; i++) { for (int j = 0; j<10; j++) { asientos[i][j] = true; System.out.print("[d] "); } System.out.println(); } int[] posicion = new int[2]; boolean menu = true; int opcion; Scanner input = new Scanner(System.in); while (menu) { System.out.println("1. Reservar asiento"); System.out.println("2. Ver asientos disponibles"); System.out.println("3. Salir"); System.out.println("Opción: "); opcion = input.nextInt(); switch(opcion) { case 1: System.out.print("Ingrese fila : "); posicion[0] = input.nextInt()-1; // nota: la filas empiezan del 1 pero su indice es 0, por lo tanto System.out.print("Ingrese columna: "); // se resta 1, para que el asiento 1,1 sea el primero, (pero con índice 0,0) posicion[1] = input.nextInt()-1; getAsientos(asientos, posicion); System.out.println(); break; case 2: getAsientosDisponibles(asientos); break; case 3: menu = false; break; default: System.out.println("Ingrese una opción válida."); break; } } System.out.println("Adiós"); input.close(); } public static void getAsientos(boolean[][] asientos, int[] posicion) { Scanner input = new Scanner(System.in); if (asientos[posicion[0]][posicion[1]]) { asientos[posicion[0]][posicion[1]] = false; System.out.print("Ingrese nombre : "); String nombre = input.next(); for (int i = 0; i<6; i++) { for (int j = 0; j<10; j++) { if (i == posicion[0] && j == posicion[1]) System.out.print("["+nombre+"] "); else { if (asientos[i][j]) System.out.print("[d] "); else System.out.print("[r] "); } } System.out.println(); } } else System.out.println("Ese asiento está reservado"); } public static void getAsientosDisponibles(boolean[][] asientos) { for (int i = 0; i < 6; i++) { for (int j = 0; j < 10; j++) { if (asientos[i][j]) { System.out.print("[d] "); } else System.out.print("[r] "); } System.out.println(); } } }
异常信息
Exception in thread "main" java.util.NoSuchElementException at java.base/java.util.Scanner.throwFor(Scanner.java:937) at java.base/java.util.Scanner.next(Scanner.java:1594) at java.base/java.util.Scanner.nextInt(Scanner.java:2258) at java.base/java.util.Scanner.nextInt(Scanner.java:2212) at lab05.Ayudantia.main(Ayudantia.java:24)
异常原因分析
所有基于System.in创建的Scanner对象,共享同一个底层输入流。如果在getAsientos()中关闭新建的Scanner,会直接关闭System.in流。当main方法后续再次调用input.nextInt()时,输入流已经关闭,无法读取用户输入,因此抛出NoSuchElementException。而当前代码中没有关闭这个Scanner,所以功能能正常运行,但会导致Scanner资源无法释放,存在泄漏风险。
资源泄漏解决方法
核心思路是全局复用同一个Scanner实例,避免重复创建和关闭流:
- 不在
getAsientos()中新建Scanner,而是将main方法中的Scanner作为参数传递给该方法; - 仅在main方法结束时统一关闭Scanner,确保流全程可用,且资源能被正确释放。
修改后的关键代码片段:
// 修改main方法中的调用处 getAsientos(asientos, posicion, input); // 修改getAsientos方法的定义和实现 public static void getAsientos(boolean[][] asientos, int[] posicion, Scanner input) { if (asientos[posicion[0]][posicion[1]]) { asientos[posicion[0]][posicion[1]] = false; System.out.print("Ingrese nombre : "); String nombre = input.next(); // 后续打印逻辑不变... } else { System.out.println("Ese asiento está reservado"); } }
代码优化建议
- 提取公共打印逻辑:
getAsientos()和getAsientosDisponibles()中的座位打印代码高度重复,可抽成一个公共方法,减少冗余:public static void printSeats(boolean[][] asientos, int[] reservedPos, String reservedName) { for (int i = 0; i < 6; i++) { for (int j = 0; j < 10; j++) { if (reservedPos != null && i == reservedPos[0] && j == reservedPos[1]) { System.out.print("[" + reservedName + "] "); } else { System.out.print(asientos[i][j] ? "[d] " : "[r] "); } } System.out.println(); } } - 添加输入边界校验:当前未校验用户输入的行/列是否合法(比如输入0或7行),需添加判断逻辑,避免数组越界:
// 在main方法的case 1中添加校验 if (posicion[0] < 0 || posicion[0] >= 6 || posicion[1] < 0 || posicion[1] >= 10) { System.out.println("Fila o columna inválida!"); continue; } - 使用常量定义座位尺寸:将6和10定义为类常量,后续修改座位数量更便捷,提升代码可读性:
private static final int ROW_COUNT = 6; private static final int COL_COUNT = 10; - 语义化变量命名:比如将
posicion改为selectedSeat,getAsientos改为reserveSeat,让代码意图更清晰; - 规范代码格式:统一缩进、空格,优化switch语句的排版,提升代码可读性。
内容的提问来源于stack exchange,提问作者Adaxil
相关产品推荐
相关产品推荐

