아이템9. try-finally 보다는 try-with-resources 를 사용하라

wisdom·2022년 8월 17일
0

Effetctive Java

목록 보기
9/80
post-thumbnail

✔️ try-finally

자바에는 InputStream, OutputStream, java.sql.Connection과 같이 close 메서드를 호출해 직접 닫아줘야 하는 자원이 많다. 그런데 자원 닫기는 클라이언트가 놓치기 쉬워 성능 문제로 이어질 수 있다.

그렇다면 자원이 제대로 닫히도록 보장할 수 있는 방법은 무엇일까?

전통적으로 자원이 제대로 닫힘을 보장하는 수단으로 try-finally가 쓰였다.

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();
    }
}

이렇게 구현하면 예외 발생 시 문제가 될 수 있다.

예를 들어 기기에 물리적인 문제가 발생한다면, firstLineOfFile 메서드 안의 readLine 메서드가 예외를 던지고, close 메서드도 같은 이유로 예외를 던질 것이다. 이런 상황이라면 두 번째 예외로 인해 첫 번째 예외에 대한 정보가 스택 추적 내역에 남지 않을 것이고, 디버깅이 어려워진다. (일반적으로 문제 진단 시, 처음 발생한 예외를 보고싶을 것이다.)

또한, 자원을 추가할 때마다 try-finally 블록을 중첩적으로 사용하여야 하기 때문에 코드가 지저분해진다.

이러한 문제점은 자바 7부터 try-with-resources 를 통해 모두 해결되었다.

✔️ try-with-resources

try-with-resources 구조를 사용하기 위해서는 해당 자원이 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 꼬리표를 달고 출력된다.

static String firstLineOfFile(String path) throws IOException {
    try (BufferedReader br = new BufferedReader(new FileReader(path))) {
        return br.readLine();
    } catch (IOException e) {
        return defaultValue;
    }
}

try-with-resources 는 catch 절도 쓸 수 있다.
catch 절을 사용하면 try 문을 중첩시키지 않아도 다수의 예외를 처리할 수 있다.


📌핵심 정리

꼭 회수해야 할 자원을 다룰 때는 try-with-resources를 사용하자.
코드도 짧고 분명해지고, 예외 정보도 훨씬 유용하다.

profile
백엔드 개발자

0개의 댓글