【问题标题】:Java -- Closing Scanner and Resource LeakJava——关闭扫描器和资源泄漏
【发布时间】:2014-06-30 13:28:35
【问题描述】:

我正在学习 Java 并从事一些有趣的项目。我遇到的一个问题是,当我使用 Scanner 对象时,Eclipse 会警告我:

资源泄漏:“扫描”从未关闭。

所以,我在代码末尾添加了一个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 的扫描仪对象在程序的最后关闭并@suppress 其他类中的所有其他扫描仪警告?或者这仍然会使所有这些扫描仪对象保持打开状态(坏)?

【问题讨论】:

  • 这里有一个很好的讨论这个问题:stackoverflow.com/questions/12519335/…
  • 我相信诀窍是只允许您使用一个与 System.in 对象相关联的 Scanner 对象。因此,您需要构建代码以利用这一 Scanner 对象。

标签: java eclipse java.util.scanner


【解决方案1】:

首先,这不是内存泄漏。

其次,当您关闭流包装器时,默认实现是关闭它所包装的流。这意味着当你第一次关闭你的 Scanner 时(正如它所写的那样),是的,你关闭了 System.in。

一般来说,如果要再次从 System.in 读取,则希望避免关闭 System.in。解决此问题的最佳方法取决于您的程序。

可以将 System.in 中的信息复制到某种缓冲区中,然后扫描缓冲区。人们可能不会关闭扫描仪,而是在其他位置重复使用它。甚至可以取消对垃圾收集的 Scanner 的引用,并在 System.in 上创建多个新的 Scanner。

这些解决方案并非都是等效的,有些被认为比其他解决方案好得多;但是,这一切都取决于调用程序。尝试一些,如果遇到问题,请打开一个新的 StackOverflow 问题,在其中显示代码的相关部分、问题描述、示例输入和错误输出(以及所需的输出)。

祝你好运。

【讨论】:

  • 也不是资源泄漏。事实上,它不会泄漏任何东西。这只是对预期行为的误解。
  • 感谢 cmets。如果我不关闭特定类中的 Scanner(System.in) 但确保在程序结束时关闭 System.in 可以吗?我在其他类中使用的所有其他 Scanner(System.in) 对象也会被关闭吗?
  • 您不需要关闭System.in ... 永远... 除非您正在使用System.setIn(...) 进行涉及更改 System.in 的时髦事情。当应用程序退出时,操作系统会自动关闭打开的文件句柄。如果应用程序有未刷新的输出缓冲区,您可能会丢失数据,但操作系统并不关心这一点。
【解决方案2】:

是的,当您关闭扫描仪时,您将关闭底层流(在本例中为 System.in)。为了避免这种情况,要么创建一个可以被所有类使用的扫描仪的全局变量,要么有一个中心点来关闭扫描仪(在程序退出之前是理想的)

【讨论】:

  • 在我的程序中使用全局变量可能很容易,然后我可以在程序退出之前将其关闭。但是,我的许多老师建议如果不赞成使用全局变量。
  • 在这种情况下,考虑使用保存扫描仪对象的公共静态类 - 实现相同的目的。
【解决方案3】:

不要为所有扫描仪命名相同。如果你有多个这样的事情:

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

}

为您的每台扫描仪输入*scanner name goes here*.close();。如果它们都具有相同的名称,则更改与其他扫描仪执行不同操作的名称。

【讨论】:

  • 扫描器缓冲区对象的命名是否不同并不重要。基础流已关闭并且仍然具有相同的行为。这不是一个准确的建议或解决方案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-08-16
  • 1970-01-01
  • 2016-06-06
  • 2021-03-14
  • 2012-10-23
相关资源
最近更新 更多