You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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实例,避免重复创建和关闭流:

  1. 不在getAsientos()中新建Scanner,而是将main方法中的Scanner作为参数传递给该方法;
  2. 仅在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 08:50:29