📌 throwable
- Java는 문제 상황을 알리는 타입(throwable)으로 3가지를 제공한다.
- 검사 예외
- 비검사 예외(Runtime 예외, Error)
- 어느정도 참고할만한 지침을 따르는 것이 좋다.
📌 검사 예외
💡 호출하는 쪽에서 복구하리라 여겨지는 상황이라면 검사 예외를 사용하라
- 기본적으로 "복구할 수 있는 조건"이라는 전제여야 한다.
- 검사예외를 던지면 호출자가 해당 예외를 catch로 잡아 처리하거나, 더 바깥으로 전파하도록 강제하게 된다.
- 사용자가 예외를 잡기만 하고 별다른 조취를 취하지 않을 수 있는데 좋지 않은 생각이다. (Item 77)
- 따라서 메서드 선언에 포함된 검사 예외 각각은 해당 메서드를 호출했을 때 발생할 수 있는 유력한 결과임을 API 사용자에게 알려준다.
📌 비검사 예외
- Runtime 예외, Error 둘 다 동작 측면에서는 다르지 않다.
- 프로그램에서 잡을 필요가 없거나 혹은 통상적으로 잡지 말아야 한다.
- 복구가 불가능하거나, 더 실행해봐야 득보다는 실이 많은 경우에 비검사 예외를 던지기 때문이다.
- throwable을 잡지 않은 thread는 적절한 오류 메시지를 내뱉으며 중단된다.
1️⃣ Runtime 예외
💡 프로그래밍 오류를 나타낼 때는 런타임 예외를 사용하라.
- Runtime 예외 대부분은 전제조건을 만족하지 못했을 때 발생한다.
- 복구 가능하다고 믿는다면 검사 예외를 사용하라.
- 확신할 수 없다면 비검사 예외를 선택하는 편이 나을 것이다. (Item 71)
✒️ 확신할 수 없는 경우
자원 고갈로 인한 에러가 발생했을 때 원인이 무엇일지 파악할 수 있을까?
정말 말도 안 되는 크기의 배열을 할당해 생긴 프로그래밍 오류일 수도 있다.
하지만 진짜 순간적으로 자원이 부족해서 불가능했을 수도 있다.
후자의 경우에는 충분히 복구할 수 있는 상황일 것이다.
문제는 이렇게 복구할 수 있는 상황인지 프로그래밍 오류인지 항상 명확히 구분되지는 않는다는 점이다.
해당 자원 고갈 상황이 복구될 수 있는 것인지는 API 설계자의 판단에 달려있다.
2️⃣ 에러(Error)
💡 구현하는 비검사 throwable은 모두 RuntimeException의 하위 클래스여야 한다.
- 보통 JVM이 자원 부족, 불변식 깨짐 등 더 이상 수행을 계속할 수 없는 상황을 나타낼 때 사용한다.
- Error 클래스를 상속해 하위 클래스를 만드는 일은 자제하라.
- Java 언어 명세가 요구하지는 않지만, 업계에 널리 퍼진 규약이다.
- Error는 상속하지 말아야 할 뿐 아니라, throw 문으로 직접 던지는 일도 없어야 한다.
✒️ 검사 예외도 아니고 Runtime 예외도 아닌 throwable
Exception, RuntimeException, Error를 상속하지 않는 throwable을 만들 수도 있다.
암묵적으로 Exception 하위 클래스 중 RuntimeException을 상속하지 않은 일반적인 검사 예외처럼 다룬다.
하지만 이로울 게 없으니 절대로 사용하지 마라
throwable은 정상적인 검사 예외보다 나을 게 하나도 없으면서 API 사용자를 헷갈리게 할 뿐이다.
📌 예외 클래스 접근자
- Exception 또한 어떤 메서드라도 정의할 수 있는 완벽한 객체다.
- Exception의 메서드는 주로 예외를 일으킨 상황에 관한 정보를 코드 형태로 전달할 때 쓰인다.
- 이런 메서드가 없으면 프로그래머가 오류 메시지를 파싱해야 하는데, 이는 나쁜 습관이다.
- throwable 클래스들은 대부분 오류 메시지 포맷을 상세히 기술하지 않는다.
- JVM이나 릴리즈에 따라 포맷이 달라질 수 있기 때문이다.
- 문자열 파싱으로 얻은 코드는 깨지기 쉽고, 실행 환경에 크게 영향을 받는다.
- 검사 예외의 경우, 예외 상황에서 벗어나는 데 필요한 정보를 알려주는 메서드를 함께 제공하라. (Item 75)
public class CustomExceptionDemo {
// 사용자 정의 예외
public static class CustomException extends Exception {
private int errorCode;
public CustomException(int errorCode, String message) {
super(message);
this.errorCode = errorCode;
}
public int getErrorCode() {
return errorCode;
}
}
public static void main(String[] args) {
try {
// 예외 발생
throw new CustomException(404, "Resource not found");
} catch (CustomException e) {
// 잡은 예외를 사용해 오류 정보 출력
System.out.println("Error Code: " + e.getErrorCode());
System.out.println("Error Message: " + e.getMessage());
}
// 오류 메시지 파싱 방식 (권장되지 않는 방식)
String errorMessage = "Error 404: Resource not found";
String[] parts = errorMessage.split(" ");
try {
int parsedErrorCode = Integer.parseInt(parts[1]);
System.out.println("Parsed Error Code: " + parsedErrorCode);
} catch (Exception ex) {
System.out.println("Failed to parse error message.");
}
}
}