try-finally 보다는 try-with-resources를 사용하라 InputStream, OutputStream, java.sql.Connection 등은 close 메소드를 호출해 닫아줘야 한다. 하지만 이걸 클라이언트가 놓치면 성능 문제가 생긴다. 안전망으로 finalizer가 있지만 그리 믿을만 하지 못하다.(아이템8)
public static String inputString() throws IOException {
BufferdReader br = new BufferdReader(new InputStreamReader(System.in));
String result = br.readLine();
br.close();
return result;
}
위 방법대로 close를 호출하게 되면 문제가 발생할 수 있다. BufferdReader는 사용중 IOException이 발생할 수 있는데, 만약 br.readLine() 메소드에서 IOException이 발생하게 되면 메소드가 종료되므로 close가 호출되지 않고 스트림이 메모리에 남게된다. 전통적인 try-finally를 이용한 close해결 public static String inputString() throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
try {
return br.readLine();
} finally {
br.close();
}
}
finally블록은 try, catch 블록이 끝난 뒤 실행할 로직을 정의해주는 블록이다. 따라서 이제 IOException이 발생하게 되더라도 상위 메소드로 IOException 객체를 던져준 뒤 finally 메소드를 종료하게 된다.
하지만 try-finally 방식은 자원을 둘 이상 사용하게 되면 코드가 지저분해지고 실수를 저지를 가능성이 커지게 된다.
try-finally의 문제점 public static void inputAndWriteString() throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
try {
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
try {
String line = br.readLine();
bw.write(line);
} finally {
bw.close();
}
} finally {
br.close();
}
}
코드가 지저분해 지는 문제도 있지만 더 큰 문제가 존재한다. public static String inputString() throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
try {
return br.readLine();
} finally {
br.close();
}
}
inputString 메소드의 try 블록을 실행하던 도중 기기에 문제가 생긴다면 readLine이 정상적으로 실행되지 못하고 예외를 던지게 되고, 같은 이유로 finally 블록의 close 메소드도 예외를 던지게 된다. 만약 이 예외들을 catch해서 상위 메소드에서 예외 정보를 체크해본다면, finally 블록에서 터딘 예외가 try 블록에서 생긴 예외를 집어 삼켜서 finally 블록의 예외만 체크하게 된다. try 블록에서 터진 예외로 인해 finally 블록에서 예외가 발생했음에도 불구하고 최초 원인인 예외를 체크하지 못하게 되는 것이다. 물론 적절한 코드를 통해 최초 원인을 체크할 수는 있지만 코드가 너무 지저분해지기 때문에 추천하는 방법은 아니다. 해결책 위의 문제를 해결하기 위해 자바 7버전 부터는 try-with-resources 가 도입 되었다. try-with-resources를 사용하기 위해서는 사용하는 자원이 AutoCloseable 인터페이스를 구현해야 한다. public static String inputString() throws IOException {
try (BufferedReader br = new BufferedReader(new InputStream(System.in))) {
return br.readLine();
}
}
가독성이 좋아지고 예외가 발생했을 때 디버깅 하기에도 더 편리해졌다. inputString 메소드의 readLine과 close 모두에서 예외가 발생하는 경우 close호출 시 발생하는 예외는 숨겨지고 readLine 예외가 기록된다. 이렇게 숨겨진 예외는 무시되는 것이 아니라 suppressed 상태가 되어 stackTrace 시 숨겨졌다는 메시지로 출력된다. suppressed 상태가 된 예외는 자바7 부터 도입된 getSuppressed 메소드를 통해 가져와서 사용할 수 있다.