[Effective Java] try-finally보다는 try-with-resources를 사용하라

들어가며

자바 라이브러리에는 close 메소드를 호출해 직접 닫아줘야 하는 자원이 많다. 자원 닫기는 클라이언트가 놓치기 쉬워서 예측할 수 없는 성능 문제로 이어지기도 한다. 전통적으로 이를 위해 try-finally가 쓰였다. 다음 코드를 보자.

1
2
3
4
5
6
7
8
static String firstLineOfFile(String path) throws IOException {
BufferedReader br = new BufferedReader(new FileReader(path));
try {
return br.readLine();
} finally {
br.close();
}
}

나쁘진 않지만 자원을 하나 더 사용한다면 어떨까?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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();
}
}

try-finally를 사용하는 앞의 코드에서 조차 미묘한 결점이 있다. firstLineOfFile 메소드를 보자. 예외는 try 블록과 finally 블록 두 군대에서 생길 수 있다. 예를 들어 기기에 물리적 결함이 생겨 readLine 메소드가 예외를 던지고 close도 연쇄적으로 예외를 던진다. 이 때 두번째 예외가 첫번째 예외를 집어삼켜 버린다. 두번째 예외를 대신 첫번째 예외를 기록하도록 수정할 수 있지만 코드가 너무 지저분해진다.

이런 문제는 try-with-resources로 해결할 수 있다. 이 구조를 사용하려면 해당 자원이 AutoCloseable 인터페이스를 구현해야한다. 다음 코드를 보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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 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);
}
}

try-with-resources가 코드를 보기 수월하고 문제를 찾기에도 좋다. firstLineOfFile 메소드에서 readLine과 close 양쪽에서 예외가 발생하면 close에서 발생한 예외는 숨겨지고 readLine에서 발생한 예외가 기록된다. 숨겨진 예외들도 suppressed 꼬리표를 달고 출력된다. getSupressed 메소드를 통해 코드로 가져올 수도 있다. try-with-resources에서도 catch 절을 사용할 수 있다. try-with-resources는 자동으로 close를 수행해준다. 단, AutoCloseable을 구현한 객체에 한해서 말이다.

Author: Song Hayoung
Link: https://songhayoung.github.io/2020/08/06/Languages/Effective%20JAVA/item9/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.