백엔드 개발자라면 대답해야 할 100가지 질문

6. throw와 throws의 차이는 무엇인가요?

ignuy 2023. 7. 25.

자바에서 프로그램이 비정상 종료하는 가장 대표적인 경우는 Error를 만나는 경우와 Exception을 만나는 경우이다. Exception을 만나는 경우 에러를 처리하는 복구 로직을 통해 프로그램의 비정상 종료를 막고 원활한 서비스를 돕는다. (물론 Exception Handling의 의미는 훨씬 깊고 넓다. 언급하지 않은 의미가 훨씬 많으니 “Exception”에 대해서는 따로 공부하자.)

throw

예외를 고의로 발생시키는 역할을 한다.

public class File {
    ...
    public File(String pathname) {
        if (pathname == null) {
            throw new NullPointerException(); // 예외 던지기
        }
        this.path = fs.normalize(pathname);
        this.prefixLength = fs.prefixLength(this.path);
    }
    ...
}

위 File 객체는 생성자로 String 타입의 경로를 받고 경로가 null이라면 NullPointerException을 던진다. 예외가 발생했다면 내부적으로 try{} catch{} 구문을 구현하여 예외처리를 직접 하지 않는 한 실행을 중단하고 상위 블럭에 throws를 언급한 곳으로 찾아가 발생한 예외를 얹어 올린다.

throws

예외를 상위 메소드(콜러)로 던지는 역할을 한다.

public FileInputStream(File file) throws FileNotFoundException {
    String name = (file != null ? file.getPath() : null);
    SecurityManager security = System.getSecurityManager();
    if (security != null) {
        security.checkRead(name);
    }
    if (name == null) {
        throw new NullPointerException();
    }
    if (file.isInvalid()) {
        throw new FileNotFoundException("Invalid file path");
    }
    ...
}

FileInputStream 생성자로 들어온 File 객체 파라미터가 유효하지 않다면 FileNotFoundException을 throw한다.

public class Main {

    public static void main(String[] args) throws FileNotFoundException {
        FileInputStream fs = new FileInputStream(new File("test")); // 예외 발생!
    }
}

던져진 예외는 생성자를 호출했던 상위 메소드(콜러)로 넘어가 다시 예외를 발생시킨다. 상위 메소드에서도 try~catch 구문이 없다면 다시 throws에 예외를 얹어 상위메소드로 예외를 던진다.

 

이렇게 메소드, 생성자의 선언부 끝에 예외를 선언함으로 메서드를 실행했을 때 발생하는 예외에 대해 개발자가 손쉽게 알아볼 수 있는 역할도 수행한다.

  + 위 File() 생성자는 throws를 하지 않는데요?

컴파일러에 의해 탐지되는 checkedException(Exception, IOException…)은 throws를 반드시 명시해주어야 하지만 탐지되지 않는 uncheckException(RuntimeException…)은 명시해주지 않아도 된다. NullPointerException은 RuntimeException을 상속받고 있는 uncheckedException에 해당하여 throws를 명시하지 않아도 컴파일 에러가 발생하지 않고 FileNotFoundException은 IOException을 상속받고 있는 checkedException에 해당하여 throws를 명시하지 않으면 컴파일 에러가 발생한다.

  + main에서 발생한 예외는 어디로 던지죠?

이 경우 JVM이 main에서 던진 예외를 받고 단순히 에러를 출력하는 형태로 간단한 try~catch구문을 수행한다. 다만 프로그램 사용자는 알 수 없는 예외에 대해 JVM에게 처리를 위임하는 것은 프로그램 안정성과 향후 유지 보수에 악영향을 미치므로 try~catch 구문을 통해 main에서 예외처리를 마무리하는 것을 장려한다.

댓글