본문 바로가기

reviews/Effective JAVA

009. try-finally보다는 try-with-resource를 사용하라

자바 라이브러리에는 close 메서드를 직접 호출해 닫아줘야 하는 자원들이 있다.

ex) InputStream, OutputStream, java.sql.Connection 등

이런 자원 중 상당수가 안전망으로 finalizer를 활용하고는 있지만 그리 믿을만하지 못하기 때문에 클라이언트가 자원 닫기를 놓치는 경우 예측할 수 없는 성능 문제로 이어지기도 한다.

👉 전통적인 자원 닫기 방법

static String firstLineOfFile(String path) throws 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();
    }
}

두 예제 코드 모두에 미묘한 결점이 있다. 기기에 물리적인 문제가 생긴다면 readLine()이 예외를 던지고, 같은 이유로 close()도 실패하게 되는데 이럴 경우 두 번째 예외가 첫 번째 예외를 집어삼켜 버린다.

그러면 스택 추적 내역에 첫 번째 예외에 대한 정보가 남지 않게 되어 디버깅이 어렵게 된다. (첫 번째 예외를 기록하도록 코드를 수정할 수 있긴 하다.)

👉 try-with-resources

자바 7에서 생겼다.

이 구조를 사용하려면 해당 자원이 AutoCloseable 인터페이스를 구현해야 한다.

이 버전이 더 짧고 읽기 수월할 뿐 아니라 문제를 진단하기도 훨씬 좋다.

static String firstLineOfFile(String path) throws 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);
    }
}

readLine()과 close()(코드에 나타나진 않지만) 호출 양쪽에서 예외가 발생하면 close()에서 발생하는 예외는 숨겨지고 readLine()에서 발생한 예외가 기록된다.

프로그래머에게 보여줄 예외 하나만 보존되고 나머지 여러개의 다른 예외는 숨겨질 수 있는데 숨겨진 경우 스택 추적 내역에 suppressed 꼬리표를 달고 출력된다. 혹은 자바 7에서 Throwable에 추가된 getSuppressed()를 이용해 프로그램 코드에서 가져올 수도 있다.

그 외 예외처리가 필요한 경우

보통의 try-finally 처럼 catch 절을 쓸 수 있어 try 문을 중첩하지 않고도 다수의 예외를 처리할 수 있다.

반응형