try-finally보다 try-with-resource를 사용하라 item09

Bong2·2022년 5월 19일
0

이팩티브자바

목록 보기
8/9

자바 라이브러리에는 close메서드를 호출해 직접 닫아줘야 하는 자원이 많다. InputStream, OutputStream,java.sql.Connection 등이 좋은 예다.
자원 닫기는 클라이언트가 놓치기 쉬워서 예측할 수 없는 성능문제로 이어지기도 한다.이러한 자원 중 상당수가 안전망으로 finalizer를 활용하고는 있지만 finalizer는 그리 믿음직하지 못 한다.

전통적인 try-finally

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class TopLine {
    // 코드 9-1 try-finally - 더 이상 자원을 회수하는 최선의 방책이 아니다! (47쪽)
    static String firstLineOfFile(String path) throws IOException {
        BufferedReader br = new BufferedReader(new FileReader(path));
        try {
            return br.readLine();
        } finally {
            br.close();
        }
    }

    public static void main(String[] args) throws IOException {
        String path = args[0];
        System.out.println(firstLineOfFile(path));
    }
}

해당 하나의 자원에서는 나쁘지 않다.

 // 코드 9-2 자원이 둘 이상이면 try-finally 방식은 너무 지저분하다! (47쪽)
    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-with-resource

 // 코드 9-3 try-with-resources - 자원을 회수하는 최선책! (48쪽)
    static String firstLineOfFile(String path) throws IOException {
        try (BufferedReader br = new BufferedReader(
                new FileReader(path))) {
            return br.readLine();
        }
    }

이 구조를 사용하려면 해당 자원이 AutoCloseable 인터페이스를 구현해야한다. 단순히 void를 반환하는 close메서드 하나만 덩그러니 정의한 인터페이스이다.

다수의 자원을 이용할 때

// 코드 9-4 복수의 자원을 처리하는 try-with-resources - 짧고 매혹적이다! (49쪽)
    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);
        }
    }

버전이 짧고 읽기 수월할 뿐 아니라 문제를 진단하기도 훨씬 좋다.왜냐하면 처음에 발생한 예외가 뒤에 발생한 에러에 덮히지 않기 때문이다.
뒤에 발생한 에러는 첫 번째 발생한 에러 뒤에다 쌓아두고(suppressed)처음 발생한 에러를 중요시 여긴다. 그리고 ThrowablegetSuppressed메소드를 사용해서 뒤에 쌓여있는 에러를 코딩으로 사용가능하다.
catch 블록은 try-finally에서처럼 사용가능하다.

핵심정리

꼭 회수해야되는 자원이 있을 경우에는 try-with-resource를 사용하자. 코드는 더 짧고 분명해지고, 만들어지는 예외 정보도 훨씬 유용하다.

profile
자바 백엔드 개발자로 성장하자

0개의 댓글

관련 채용 정보