1 minute read

item9 try-finally 보다는 try-with-resources를 사용하라

try-finally

  • 자바에는 close를 호출해 직접 닫아줘야 하는 자원이 많다. InputStream, OutputStream, java.sql.Connection
  • 이런 자원 닫기는 클라이언트에서 놓치기 쉬워 성능 문제로 이어지기도 한다.
  • 전통적으로 자원이 닫힘을 보장하는 수단으로 쓰였다.
// 하나의 자원만을 사용한다면...
public static String inputString() throws IOException {
    BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    try {
        return br.readLine();
    } finally {
        br.close();
    }
}
// 2개 이상을 사용한다면... 코드가 더 지저분해진다.
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 블록을 실행하던 중 기기에 문제가 생긴다면 readLine이 정상적으로 실행되지 못하고 첫번째 예외를 던지게 되고, 같은 이유로 finally 블록의 close 메서드도 두번째 예외를 발생하게 된다.
  • 이 경우, 두번째 예외가 첫번째 예외를 집어 삼켜서 두번째의 예외만 체크하게 된다. 최초 원인이 되는 예외를 체크하지 못하게 되어, 실제 시스템에서의 디버깅이 어려워 질 것이다.

try-with-resources

  • try-finally 방식의 단점을 보완하기 위해 자바 7부터 등장하였다.
  • 사용하는 자원이 AutoCloseable 인터페이스를 구현해야 할 필요가 있다.
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);
    }
}

static String firstLineOfFile(String path) throws IOException {
    try (BufferedReader br = new BufferedReader(new FileReader(path))) {
        return br.readLine();
    }
}
  • 가독성이 읽기 수월할 뿐만 아니라, 문제 발생시 이에 대한 진단을 내리기에도 훨씬 좋다.
  • readLine과 close 호출 양쪽에서 예외가 발생하면, close에서 발생한 예외는 숨겨지고 readLine에서 발생한 예외가 기록된다. 또한 이렇게 숨겨진 예외도 그냥 버려지지는 않고, 스택 추적 내역에 Suppressed 태그를 달고 출력된다.
static String firstLineOfFile(String path) throws IOException {
    try (BufferedReader br = new BufferedReader(new FileReader(path))) {
        return br.readLine();
    } catch(IOException e){
	    return defaultVal;
    }
}
  • try-finally 처럼 catch 절을 사용할 수도 있다.

Leave a comment