[EffectiveJava] item09 - try-finally보다는 try-with-resources를 사용하라.
20 Apr 2022try-finally보다는 try-with-resources를 사용하라.
자바 라이브러리에는 close()
메서드를 호출해 닫아줘야 하는 자원이 많다.
예를 들면 InputStream
,OutputStream
,java.sql.Connection
같은 클래스들이 있다.
close();를 사용하는 이유 IO 객체와 같은 클래스는 OS로부터 자원을 할당받아 사용하는 네이티브 메서드들을 가지고 있는 경우가 있다. GC는 해당 자원을 항당 받았는지 알 수 없기 때문에, close() 메서드를 통해 자원을 닫는다는 것을 알린다.
해당 자원들을 close하기 위해 바로 저번 item08과 같이 finalizer
나 cleaner
를 사용 할 수 있지만, 그리 믿음직 스럽지못하다.
주로 예외 상황이 생길것을 대비해서 try-finally
를 이용해 아래의 코드처럼 자원들을 close를 해준다.
static String readFile(String path) throws IOException {
BufferedReader br = new BufferedReader(new FileReader(path));
try {
return br.readLine();
} finally {
br.close();
}
}
하지만 자원이 둘이상 사용된다면? 코드는 지저분해질 수 있다. 바로 아래의 코드와 같이 말이다.
static String 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 = i.read(buf)) >= 0)
out.write(buf, 0, n);
} finally {
out.close();
}
} finally {
in.close();
}
}
또한 readFile
메서드 또한 잠재적인 이슈가 있는데, 만일 기기에 물리적인 문제가 생긴다면 readLine
메서드가 예외를 던지고, 같은 이유로 finally
안에 close()
메서드가 실패할 수 도 있다.
해당 문제는 자바 7 이후 try-with-resources
덕에 모두 해결 되었다.
try-with-resources
는 AutoCloseable 인터페이스를 구현 해야 할 수 있는데, 내부 구조는 단순하게 void를 반환하는 close 메서드 하나만 덩그러니 있는 인터페이스이다.
AutoCloseable를 이용해 readFile
와, copy
메서드를 리팩토링 해보자.
- readFile 리팩토링
static String readFile(String path) throws IOException {
try (BufferedReader br = new BufferedReader(
new FileReader(path))) {
return br.readLine();
}
}
- copy 리팩토링
static String 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 = i.read(buf)) >= 0)
out.write(buf, 0, n);
}
}
try-with-resources
버전이 짧고 읽기 수월할 뿐 아니라 문제를 진단하기 훨씬 좋다.
또한 자바 7에서는 Throwable
에 추가된 getSuppressed
메서드를 이용하면 프로그램 코드에서 가져 올 수도있다.
보통의 try-catch
처럼 try-with-resources
에서도 catch구문을 사용할 수 있다.
아래는 readFile를 수정하여 catch구문을 이용한 예제이다.
static String readFile(String path) {
try (BufferedReader br = new BufferedReader(
new FileReader(path))) {
return br.readLine();
} catch (IOException e) {
}
}