java lib에는 close메서드를 호출해 직접 닫아줘야 하는 자원이 많다.
예로 InputStream,OutputStream,java.sql.Connection 등등이 있다.
자원 닫기는 클라이언트가 놓치기 쉬워서 예측할 수 없는 성능 문제로 이어지기도 한다.
이런 자원 중 상당수가 안전망으로 finalizer를 활용하고는 있지만 finalizer는 그리 믿을만하지 못하다.
전통적으로 자원이 제대로 닫힘을 보장하는 수단으로 try-finally가 쓰였다.
public class TopLine {
static String firstLineOfFile(String path) throws IOException {
BufferedReader br = new BufferedReader(new FileReader(path));
try {
return br.readLine();
} finally {
br.close();
}
}
나쁘지 않지만, 자원을 하나 더 사용한다면?
public class Copy {
private static final int BUFFER_SIZE = 8 * 1024;
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();
}
위 방법은 2가지 단점이 존재한다.
만약 try문과 finally문 둘 다에서 예외가 발생할 경우, 두번째 예외가 첫번째 예외를 집어삼키게 된다는 결점이 있다.
이러면 디버깅 자체가 난항을 겪게된다.
위의 try-finally 문제를 해결한 것이 바로 java7에서 등장한 try-with-resources다.
public class TopLine {
static String firstLineOfFile(String path) throws IOException {
try (BufferedReader br = new BufferedReader(
new FileReader(path))) {
return br.readLine();
}
}
public static void main(String[] args) throws IOException {
String path = args[0];
System.out.println(firstLineOfFile(path));
}
}
바로 try 안에 파라미터로 자원을 넘겨주는 방식이다.
자원이 한개라 크게 와닿지 않아 추가 예제를 살펴본다.
public class Copy {
private static final int BUFFER_SIZE = 8 * 1024;
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);
}
}
자원이 몇개던 간단하게 작성이 가능하다.
try-with-resources에서도 catch문을 사용 가능하다.
public class TopLineWithDefault {
static String firstLineOfFile(String path, String defaultVal) {
try (BufferedReader br = new BufferedReader(
new FileReader(path))) {
return br.readLine();
} catch (IOException e) {
return defaultVal;
}
}
꼭 회수해야 하는 자원을 다룰 때는 try-with-resources를 쓰자.
예외는 없다. 코드는 더 짧고 분명해진다.
만들어지는 예외 정보도 훨씬 유용하다.