우리가 사전에 체크한 Exception
우리가 예외 처리를 안 해 놓으면 컴파일 시 오류 발생 (try-catch로 감씨줬는지 확인)
그런 게 안 되어 있는 것
런타임 때 발생하기 때문에 예측 불가
시작은 모든 객체의 원형인 Object
클래스에서 시작
“문제 상황”을 뜻하는 Throwable
클래스가 Object 클래스를 상속
Throwable
클래스의 자식으로 앞서배운 에러(Error
)와 예외(Exception
) 클래스 존재
에러(Error
) 클래스와 예외(Exception
) 클래스는 각각 IOError
클래스, RuntimeException
클래스와 같이 구분하여 처리
RuntimeException을 상속한 예외들은 UncheckedException
상속하지 않은 예외들은 CheckedException으로 구현되어 있음
Checked Exception에 속하는 에러 구현체들은 핸들링 하지 않으면 컴파일 에러가 발생하는 대신, 컴파일이 됐다면 100% 복구가 가능한 에러
연결된 예외
예외는 다른 예외를 유발할 수 있음
원인 예외를 새로운 예외에 등록한 후 다시 새로운 예외를 발생시키는데, 이를 예외 연결이라고 함
❓ 왜 연결할까 ❓
여러 가지 예외를 하나의 큰 분류의 예외로 묶어서 다루기 위함(어떤 특정한 문제에 대해서 예외 상황이 발생했을 때 이 예외 상황의 원인이 하나가 아니라 여러개일수도 있기 때문에)
checked exception을 unchecked exception으로 포장(Wrapping)하는데 유용하게 사용(코드를 줄이기 위해서)
원인 예외를 다루기 위한 메소드
initCause()
: 지정한 예외를 원인 예외로 등록하는 메소드getCause()
: 원인 예외를 반환하는 메소드throw new RuntimeException(new Exception("이것이 진짜 예외 이유 입니다."));
=new Exception을 감싸서 RuntimeException안에 넣음으로써(chaining) 한 줄로 끝냄
즉, checked exception을 감싸서 unchecked exception안에 넣음
// 연결된 예외
public class main {
public static void main(String[] args) {
try {
// 예외 생성
NumberFormatException ex = new NumberFormatException("가짜 예외이유");
// 원인 예외 설정(지정한 예외를 원인 예외로 등록)
ex.initCause(new NullPointerException("진짜 예외이유"));
// 예외를 직접 던집니다.
throw ex;
} catch (NumberFormatException ex) {
// 예외 로그 출력
ex.printStackTrace();
// 예외 원인 조회 후 출력
ex.getCause().printStackTrace(); // 원인에 대한 tracking이 편함
}
}
}
public String getDataFromAnotherServer(String dataPath) {
try {
return anotherServerClient.getData(dataPath).toString();
} catch (GetDataException e) {
return defaultData; // 정상적인 데이터 또는 defaultData를 반환함으로써 정상 상태로 복구
}
}
실제로 try-catch로 예외를 처리하고 프로그램을 정상 상태로 복구하는 방법
가장 기본적인 방식
현실적으로 복구가 가능한 상황이 아닌 경우가 많거나 최소한의 대응만 가능한 경우가 많기 때문에 자주 사용하지는 않음
public void someMethod() throws Exception { ... }
public void someIrresponsibleMethod() throws Exception {
this.someMethod();
} // this.someMethod()를 호출하게 됐을 떼 Exception이 발생하면 throws Exception으로 흘러감
someMethod()
에서 발생시킨 에러가 someIrresponsibleMethod()
의 throws를 통해서 그대로 다시 흘러나감
관심사를 분리해서 한 레이어에서 처리하기 위해서 에러를 회피해서 그대로 흘러 보내는 경우도 존재
public void someMethod() throws IOException { ... }
public void someResponsibleMethod() throws MoreSpecificException {
try { // someMethod() 오류가 발생하면 catch문(IOException)으로 잡힘
// throws IOException으로 잡아놨기 때문에
this.someMethod();
} catch (IOException e) {
throw new MoreSpecificException(e.getMessage()); // 그대로 처리하지 않고 다시 throw
} // MoreSpecificException이라는 새로운 Exception을 만들어서 던짐
}
예외처리 회피하기 방법과 비슷
예외처리 회피하기보다 조금 더 적절한 예외를 던져주는 경우
RuntimeException처럼 일괄적으로 처리하기 편한 예외로 바꿔서 던지고 싶은 경우 사용