자바 라이브러리에는 close 메서드를 호출해 직접 닫아줘야 하는 자원이 많다.
InputStream , OutStream, java.sql.Connection 등이 그런 예이다.
자원 닫기는 클라이언트가 놓치기 쉬워서 예측할 수 없는 성능 문제로 이어지기도 한다.
이런 자원 중 상당수가 안전망으로 finalizer를 활용하고는 있지만 finalizer는 그리 믿을만하지 못하다.
public class Copy {
private static final int BUFFER_SIZE = 8 * 1024;
// 코드 9-2 자원이 둘 이상이면 try-finally 방식은 너무 지저분하다! (47쪽)
static void copy(String src, String dst) throws IOException {
InputStream in = new FileInputStream(src);
try {
OutputStream out = new FileOutputStream(dst);
try {
byte[] buf = new byte[BUFFER_SIZE];
int n;
while ((n = in.read(buf)) >= 0)
out.write(buf, 0, n);
} finally {
out.close();
}
} finally {
in.close();
}
}
public static void main(String[] args) throws IOException {
String src = args[0];
String dst = args[1];
copy(src, dst);
}
}
전통적으로 자원이 제대로 닫힘을 보장하는 수단으로 try - finally가 사용되었다.
하지만 자원이 둘 이상이라면 이 방식은 너무 지저분해진다.
또, 이 코드에서는 미묘한 결점이 있다.
예외는 try 블록과 finally 블록 모두에서 발생할 수 있는데,
어떠한 문제가 생겨서 첫번째 메서드가 예외를 던지고, 같은 이유로 finally 부분도 실패할 것이다.
이런 상황이라면 두번째 예외가 첫 번째 예외를 완전히 집어삼켜 버린다.
그러면 스택 추적에서 첫 번째 예외를 집어삼켜서 관련 정보를 확인할 수 없다.
이것은 실제 시스템에서 디버깅을 몹시 어렵게 한다.
프로젝트 중 예외처리를 제대로 안하거나, JNI를 사용하여 예외처리를 수동으로 해야했던 경험이 있었는데
간단한 문제도 예외가 생성되지 않아 한참을 헤맸던 기억이 있다.
그 후 예외처리에는 시간을 쏟더라도 정말정말정말 신경을 쓰려고 노력한다.
어쨋든 이러한 문제들은 자바 7에서 try-with-resources 덕에 모두 해결되었다.
이 구조를 사용하려면 해당 자원이 AutoCloseable 인터페이스를 구현해야 한다.
단순히 void를 반환하는 close 메서드를 하나만 덩그러니 정의한 인터페이스다.
닫아야하는 자원이 있는 클래스를 작성한다면 AutoCloseable을 반드시 구현해야 한다.
전 포스팅에 프로젝트 중 멀티스레드를 사용해야 하는 상황에서 AutoCloseable을 이용해 스레드를 정리했던 경험을 간단하게 적어놓았다.
try-with-resources를 사용한 방식
public class Copy {
private static final int BUFFER_SIZE = 8 * 1024;
// 코드 9-4 복수의 자원을 처리하는 try-with-resources - 짧고 매혹적이다! (49쪽)
static void copy(String src, String dst) throws IOException {
try (InputStream in = new FileInputStream(src);
OutputStream out = new FileOutputStream(dst)) {
byte[] buf = new byte[BUFFER_SIZE];
int n;
while ((n = in.read(buf)) >= 0)
out.write(buf, 0, n);
}
}
public static void main(String[] args) throws IOException {
String src = args[0];
String dst = args[1];
copy(src, dst);
}
}
해당 처리가 짧고 가독성이 좋을 뿐만 아니라 문제를 진단하기에도 훨씬 좋다.
예외가 숨겨진다고 해도 스택 추적 내역에 예외가 숨겨졌다는 꼬리표를 달고 출력된다.
또한 catch 절을 사용해서 try 문을 더 중첩하지 않고도 다수의 예외를 처리할 수 있다.
'JAVA > 이펙티브 자바' 카테고리의 다른 글
11. equals를 재정의하려거든 hashCode도 재정의하라 (0) | 2023.07.01 |
---|---|
10. equals는 일반 규약을 지켜 재정의하라 (0) | 2023.06.26 |
8. finalizer와 cleaner 사용을 피하라 (0) | 2023.06.20 |
7. 다 쓴 객체 참조를 해제하라 (0) | 2023.06.19 |
6. 불필요한 객체 생성을 피하라 (0) | 2023.06.14 |