이펙티브 자바

[이펙티브 자바]try-finally보다는 try-with-resources를 사용하라_아이템9

climb-up 2023. 12. 5. 22:04

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


[자원이 둘 이상이면 try-finally 방식은 지저분하다.]

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 메서드도 실패할 것이다. => 두번째 예외가 첫번째 예외를 삼킨다.
[문제점] : 스택 추적 내역에 첫 번째 예외에 관한 정보는 남지 않아서 디버깅이 어렵다.

Finally 문제 해결 => try-with-resources 사용

try-with-resources 사용하기 위해 AutoCloseable인터페이스(void를 반환하는 close 메서드만 정의한 인터페이스)를 구현해야한다.
닫아야하는 자원의 클래스가 있다면 AutoCloseable을 반드시 구현하여 try-with-resources를 사용하자.

[try-with-resources 사용]

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


[try-with-resources 복수 자원 처리]

static void copy(String src, String dst) throws IOException {
    try (InputStream in = new FileInputStream(src);
        OutputStream out = new FileInputStream(dst)) {
        
        int n;
        while ((n = in.read(buf)) >= 0)
            out.write(buf, 0, n);
    }
}


firstLineOfFile 메서드에 readLine과 코드에 나타나지 않은 close 호출 양쪽에서 예외가 발생하면, close에서 발생한 예외는 숨겨지고 readLine에서 발생한 예외가 기록된다. 숨겨진 예외들은 버려지지않고 스택 추적 내역에 suppressed를 달고 출력된다. => Throwable에 추가된 getSuppressed메서드를 이용하면 프로그램 코드를 가져올 수 있다.

try-with-resources에서도 catch절을 쓸 수 있다. => try 문을 중첩하지 않고 다수 예외 처리 가능.
firstLineOfFile 메서드를 수정하여 파일을 열거나 데이터를 읽지 못할 때 예외 대신 기본 값을 반환하도록 해봤다.

[try-with-resources를 catch절과 함께 사용]

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


마무리로 회수할 자원을 다룰 땐 try-with-resources를 사용하자. 가장 정확하고 쉽게 자원을 회수할 수 있다.