DEV ℧ Developer Diary

[EffectiveJava] item09 - try-finally보다는 try-with-resources를 사용하라.

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

자바 라이브러리에는 close()메서드를 호출해 닫아줘야 하는 자원이 많다.

예를 들면 InputStream,OutputStream,java.sql.Connection 같은 클래스들이 있다.

close();를 사용하는 이유 IO 객체와 같은 클래스는 OS로부터 자원을 할당받아 사용하는 네이티브 메서드들을 가지고 있는 경우가 있다. GC는 해당 자원을 항당 받았는지 알 수 없기 때문에, close() 메서드를 통해 자원을 닫는다는 것을 알린다.

해당 자원들을 close하기 위해 바로 저번 item08과 같이 finalizercleaner를 사용 할 수 있지만, 그리 믿음직 스럽지못하다.

주로 예외 상황이 생길것을 대비해서 try-finally를 이용해 아래의 코드처럼 자원들을 close를 해준다.

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

하지만 자원이 둘이상 사용된다면? 코드는 지저분해질 수 있다. 바로 아래의 코드와 같이 말이다.

static String 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 = i.read(buf)) >= 0)
                out.write(buf, 0, n);
        } finally {
            out.close();
        }
    } finally {
        in.close();
    }
}

또한 readFile메서드 또한 잠재적인 이슈가 있는데, 만일 기기에 물리적인 문제가 생긴다면 readLine 메서드가 예외를 던지고, 같은 이유로 finally안에 close()메서드가 실패할 수 도 있다.

해당 문제는 자바 7 이후 try-with-resources 덕에 모두 해결 되었다.

try-with-resources는 AutoCloseable 인터페이스를 구현 해야 할 수 있는데, 내부 구조는 단순하게 void를 반환하는 close 메서드 하나만 덩그러니 있는 인터페이스이다.

AutoCloseable를 이용해 readFile와, copy 메서드를 리팩토링 해보자.

  • readFile 리팩토링
static String readFile(String path) throws IOException {
    try (BufferedReader br = new BufferedReader(
            new FileReader(path))) {
        return br.readLine();
        }
}
  • copy 리팩토링
static String 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 = i.read(buf)) >= 0)
            out.write(buf, 0, n);
        }
}

try-with-resources 버전이 짧고 읽기 수월할 뿐 아니라 문제를 진단하기 훨씬 좋다. 또한 자바 7에서는 Throwable에 추가된 getSuppressed 메서드를 이용하면 프로그램 코드에서 가져 올 수도있다.

보통의 try-catch처럼 try-with-resources에서도 catch구문을 사용할 수 있다.

아래는 readFile를 수정하여 catch구문을 이용한 예제이다.

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

    }
}