자바 라이브러리에서 close 메서드를 호출해 직접 닫아줘야 하는 자원 예시 : InputStream, OutputStream, java.sql.Connection 등
자원 닫기는 예측할 수 없는 성능 문제로 이어져 finalizer을 사용하지만 썩 믿음직스럽지 못하다.
public static String firstLineOfFile(String path) throw IOException {
BufferedReader br = new BufferedReader(new FileReader(path));
try {
return br.readLine();
} finally {
br.close();
}
}
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 구문을 하나만 쓰면 leak이 생길 수 있다.
예외는 try 블록과 finally 블록 모두에서 발생할 수 있음.
하지만 기기에서 물리적인 문제가 생긴다면 firstLineOfFile 메서드 안의 readLine 메서드가 예외를 던지고, 같은 이유로 close 메서드도 실패할 것이다. 이런 상황에선 두 번째 예외가 첫 번째 예외를 완전히 집어삼켜 버린다. 이 경우 디버깅이 굉장히 어렵다.
이 구조를 사용하려면 해당 자원이 AutoCloseable 인터페이스(단순히 void를 반환하는 close 메소드 하나만 정의한 인터페이스)를 구현해야 한다.
자바 라이브러리와 서드파티 라이브러리들의 수많은 클래스와 인터페이스가 이미 AutoCloseable을 구현하거나 확장해뒀다.
public static String firstLineOfFile(String path) throw 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);
}
}
public static String firstLineOfFile(String path) throw IOException {
try (BufferedReader br = new BufferedReader(new FileReader(path))) {
return br.readLine();
} catch (Exception e) {
return defaultVal;
}
}
파일을 열거나 데이터를 읽지 못했을 때 예외를 던지는 대신 기본값을 반환하도록 한 예시.
p48. 자바 퍼즐러 예외 처리 코드의 실수
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);
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 {
try {
out.close();
} catch(IOException e){
//TODO 이렇게 하면 되는 거 아닌지?
}
try {
in.close();
} catch(IOException e){
//TODO 안전한지?
}
}
IOException이 아닌 RuntimeException이 발생하면 out.close()에서 끝남.
p49. try-with-resources 바이트코드
public class TopLine{
public TopLine(){
}
static String firstLineOfFile(String path) throws IOException{
BufferedReader br = new BufferedReader(new FileReader(path));
String var2;
try{
var2 = br.readLine();
} catch (Throwable var5) {
try{
br.close();
} catch(Throwable var4) {
var5.addSuppressed(var4);
//close할 때 발생하는 예외를 계속 추가해줌
}
throw var5;
}
br.close();
return var2;
}
public static void main(String[] args) throws IOException{
String path = args[0];
System.out.println(firstLineOfFile(path));
}
}