Java -- 关闭Scanner和资源泄漏

14

我正在学习Java,并且为了好玩而做一些项目。 我遇到的一个问题是,当我使用 Scanner 对象时,Eclipse会警告我:

资源泄漏:'scan'从未关闭。

因此,我在代码末尾添加了 scan.close(); ,这样就解决了警告问题。

问题出在,我在同一个包中有其他使用扫描器对象的类,Eclipse告诉我在这些类里面分别关闭扫描器。但是,当我这样做时,似乎关闭了所有的扫描器对象,导致运行时出现错误。

以下是导致错误的示例:

import java.util.Scanner;
public class test2 {
    public static void main(String [] args) {
        Scanner scan = new Scanner(System.in);
        int test = 0;
        do {    
            //Do stuff
            test = scan.nextInt();
            System.out.println(test);

            scanTest scanTest = new scanTest();
            scanTest.test();
        } while (test != 0);

        scan.close();       
    }
}

import java.util.Scanner;
public class scanTest { 
    public void test() {
        Scanner scanner = new Scanner(System.in);
        int blah = scanner.nextInt();
        System.out.println(blah);
        scanner.close();
    }
}

scanTest类中关闭扫描器后,进入test2的do循环后,在test = scan.nextInt();这行代码处发生错误。

我尝试将扫描器对象的创建移动到do循环中,以便每次都创建一个新对象,但仍然会出现错误。

不确定为什么会发生这种情况,也不知道如何确保关闭所有的I/O对象而不会遇到问题。

我看到的一篇帖子提到当关闭System.in时,它不能重新打开。如果是这样,那我只需要在程序的最后确保关闭使用了System.in的扫描器对象,并忽略其他类中的所有扫描器警告吗?还是这会使得所有这些扫描器对象都保持打开状态(不好)?


这个问题在这里有一个很好的讨论:https://dev59.com/l2cs5IYBdhLWcg3w3HoG - azurefrog
1
我相信诀窍在于你只允许使用一个绑定到System.in对象的Scanner对象。因此,你需要构建你的代码来利用这个Scanner对象。 - john p
3个回答

4
首先,这不是内存泄漏。
其次,当您关闭流包装器时,默认实现是关闭它所包装的流。这意味着第一次关闭Scanner(按照当前写法),是的,您会关闭System.in。
通常情况下,如果您想再次从System.in中读取数据,则需要避免关闭System.in。最佳方法取决于您的程序。
您可以将信息从System.in复制到某种缓冲区中,然后扫描该缓冲区。您可以不关闭Scanner,在其他位置重用它。您甚至可以取消Scanner的引用以进行垃圾收集,并在System.in上创建多个新Scanner。
这些解决方案并不完全等效,有些被认为比其他方案更好;但是,这完全取决于调用程序。尝试几个解决方案,如果遇到问题,请在新的StackOverflow问题中展示代码的相关部分,问题描述,示例输入以及错误输出(以及期望的输出)。
祝你好运。

这也不是资源泄漏。事实上,它没有泄漏任何东西。这只是对预期行为的误解。 - Edwin Buck
感谢您的评论。如果我在特定类中没有关闭Scanner(System.in),但确保在程序末尾关闭了System.in,那么这样做可以吗?那么我在其他类中使用的所有Scanner(System.in)对象是否也会被关闭? - SuperCow
除非您正在使用System.setIn(...)更改System.in,否则您永远不需要关闭System.in。当应用程序退出时,操作系统会自动关闭打开的文件句柄。如果应用程序具有未刷新的输出缓冲区,则可能会丢失数据,但操作系统不关心这一点。 - Stephen C

3

是的,当您关闭扫描仪时,您将关闭基础流(在此情况下为System.in)。为了避免这种情况,请创建一个全局扫描仪变量,可以被所有类使用,或者有一个集中的点来关闭扫描仪(程序退出之前最理想)。


在我的程序中使用全局变量可能很容易,然后我可以在程序退出之前关闭它。然而,我的许多老师建议不要使用全局变量。 - SuperCow
在这种情况下,考虑使用一个公共静态类来持有扫描器对象 - 可以实现相同的功能。 - Scary Wombat

-1

不要给所有的扫描仪都取同样的名字。如果你在一个东西中有多个扫描仪,就像这样:

import java.util.Random;
import java.util.Scanner;

public class DayThree {

    public static void main(String[] args) {

        **Scanner textScanner = new Scanner(System.in);**

        // boolean operands 
            //    String(or objects)   .equals()      "this".equals("that")     false
            //    primitive data types     ==          'a'=='a'   ->  true   5==6   false
            //                             !=          'a'!='a'   ->  false  5!=6   true
            //                             !            !(true)   ->  false   !(false)   true
            //                             >            5 > 4     -> true    'a' > 'b'  false
            //                              <           5 < 4     -> false
            //                              <=          
            //                              >=    
            //       &&     ->  and        5 < 6 &&  7 > 10   -> false
                        // if either side of and is false the outcome is false
            //       ||     ->  or        5 < 6  || 7 > 10     -> true
                        // if either side of or is true  the outcome is true
        //System.out.println(!(5 < 10) && (7>3) ||  (true && false || true));

        /*     <-- this is a multi line comment
            System.out.println("What is the most amazing show on tv this week? ");
            String show = textScanner.nextLine().toLowerCase();  //this is case sensitive

            show = show.toLowerCase(); // changes the strng to a lowercase version
            show = show.toUpperCase(); 

            if(show.equalsIgnoreCase("game of thrones")){ // .equalsIgnoreCase( ignores caps/lower)
                System.out.println("Yes it is!");
            }
            else{
                System.out.println("You are wrong.");
                System.out.println(show + " is clearly inferior to Game of Thrones.");
            }

            System.out.println("Who is your favorite character in " + show + ".");
            String character = textScanner.nextLine().toLowerCase();

            if(character.contains("dragon")){
                System.out.println("CGI magic is so cool!");
            }
            else if(character.contains("lanister")){
                System.out.println("Wrong house.");
            }
            else{
                System.out.println(character + "is pretty cool I guess...");
            }
        */ 
//      asdf      alternate multi line comment use ctrl + /  on highlighted text. 
//                      doing this a second time undoes the comment 
//      sdaf
//      asdf
//      asdf
//      asdf

//      1.  ask about favorite something (pet)
//      2. save that into a string all lowercase
//      3. have a series of if else (x3) and else statements about the something

        //NOTE: DO NOT END CONDITIONALS WITH ; example: if(boolean);  IS WRONG. 
        **Scanner numScanner = new Scanner(System.in);** // the variable tells you what to use it for
        Random rand = new Random();  // this makes a new random object 
        System.out.println("Pick a number.");
        int num = numScanner.nextInt();
        int sNum = rand.nextInt(9) + 1;  // gives me a random num between 1-10
                                        // nextInt(bound)gives you a num from 0-bound
                                        //adding one gives you a num from 1 - bound + 1
        if(num > sNum){
            System.out.println("Too high");
            System.out.println("The number was " + sNum);
        }
        else if(num < sNum){
            System.out.println("Too low");
            System.out.println("The number was " + sNum);
        }
        else{
            System.out.println("Wow are you psychic? ");
        }
        textScanner.close();
        numScanner.close();
    }//main method

}

*扫描仪名称在此处* .close();放置在每个扫描仪上。如果它们都有相同的名称,则更改那些与其他扫描仪不同的扫描仪。

扫描器缓冲区对象的名称不重要。底层流已关闭,仍具有相同的行为。这不是一个准确的建议或解决方案。 - Sunil K-Standard Chartered

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接