모든 에러, 예외 클래스의 부모 클래스
절대 발생해서는 안되는 && 비정상적 && 심각한 문제.
보통 개발자가 처리할 수 없는 문제(시스템 자체의 문제)가 발생했을 때 사용된다.
ex. StackOverflowError, OutOfMemoryError
일반적인 개발 과정에서 충분히 발생할 수 있는 문제
Checked Exception과 Unchecked Exception(RuntimeException)으로 나뉘어진다.
공식문서에서 reasonable한 경우라고 적혀 있는데 일반적인 개발 과정이라고 의역했습니다. 혹시 잘못된 설명이라면 지적 부탁드립니다.
Exception의 자식 중 RuntimeException을 제외한 모든 예외를 CheckedException이라고 한다. 어디에선가 해당 예외를 catch해서 처리하는 걸 강제한다. 만약 처리하지 않으면 컴파일 오류가 발생한다.
예외가 발생한 스코프에서 해당 예외를 catch하지 않는다면 throws를 명시해서 부모 스코프로 던져줘야 한다.
컴파일 과정에서 검사할 수 없는 예외이다. 컴파일 과정에서 오류를 발생시키지 않기 때문에 명시적으로 throw를 하지 않아도 된다. 런타임 중 해당 예외 발생 시, catch해주는 로직이 나타날 때까지 부모 스코프를 타고 올라간다.
예외 발생 시 해당 문제를 복구할 수 있다면
Checked Exception이 권장된다.
그게 아니라면 Unchecked Exception이 권장된다.
해당 개념은 예시를 통해 보면 쉽게 이해할 수 있다고 생각한다.
보통 RuntimeException은 개발자가 로직을 잘못 작성했거나, 입력값이 잘못되었거나 하는 상황에서 발생된다. 이런 에러가 발생했을 때, 예외문에서 복구를 위해 무얼 할 수 있겠는가.. 그냥 이런 문제가 발생했다는 정보만 전달하면 된다. 추가적인 처리를 할 필요가 없다면 굳이 해당 예외를 명시할 이유가 없다.
예약어 try와 함께 쓰이며, 예외를 처리하기 위해 사용한다.
try {
...
} catch(throwable1) {
...
} catch(throwable2) {
...
}
try 블록 내부에서 명시해둔 throwable가 발생하면, 해당 catch 블록으로 코드의 흐름이 이동한다.
catch 시에는 순서를 신경써라.
catch는 선언되어 있는 순서대로 발생한다. 만약 부모 예외를 먼저 catch하고 이후에 자식 예외를 catch한다면, 자식 예외는 영영 catch가 불가능하다.
위에 선언된 부모 예외를 catch하는 부분에서 모든 자식 예외를 처리하기 때문이다.
try 블록에서 어떤 상황에서도 실행되야 하는 로직이 있다면, finally 블록에 작성된 내용들이 실행된다.
finally는 resource의 정리 목적으로만 사용하는 걸 권장한다고 한다.
만약 finally 블록에 비즈니스 로직을 넣게 되면 우리가 예측한대로 흘러가지 않을 수 있다. 예를 들어, finally 블록 내부에서 return문을 넣는 건 안티 패턴이다. try나 catch에서 리턴한 내용을 무시하고 finally에서 return한 내용을 반환하기 때문이다.
사족을 하나 붙이면, 예전에 인터셉터의 afterCompletion 메서드가 어떻게 동작하는지 궁금해서 코드를 살펴봤던 적이 있다.
그 때, DispatcherServlet의 try문 마지막에 afterCompletion 메서드가 들어가 있었고, catch 블록의 마지막에서도 afterCompletion를 호출하는 걸 확인할 수 있었다.
당시에 나는 afterCompletion는 정상 경우에도, 예외 경우에도 무조건 실행되는 메서드인데 왜 finally에 넣지 않은걸까 궁금했던 적이 있는데 이러한 이유인걸까 궁금해진다.
(확인해보고 싶으시면 DispatcherServlet 클래스에서 triggerAfterCompletion 를 검색해보세요)
https://docs.oracle.com/en/java/javase/11/docs/api/index.html
https://wikidocs.net/229#_2
https://simgee.tistory.com/27