왜 알아야 하는가?
다음은 Java Documentation에 나와있는 Exception의 정의입니다.
위 정의에서 알 수 있듯이, 런타임에서 비정상적으로 흘러갈 수 있는 부분에 대해 Exception 처리를 해주어야 합니다.
코드를 작성할 때 예외 상황에 대해서 적절히 처리를 해주는 것은 중요합니다. 자바에서는 이러한 예외 처리를 해주기 위해 Exception이라는 Class가 존재합니다.
Exception의 계층구조

[사진 1]

[사진 2]
Exception Class는 Throwable을 상속받고, Throwable은 최상위 클래스인 Object를 상속받습니다.
Exception의 2가지 종류
- checked-Exception / unchecked-Exception
checked-Exception
Exception Class를 바로 상속받는 예외 클래스 입니다. 예시로 IOException Class가 checked-Exception인데, 이들은 컴파일 타임에 exception 처리를 하는 지 체크해주는 예외입니다. 즉, 예외처리를 강제하기 때문에 무조건 별도의 예외처리를 해주어야 합니다.
가령 시스템 입력을 받기위해 BufferedReader의 객체를 사용한다면, IOException 처리를 필수로 해주어야 합니다. IOException에 대한 예외 처리를 해주지 않는다면 컴파일 타임에 Error가 발생하며 다음 사진과 같습니다.



unchecked-Exception
Exception을 상속받는 RuntimeException Class가 존재하고 RuntimeException Class를 상속받는 예외들은 unchecked-Exception 입니다. 위와 달리 예외처리를 강제하지 않아서, 예외처리를 안해줘도 컴파일 타임에 Error가 발생하지 않습니다.
Exception의 흐름
다음과 같이 코드를 작성했다고 합시다.
class ExceptionThrown
{
static int divideByZero(int a, int b){
int i = a/b;
return i;
}
static int computeDivision(int a, int b) {
int res =0;
try {
res = divideByZero(a,b);
} catch (NumberFormatException ex) {
System.out.println("exception in computeDivision");
}
return res;
}
public static void main(String args[]){
int a = 1;
int b = 0;
try {
int i = computeDivision(a,b);
} catch (ArithmeticException ex) {
System.out.println("exception in main catch");
}
}
}
실행결과
exception in main catch.
divideByZero()가 호출되었을 때, b가 0이기 때문에 ArithmeticException이 발생합니다. 그러면 적절히 예외처리가 이루어질 수 있는 곳까지 호출했던 메소드를 거슬러 올라갑니다. 그림으로 나타내면 다음과 같습니다.

이러한 프로세스는 JVM의 기본 Exception Handling을 따릅니다. Exception 발생 시, Exception 객체를 생성해서 JVM에 넘기는데 그 객체 안에는 exception의 이름, 프로그램의 현재 상태, exception 발생 위치를 기준으로 호출된 메소드 리스트 (call stack 구조)가 포함되어 있습니다. 이러한 과정을 Exception을 던진다 (Throwing an Exception) 라고 표현 합니다.
던져진 Exception은 다음과 같은 과정을 거쳐 처리됩니다.
- call stack을 순회하며 적절한 exception handler(try-catch)를 찾습니다.
- 적절한 exception handler를 찾았다면 해당 handler로 exception을 넘깁니다.
- 적절한 exception handler란 던져진 exception과 매칭되는 exception 입니다.
- 모든 call stack을 순회한 후에도 적절한 handler를 찾지 못했으면 default exception handler가 실행됩니다.
- default exception handler는 exception 정보를 노출하며 해당 thread를 중지 시킵니다,
Exception Handling 효과
적절하게 exception handling을 한다면 다음과 같은 이점을 얻을 수 있습니다.
- Provision to Complete Program Execution
- Easy Identification of Program Code and Error-Handling Code
- Propagation of Errors
- Meaningful Error Reporting
- Identifying Error Types
Throwing an Exception
예외가 발생하면 다음과 같이 try-catch문을 통해 곧장 예외를 처리해줄 수 있습니다.

하지만 경우에 따라서 곧장 예외처리를 해주는 것이 아니라 호출한 메소드로 예외를 던질 수 있습니다. 호출한 메소드는 call stack에 저장되어 있으며 다음 2가지의 방법으로 예외를 던집니다.
throws

메소드 선언부에 위의 방식으로 사용할 수 있고 main() 함수 내에서 IOException이 발생하면 main()을 호출한 곳으로 exception을 던지게 됩니다.
참고로 throws 뒤에는 여러 개의 Exception을 던져줄 수 있습니다.
throw
public Object pop() {
Object obj;
if (size == 0) {
throw new EmptyStackException();
}
obj = objectAt(size - 1);
setObjectAt(size - 1, null);
size--;
return obj;
}
위 코드는 java docs에서 직접 가져온 코드 입니다. throw 키워드는 위와 같이 사용합니다.
[출처]
https://docs.oracle.com/javase/tutorial/essential/exceptions/index.html
https://docs.oracle.com/javase/tutorial/essential/exceptions/throwing.html
https://www.geeksforgeeks.org/exceptions-in-java/