사용자 입력, 네트워크 통신 등 미리 알 수 없거나
예상하지 못한 작업으로 인해 프로그램에 오류가 발생할 수 있다
이 때 프로그램 전체 흐름을 방해하지 않으면서 오류를 처리할 수 있다
자바는 프로그램 실행 중 발생할 수 있는 예상치 못한 상황인
예외를 처리하기 위한 매커니즘을 제공한다
이는 프로그램 안정성과 신뢰성을 높이는 데 중요한 역할을 한다
위 키워드와 예외를 다루기 위한 예외 처리용 객체들을 제공한다
체크 예외는 발생한 예외를 개발자가 명시적으로 처리해야만 한다 (컴파일 오류 발생)
언체크 예외는 개발자가 발생한 예외를 명시적으로 처리하지 않아도 프로그램을 실행할 수 있다
상속 관계에서 다형성으로 인해 부모 타입은 자식 타입을 담을 수 있다
예외도 객체이기 때문에 그대로 적용된다
때문에 상위 예외를 잡으면 하위까지 함께 잡히게 된다
따라서 애플리케이션 로직에서 Throwable 예외를 잡으면 안된다 (Error 예외도 함께 잡히기 때문)
우리가 다루는 애플리케이션 로직에서는 Exception 부터 관리하면 된다
런타임 예외를 제외한 Exception 과 그 하위 예외
체크 예외의 체크란 컴파일러가 체크하는 것을 말한다
체크 예외는 모두 잡아서 처리하거나 밖으로 던지도록 처리해야 한다
Exception 을 상속 받은 예외는 체크 예외가 된다
public class CheckedException extends Exception {
public CheckedException(String message) {
super(message); // Exception 의 메시지를 보관하는 기능 사용
}
}
public class Client {
public void call() throws CheckedException {
// 에러를 내부에서 직접 처리하지 않을 거라면 throws CheckedException 을 선언함으로 예외 발생 시 외부로 던질 것임을 알려야 한다
throw new CheckedException("ex");
}
}
public class Service {
Client client = new Client();
public void callCatch() {
// 예외를 잡아서 처리
try {
client.call();
} catch (CheckedException e) {
sout("예외 처리 메시지 = " + e.getMessage());
}
sout("정상 흐름")
}
public void callThrow() throws CheckedException {
// 예외를 외부로 던진다
client.call();
}
}
Throwable 예외 객체에는 String detailMessage 필드를 통해
메시지를 보관할 수 있는 기능이 있다
getMessage() 메서드로 조회 가능
체크 예외는 직접 처리하지 못한다면 throws 를 선언해 밖으로 던져줘야만 한다
그렇지 않으면 컴파일 오류 발생
예외를 직접 잡거나 던지지 않아도 되지만
아무것도 하지 않으면 자동으로 외부에 던진다
언체크 예외에서는 throws 를 주로 생략하지만
중요한 코드의 경우 생략하지 않고 IDE 를 통해 예외를 발생하는 사실을 인지할 수 있다
throws 를 선언한다고 컴파일러가 체크를 하진 않는다
자바에서는 예외의 모든 경우에서 호출하는 finally 기능을 제공한다
주로 try 에서 사용했던 자원 회수 시 사용된다
예외를 단순 오류 코드로 분류하는 것이 아니라
계층화하여 처리하면 더 세밀하게 예외를 다룰 수 있다
예외도 객체이기 때문에 부모 예외를 잡으면 하위 예외 모두를 잡을 수 있고
자식 예외만 따로 처리할 수도 있다
이를 이용해 예외를 계층화 하고 세분화 할 수 있다
작은 예외부터 순차적으로 잡으면 예외를 세밀하게 다룰 수 있다
데이터베이스나 네트워크 장애 등 시스템 오류에 의해 발생한 예외들은
대부분 잡아도 해결할 수 있는 것이 거의 없다
웹이라면 고객에게는 오류 페이지를 보여주고
내부 개발자가 빠르게 상황을 인지할 수 있도록 로그를 남겨두어야 한다
체크 예외는 컴파일러가 예외들을 체크해주기 때문에 많이 사용되었었지만
프로그램이 점점 복잡해지면서 처리할 수 없는 예외가 많아져
체크 예외를 사용하는 것이 부담스러워졌다
모든 체크 예외는 Exception 의 자식이기 때문에
Exception 을 던지면 된다는 생각을 해볼 수 있다
하지만 이렇게 부모 예외를 던지게 되면
자식 체크 예외들의 기능이 무효화되고
중요한 체크 예외 또한 전부 놓치게 된다
throws Exception 은 Exception 을 포함한 모든 에러를
한 번에 던지는 것이기 때문에 좋지 않다
이런 부분 때문에 주로 언체크 예외를 사용한다
런타임 예외는 throws 를 강제하지 않고
본인이 필요한 예외만 잡으면 되기 때문에 훨씬 간결해진다
처리할 수 없는 예외들은 여러 곳에서 나누어 처리하기 보다는
공통으로 처리하는 편이 좋다
어짜피 해결할 수 없다면 사용자에게 서비스에 문제가 있음을 알리고
다른 개발자들을 위해 로그를 남긴다
로그를 남기는 것은 주로 Slf4j, logback 같은 별도의 라이브러리를 사용해
콘솔과 특정 파일에 함께 결과를 출력한다
애플리케이션에서 외부 자원을 사용하는 경우 반드시 해제해야 한다
따라서 finally 구문을 반드시 사용해야 한다
try-with-resources 는 try 에서 외부 자원을 사용 후
바로 반납하도록 구현할 수 있다
이 기능을 사용하기 위해 먼저 AutoCloseable 인터페이스를 구현해야 한다
package java.lang;
public interface AutoCloseable {
void close() throws Exception;
}
이 인터페이스를 구현하면 try with resources 사용 시
try 가 끝나는 시점에 close() 가 자동 호출된다
close() 를 오버라이딩 하여 종료 시점에 해야할 행동을 작성하면 된다
try 사용 시에는 아래와 같이 작성한다
try (Resource resource = new Resource()) { // 사용할 리소스 객체를 받는다
// 리소스 사용 코드
}