[Java] try-catch 문의 주의사항 (feat. try-with-resource)
Java 의 예외 처리 문법인 try-catch에 대해 공부하고 정리하는 글입니다.
1. try-catch 문에서 하나의 try 에 이어서 여러개의 catch로 예외를 잡을때 예외의 순서에 주의 해야합니다.
하나의 try-catch문에서 IllegalArgumentException과 RuntimeException 두가지 예외를 잡으려 할 때
RuntimeException을 위쪽에 위치시킨다면 아래의 IllegalArgumentException을 catch 하는 문장에서 컴파일 에러가 발생합니다.
만약 예외가 상속 구조이고 상위 클래스(부모클래스)가 위쪽에 있다면 컴파일러는 에러를 발생시킵니다.
컴파일러는 "위쪽에서 이미 잡았던 예외인데 왜 또 잡으라그래?" 라고 합니다.
아래의 사진을 보면 IllegalArgumentException은 RuntimeException 은 부모 클래스입니다.
에러의 발생 이유는 아래와 같습니다.
- RuntimeException은 더 포괄적인 의미의 예외
- IllegalArgumentException은 좁은 의미의 예외
즉, 더 포괄적인 RuntimeException 에서 이미 IllegalArgumentException 까지 잡았기 때문에 아래의 catch문은 무의미한 코드이기 때문입니다.
아래처럼 더 좁은 의미의 예외를 더욱 위에서 catch해야 정상적으로 컴파일이 됩니다.
multicatch 일때는 순서에 상관없이 같이있는 예외가 상속관계라면 컴파일 에러가 발생합니다.
Bonus
그러면 항상 마지막 예외로 Exception을 잡아주는게 좋을까요? 모든 예외를 잡을 수 있잖아요.
무조건 Exception을 잡아버리면 코드만 봤을때 어떤 예외가 발생할지 예측하기가 어렵습니다.
try문 안에서 발생하는 특정 예외를 catch 해주는것이 코드를 읽는 사람에게 도움이 된다고 생각합니다.
2. 자바 8 이상의 try-catch 문에서 자원해제를 해야한다면 try-with-resource 를 사용하자
자바에서 자원을 사용하고 close() 를 호출해서 자원을 반납해야할때 코드는 아래와 같습니다.
해당 코드에서 자원해제를 위해 finally 문이 굉장히 길어지는것을 볼 수 있습니다.
이런 코드를 try-with-resource를 사용해서 간단하게 자원 해제를 해줄 수 있습니다.
주의 할 점
try-with-resource 가 자원해제해주는 대상은 AutoCloseable 인터페이스를 상속받은 대상입니다.
try 문의 () 안에서 생성된 자원은 try문에서 벗어날때 자원해제가 됩니다.
아래를 보면 FileOutputStream 이 상속받고 있는 OutputStream 클래스는 Closeable 인터페이스를 구현하고 있습니다.
여기서 몇가지 의문점들이 있습니다.
- try-with-resource 에서 finally 블럭을 추가할 수 있을까?
- 추가된다면 close()가 먼저인가 finally 안의 실행문들이 먼저인가?
실험을 위해서 AutoCloseable 인터페이스를 구현하는 MyClass 를 만들었습니다.
실행결과 [ try문 내부 -> close() 호출 -> finally ] 내부 호출 순서 입니다.
해당 실행 순서의 이유를 보려면 바이트 코드를 살펴보면 알 수 있습니다.
디컴파일된 바이트 코드를 보면 try-with-resource는 위와같이 내부에 try 문을 하나더 생성해서 close() 메서드를 호출하도록 만들기 때문에 finally 문을 붙인다해도 문법적으로 오류가 없었던겁니다.
Bonus
Lombok의 @Cleanup 애너테이션을 쓰면 자동으로 자원이 해제할 수 있습니다.
하지만 이는 추천되지 않는 방법입니다.
Lombok은 자바의 기본 라이브러리도 아니기때문에 Lombok에 너무 의존하는 코드는 좋지 않습니다.
가능하면 자바에서 기본으로 제공해주는 try-with-resource를 사용하는것을 지향합니다.
3. catch 문을 통해서 기존의 예외를 커스텀 예외로 전환해서 던져주는 경우 주의할 점
(출처 : dzone.com/articles/implementing-custom-exceptions-in-java?fromrel=true
커스텀 예외를 만들때 참고해야할 4가지 Best Practices)
커스텀 예외를 사용하는 방법중 하나로 예외 전환이 있습니다.
예외 전환이란?
기존에 발생한 예외를 catch문을 통해 다른 예외로 바꿔서 던져주는 것을 의미합니다.
try {
// todo
} catch (RuntimeException e) { // 기존의 RuntimeException 예외를
throw new MyCustomException(); //MyCustomException으로 바꿔서 던짐
}
보통 catch 된 예외에는 발생한 오류를 분석하는데 중요한 정보가 담겨있습니다.
따라서 우리는 에러에 대한 cause를 전환하는 예외에 함께 전달해주어야 합니다.
먼저 커스텀 예외를 만들 때 상속받은 예외의 다양한 생성자들을 함께 만들어주어야 합니다.
이제 커스텀 예외로 전환할 때 우리는 catch에서 잡았던 예외의 정보를 넘겨줌으로써 중요한 정보의 소실을 막을 수 있습니다.