자바 라이브러리에는 close 메서드를 호출해 직접 닫아줘야 하는 자원이 많음(InputStream, OutputStream, java.sql.Collection 등)
finalizer를 사용해도 되지만 아이템 8에서 봤듯이 믿을만한 요소가 아님
전통적으로는 try-finally를 사용함
static String firstLineOfFile(String path) throws IOException {
BufferedReader br = new BufferedReader(new FileReader(path));
try {
return br.readLine();
} finally {
br.close();
}
}
static String firstLineOfFile(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();
}
}
만약 물리적인 문제가 생겨서 firstLineOfFile 메서드 안의 readLine 메서드가 예외를 던지고 같은 이유로 close 메서드도 실패할 것임, 이런 상황에서 두 번째 예외가 첫 번째 예외를 완전히 집어삼킴, 이러면 실제 시스템에서의 디버깅도 몹시 어려움, 아무리 첫 번째 예외에 기록하도록 처리해도 코드가 길어짐
이때 try-with-resources를 통해서 해결할 수 있음, 이 구조를 사용하기 위해서 해당 자원은 AutoCloseable 인터페이스를 구현해야함, 단순히 void를 반환하는 close 메서드하나만 정의한 인터페이스임
자바 라이브러리와 서드파티 라이브러리들의 수많은 클래스와 인터페이스가 이미 AutoCloseable을 구현하거나 확장함, 닫아야 하는 자원을 뜻하는 클래스를 작성하려면 AutoCloseable을 반드시 구현해야함
try-with-resource로 고쳐쓰면 아래와 같이 쓸 수 있음
static String firstLineOfFile(String path) throws IOException {
try(BufferedReader br = new BufferedReader(new FileReader(path))) {
return br.readLine();
}
}
static void copy(String src, String dst) throws IOException {
try (InputStream in = new InputStream(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);
}
}
위와 같이 처리한다면 문제를 진단하기 수월해짐, 만약 readLine과 close에 예외가 발생하면 close에서 발생한 예외는 숨겨지고, readLine에서 발생한 예외는 기록됨
이렇게 보여줄 예외 하나만 보존되고 여러 개의 다른 예외가 숨겨질 수 있음, 이렇게 숨겨진 예외들도 그냥 버려지지 않고 스택 추적 내역에 숨겨졌다는 꼬리표를 달고 출력됨, Throwable에 추가된 getSuppressed 메서드를 이용해서 코드를 가져올 수 있음
여기서 catch를 추가해서 쓸 수 있음, try문을 중첩하지 않고도 다수의 예외를 처리 가능함
static String firstLineOfFile(String path) throws IOException {
try (BufferedReader br = new BufferedReader(new FileReader(path))) {
return br.readLine();
} catch (IOException e) {
return defaultVal;
}
}
꼭 회수해야 하는 자원을 다룰 때는 try-finally 말고 try-with-resources를 사용하자, 예외는 없음, 코드는 더 짧고 분명해지고, 만들어지는 예외 정보도 훨씬 유용함
try-finally로 작성하면 실용적이지 못할 만큼 코드가 지저분해지는 경우라도, try-with-resources로는 정확하고 쉽게 자원을 회수할 수 있음