找不到关闭此扫描程序资源泄漏的方法

can't find a way to close this scanner resource leak

提问人:Adaxil 提问时间:9/29/2022 最后编辑:Adaxil 更新时间:9/29/2022 访问量:97

问:

我有这个代码 这是关于电影院座位的,我最近才学习二维数组,我做了一个 6x10(为这个问题设置)布尔矩阵,所以检查它是否可用 (true)“d” 或 taken (false) “r”,这是西班牙语,(如果您有任何问题,我可以翻译),但我的问题是在第 50 行, 在 getAsientos() 方法中,我制作了一个新对象来获取输入,但我无法关闭它,我在方法中尝试了 input.close(),但它给了我这个错误:


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)

它并没有真正影响,代码运行良好,但只是为了它,我想不断改进,以便我了解在这种情况下发生了什么, 任何其他建议总是有帮助的!

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();
        }
    }
}
Java 数组 矩阵 输入 java.util.scanner

评论

0赞 Dawood ibn Kareem 9/29/2022
永远不要尝试关闭 ,因为关闭 会关闭(或尝试关闭)基础数据源。 应该保持打开状态。ScannerSystem.inScannerSystem.in

答:

0赞 rzwitserloot 9/29/2022 #1

它并没有真正影响,代码运行良好,但只是为了它,我想不断改进,这样我就可以了解在这种情况下发生了什么

Java 有 3 种不同类型的“资源”。“资源”是代表稀缺事物的对象。普通对象只占用一点点内存,你可以制作数百万个,你的系统有足够的RAM。无需担心“关闭”或“释放”普通对象,系统的垃圾收集器最终会找到它。

但是文件句柄?打开网络连接?数据库事务?不,这些要稀缺得多。这些是“资源”。但它们有 3 种口味,就“你必须关闭它们”而言:

1.实际重资源

new FileInputStream、 、 、 - 这些东西都会让你真正应该关闭的东西,否则就会发生崩溃。你可以制作一百万个对象,这并不重要——你可以制作另一个对象。但是您无法打开一百万个文件。不是因为内存限制,而是因为操作系统不允许你这样做。new Socketconnection.prepareStatementFiles.newInputStream

这就是警告的真正含义:你必须关闭这些东西。

这样做的方法是这样的:

try (var in = new FileInputStream(file)) {
  .. do stuff with `in` here
}

请注意,这是一种全有或全无的情况。资源必须关闭,但一旦关闭,它们就不再起作用。

这意味着必须有一些代码负责资源。该代码确保它不会“泄漏”,并且通过清楚地了解谁负责(与更自由放任的“任何完成它的人都应该立即关闭它”相比),您不会从仍在使用它的其他代码下“拉出地毯”。因此,关闭您负责的东西是非常糟糕的。

2. 轻量级

有些东西就像资源一样并不沉重。例如,可以像文件一样工作,但完全是内存中的事情。你可以制作一百万个BAIS对象,但绝对不关闭它们,这根本不重要。但是,它们看起来很像资源。很难分辨出区别。它被定义为“类 ByteArrayInputStream 扩展 InputStreamFileInputStream 扩展 InputStream”,那么如何区分呢?为什么 FIS 是您应该关闭的东西,而 BAIS 却不是?new ByteArrayInputStream, and

你可以调用轻量级,这本身不是问题。只是不值得警告。close()

你只需要知道。

3. 过滤器

Java 中很多资源式的东西都是过滤器。它们本身根本不代表一种资源,它们只是围绕着一种资源。 扫描仪本身只是管道的一部分:扫描仪本身不会生成数据或向操作系统请求文件句柄;它们包裹着重(或轻)资源。Scanner

java 的设置方式是,所有过滤器(、、等等)都将“通过”任何关闭调用:关闭过滤器,这也关闭了它们包装的东西。这很好(也许过滤器需要做一些冲洗和清洁),主要是,但使事情复杂化。BufferedInputStreamPrintWriterScannerCipherOutputStream

扫描仪确实使事情复杂化 - 扫描仪同时是所有 3 个。如果将它包裹在输入流(例如 )周围,它可以是一个过滤器,但您也可以将其称为传递文件,在这种情况下,扫描程序现在是必须关闭的重量级资源,您还可以传入要从中扫描的字符串常量,在这种情况下,它是轻量级的。new Scanner(System.in)

林特斯

“linting 工具”是一种检查代码并标记任何潜在问题的工具。“资源泄漏”是不好的(你创建了一个重量级资源,然后无法关闭它)很容易编程 - 它不仅仅是“你忘记了,而是:你没有在尝试块中完成它,因为如果代码永远不会到达,因为你//过去了,或者抛出了一个异常?合乎逻辑的是,人们会希望一个 linter 去找他们并告诉你:嘿,你这里有一个(潜在的)泄漏。in.close()in.close()returncontinuebreak

问题

鉴于扫描程序可以是 3 种不同类型中的任何一种,具体取决于您如何调用它,理想情况下,每个方法和构造函数都应该以某种方式标记:“I am as heavy/lightweight (and as responsible ) as this parameter”(filter),或“I am heavyweight”(扫描文件的 Scanner 构造函数),或“I am lightweight”(解析字符串的 Scanner 构造函数)。现在,linter 可以正常工作。

但是,方法不是这样标记的,所以扫描仪必须猜测。这意味着他们可能会弄错。

他们弄错了,在这里

大多数情况下,任何 [A] 具有类型为 、 、 或 的变量的代码,而 [B] 没有通过从参数获取该变量来获取该变量,则 [C] 负责关闭它。这通常是正确的。但并非总是如此。InputStreamOutputStreamReaderWriterSocketConnectionStatementResultSetScanner

System.in是你不应该关闭的东西。因此,您也不应该关闭您制作的任何过滤器来包装它。

结论:当 linting 工具告诉您关闭该扫描仪时,它是完全错误的(您根本不应该这样做。不,不)。然而,这是可以理解的,这是错误的,因为很难写出一个正确的,通常“你做了一个过滤器,这可能意味着你应该关闭它”是正确的。.close()try (

话虽如此,linter 工具作者:把你的东西放在一起。至少提高检测过滤器的能力是困难的,但并非不可能(即使这意味着只是手动枚举 java 核心库中的所有过滤器 - 或者启发式地,如果有人调用,你可以打赌它是一个过滤器),一旦你这样做了,弄清楚它们包装的东西是否应该关闭。如果没有,那么也不要抱怨过滤器。new Doohickey(expression-whose-type-is-a-resource)

那么,你在这里做什么

每当您制作扫描仪时,请不要关闭它。如果你的 linter 告诉你这是一个问题,知道它是错误的,并忽略它,或者将其配置为停止抱怨。System.in

它可以帮助将扫描仪作为参数传递。在方法的顶部制作 1 个扫描仪,然后传递它。这很好,因为如果你想使用其他任何东西作为你的应用程序的输入(可能是文件,或者网络连接),你可以在你的应用程序中设置一次,然后你的所有其他方法都会自动正常工作,而如果你一直在制作新的扫描仪,你就无法在不改变每个方法的情况下改变输入的来源。顺便说一句,现在你只有一个地方,linter 会错误地抱怨未能“关闭资源”。mainmain