이 글은 Effective Java 3/E의 내용을 요약한 글입니다. 자세한 내용은 책을 참고하시기 바랍니다.
try-finally와 try-with-resources의 차이
InputStream
, OutPutStream
, java.sql.Connection
과 같은 자원들은 사용 후에 close()
메서드를 통해 닫아줘야 합니다.
이는 실제로 클라이언트가 놓치기 쉬워 예측하기 어려운 성능 문제로 이어지기도 합니다.
finalizer는 믿음직하지 못하여 사용을 지양하는 것이 좋다.(이펙티브자바 아이템 7)
자원회수를 위한 전통적인 코드로는 많은 프로그래머들이 아래의 코드와 같이 try-finally
를 사용하였다.
static String firstLiseOfFile(String path) throws IOException {
BufferedReader reader = new BufferedReader(new FileReader(path));
try {
return br.readLine();
} finally {
br.close();
}
}
하지만 try-finally
를 올바르게 사용하여도 미묘한 결점들이 존재합니다.
예시 코드가 물리적인 오류로 인해 try문에 위치한 return br.readLine();
와 finally에 위치한 br.close();
가 모두 실패한다 생각해보자,
이와같이 두번의 예외가 발생하게 된다면 두번쨰 예외가 처번째 예외를 완전히 삼켜먹어 디버깅을 어렵게 할 것입니다.
물론 여기서도 두번째 예외 대신 첫번째 예외가 나오게 코드를 수정할 수는 있지만 코드가 지저분해져서 좋지 않은 방법입니다.
이러한 문제들은 자바 7에서 나온 try-with-resources
를 통해 해결할 수 있습니다.
해당 구조를 사용하려면
void
를 반환하는close()
메서드 하나만 정의되어있는AutoCloseable
인터페이스를 구현해야합니다. 수많은 자바 라이브러리들은 이미AutoCloseable
를 구현하거나 확장하였습니다. 개발을 하며 닫아야 하는 자원 클래스를 생성한다면AutoCloseable
인터페이스를 구현하길 바랍니다.
static String firstLiseOfFile(String path) throws IOException {
try (BufferedReader reader = new BufferedReader(new FileReader(path));) {
return br.readLine();
}
}
try-with-resources
를 적용한 코드는 위와 같습니다.
해당 코드는 위의 try-finally
로 구현한 코드와 비교하였을 때 코드도 짧아져 가독성이 좋아졌습니다.
또한 앞에서 설명했던 return br.readLine();
와 br.close();
에서 예외가 발생하는 상황만 생각해도
close()
의 예외는 숨겨지고 br.readLine();
의 예외만 기록되어 문제를 진단하기 쉽습니다.
close()
와 같이 숨겨진 예외들은 버려지는 것은 아니고 "숨겨졌다.(suppressed)"라는 꼬리표를 달고 출력됩니다.
핵심정리
꼭 회수해야하는 자원을 다룰 때는 예외 상황 없이 try-finally
말고, try-with-resources
를 사용하도록 하자.
try-with-resources
를 사용하면 코드는 더 짧고 분명해지며, 만들어지는 예외 정보도 훨씬 유용하다. 또한 정확하고 쉽게 자원을 회수할 수도 있다.