반응형
try-finally 보다는 try-with-resources를 사용하라
자바 라이브러리에는 InputStream
, Connection
등 close
메서드를 호출해 직접 닫아줘야 하는 자원이 많다. 자원을 닫는 것은 놓치기 쉬워 예측하기 어려운 성능 문제로 이어지기도 한다. 상당 수가 안정망으로 finalizer
, cleaner
등을 활용하지만 실행을 보장하지 못한다.
예외가 발생하거나 메서드에서 반환되는 경우를 포함해서 자원이 제대로 닫힘을 보장하는 수단으로 try-finally
가 쓰였다. 다음 코드 예시를 살펴보자.
try-finally
static String firstLineOfFile(String path) throws IOException {
BufferedReader br = new BufferedReader(new FileReader(path));
try {
br.readLine();
} finally {
br.close();
}
return "";
}
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[1024];
int n;
while ((n = in.read(buf)) >= 0)
out.write(buf, 0, n);
} finally {
out.close();
}
} finally {
in.close();
}
}
- 다음과 같이
finally
를 통해 예외 또는 메서드 반환의 경우에도close
를 할 수 있다. - 물리적인 문제 등이 생겨서
br
과 관련된 기능들이 예외를 발생시킨다면, 현재 코드에선br.readLine()
과br.close()
에서 예외가 발생된다. finally 블록은 try 블록에서 예외가 발생해도 실행하기 때문에close
에서 발생한 예외는 기록되지 않고,readLine
에서 발생한 예외만 기록된다. 즉, 첫 번째 예외에 대한 정보는 남지않기 때문에 디버깅이 어려워 진다.
try-with-resources
try-finally
구문의 문제점을 해결하기 위해 try-with-resources
구문이 자바 7 버전부터 지원되었다. 단, 이 구조를 사용하려면 해당 자원이 AutoCloseable
인터페이스를 구현해야 한다. 즉, 반드시 자원을 반환해야하는 경우 해당 인터페이스를 구현 또는 확장해야 한다.
해당 구문을 사용하면 readLine()
, close()
양 쪽에서 예외가 발생해도 모든 예외가 출력된다.
static String firstLineOfFile(String path) throws IOException {
try (BufferedReader br = new BufferedReader(new FileReader(path))) {
br.readLine();
}
return "";
}
static void copy(String src, String dst) throws IOException {
try (InputStream in = new FileInputStream(src); OutputStream out = new FileOutputStream(dst)) {
byte[] buf = new byte[1024];
int n;
while ((n = in.read(buf)) >= 0)
out.write(buf, 0, n);
}
}
- 코드가 간결해지고, 이중 try 구문도 가독성이 좋아졌다. 하지만,
AutoCloseable
구현이 없으므로 해당 기능으론close
에 대한 보장이 되지않는다. - finally와의 차이점은
try
안에 파라미터로 자원을 넘겨주는 것이다. 또한 try 구문안에 여러 자원을 넣을 수 있다. 마찬가지로catch
문도 사용이 가능하다.
package effective.item9;
import java.io.*;
public class CloseTest implements AutoCloseable {
static String firstLineOfFile(String path) throws IOException {
try (BufferedReader br = new BufferedReader(new FileReader(path))) {
br.readLine();
}
return "";
}
static void copy(String src, String dst) throws IOException {
try (InputStream in = new FileInputStream(src); OutputStream out = new FileOutputStream(dst)) {
byte[] buf = new byte[1024];
int n;
while ((n = in.read(buf)) >= 0)
out.write(buf, 0, n);
}
}
public void doSomething() {
System.out.println("Something");
}
@Override
public void close() throws Exception {
System.out.println("closed");
}
}
/*
출력
Something
closed
*/
class CloseMainTest {
public static void main(String[] args) {
try (CloseTest ct = new CloseTest()) {
ct.doSomething();
} catch (Exception e) {
e.printStackTrace();
}
}
}
- 다음과 같이
AutoCloseable
인터페이스를 구현하여close
를 재정의 한다면 자동으로 반환한다.
결론
회수해야 하는 자원을 다루는 모든 경우에서 try-finally
가 아닌 try-with-resources
를 사용하자. 코드가 더 짧아지고 가독성이 높아지며, 정확하고 쉽게 자원을 회수할 수 있다. 또한 close
를 보장하고 싶은 경우 Closeable
인터페이스를 구현하자.
REFERENCES
반응형