[Scanner] java.util.NoSuchElementException

HOSEON YOO·2023년 10월 4일
1

개요

  • JDK 8

Exception in thread "main" java.util.NoSuchElementException

public static void main(String[] args) {
    add();
    sub();
}
public static void add() {
	Scanner scanner = new Scanner(System.in);
    int number1 = scanner.nextInt();
    int number2 = scanner.nextInt();
    System.out.println(number1 + number2);
    scanner.close();
}
public static void sub(){
	Scanner scanner = new Scanner(System.in);
    int number1 = scanner.nextInt();
    int number2 = scanner.nextInt();
    System.out.println(number1 - number2);
    scanner.close();
}

자주 사용하는 Scanner 에대해 잘 알고 있다고 생각했다. 위의 코드를 작성하고 당연히 잘 실행될 줄 알았는데 에러가 발생했다.. 디버깅을 해보니 add() 실행 후 sub() 에서 int number1 = scanner.nextInt(); 행에서 에러가 발생했다. Scannerclose() 실행 후에 System.in 도 같이 닫아버려서 생기는 문제인 것 같았다.

궁금해서 추적한 Scanner의 close()

close() 내부 로직

public void close() {
    if (closed)
    return;
    if (source instanceof Closeable) {
        try {
            ((Closeable)source).close();
        } catch (IOException ioe) {
            lastException = ioe;
        }
    }
	sourceClosed = true;
	source = null;
	closed = true;
}

closedsource 의 값에 따라 결과 값이 달라진다. 그렇다면 closedsource 값이 무엇인지 Scanner 를 추적해보자.

source, closed 초기값

public final class Scanner implements Iterator<String>, Closeable {
...생략
// The input source
private Readable source;
...생략
// Boolean indicating if this scanner has been closed
private boolean closed = false;
...생략
}

closed = false로 초기화된다.
source는 다른 곳에서 초기화되는 것 같다. Scanner의 생성자를 추적해보자.

new Scanner(InputStream source)

public Scanner(InputStream source) {
	this(new InputStreamReader(source), WHITESPACE_PATTERN);
}

this(new InputStreamReader(source), WHITESPACE_PATTERN)

private Scanner(Readable source, Pattern pattern) {
    ...생략
    this.source = source;
    ...생략
}

InputStreamReader 상속관계

public class InputStreamReader extends Reader

public abstract class Reader implements Readable, Closeable

public interface Readable

Scanner scanner = new Scanner(System.in); 로 인해 Readable source = new InputStreamReader(System.in); 로 초기화되고 scanner.close() 를 실행하게 되면 인자로 받은 System.inclose 하게 된다.

결과적으로 System.in.close() 를 실행하면 표준 입력 스트림이 닫히고, 이후에는 System.in 을 통해 사용자 입력을 받을 수 없게 된다.

그렇다면 언제 close()를 써야할까?

공식문서에서는 아래와 같이 설명한다.

  • 스트림에는 BaseStream.close() 메서드가 있고 AutoCloseable을 구현하지만 거의 모든 스트림 인스턴스는 사용 후 실제로 닫을 필요가 없다.
  • 일반적으로 소스가 IO 채널(예: Files.lines(Path, Charset)에서 반환된 스트림)인 스트림만 닫아야 합니다.

참고자료

profile
안녕하세요~ 👋, 대한민국 개발자 유호선입니다.

0개의 댓글