14

I'm learning Java and working on some projects for fun. One issue that I have run in to is that when I use a Scanner object Eclipse warns me that:

Resource Leak: 'scan' is never closed.

So, I added a scan.close(); at the end of my code and that takes care of the warning.

The problem comes in because I have other classes in the same package that also use scanner objects and and Eclipse tells me to close scanner in those classes respectively. However, when I do that it seems like it closes ALL of the scanner objects and I get errors during run time.

Here is an example of what causes the error:

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();
    }
}

After scanner is closed in the scanTest class and the do loop in test2 is entered again an error occurs at the line test = scan.nextInt();

I tried moving the creation of the scanner object into the do loop just to make a new object every time as well but the error still occurs.

Not sure why this is happening or how I can make sure all my I/O objects are closed out without running into problems.

One post I came across mentioned that when System.in is closed I cannot be re-opened. If this is the case would I just need to make sure a scanner object with System.in is closed at the very end of the program and @suppress all of the other scanner warnings in other classes? Or would that still leave all those scanner objects open (bad)?

ROMANIA_engineer
  • 54,432
  • 29
  • 203
  • 199
SuperCow
  • 1,523
  • 7
  • 20
  • 32
  • There's a good discussion of this problem here: http://stackoverflow.com/questions/12519335/resource-leak-in-is-never-closed – azurefrog May 13 '14 at 02:05
  • 1
    I believe the trick is that you are only allowed one Scanner Object that ties into the System.in object. Therefore you need to structure your code to utilize this one Scanner object. – john p Oct 04 '17 at 21:00

3 Answers3

4

First, this is no memory leak.

Second, when you close a stream wrapper, the default implementation is for it to close the stream that it wraps. This means that the first time you close your Scanner (as it is written), yes, you close System.in.

In general, one would like to avoid closing System.in if they were meaning to read from System.in again. The best way to go about this depends on your program.

One might copy the information from System.in into a buffer of some sort and then scan the buffer. One might not close the Scanner, reusing it in other locations. One might even de-reference the Scanner for garbage collection and create multiple new Scanners on System.in.

These solutions are not all equivalent, some are considered much better than others; but, it all depends on the calling program. Experiment with a few, and if you run into a problem, open a new StackOverflow question where you show the relevant portions of your code, a description of the problem, the example input, and the wrong output (along with the desired output).

Good luck.

Edwin Buck
  • 69,361
  • 7
  • 100
  • 138
  • It's not a resource leak either. In fact, it doesn't leak anything. It's just a misunderstanding of expected behavior. – Edwin Buck May 13 '14 at 02:07
  • Thank you for the comments. If I don't close the Scanner(System.in) in a particular class but then make sure I do close System.in at the end of my program is that OK? Will all the other Scanner(System.in) objects that I was using in other classes then be closed as well? – SuperCow May 13 '14 at 02:20
  • You don't need to close `System.in` ... ever ... unless you are doing funky things that involve *changing* `System.in` using `System.setIn(...)`. When an application exits, the operating system automatically closes open file handles. You may lose data if the application has unflushed output buffers, but the OS doesn't care about that. – Stephen C Oct 20 '20 at 06:51
3

Yes, when you close a scanner you will be closing the underlying stream (in this case System.in). To avoid this, either create a global variable of scanner which can be used by all classes or have a central point for shutting down the scanner (just before the program exits would be ideal)

Scary Wombat
  • 44,617
  • 6
  • 35
  • 64
  • Using a global variable would probably be easy to use in my program, and then I could just close it right before the program exits. However, many of my teachers have suggested that using global variables if frowned upon. – SuperCow May 13 '14 at 02:16
  • IN that case consider the use of a public static class that holds the scanner object - achieves the same thing. – Scary Wombat May 13 '14 at 02:21
-1

Don't name all your scanners the same. If you have multiple in one thing like this:

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

}

Put the *scanner name goes here*.close(); for each one of your scanners. If they all have the same name then change the ones that do something different from and other scanner.

  • It doesn't matter if scanner buffer objects are named differently. Underlying stream is closed and still have the same behaviour. This is not an accurate suggestion or solution. – Sunil K-Standard Chartered Dec 24 '19 at 10:17