본문으로 바로가기

1. try-finally

사용한 자원을 제대로 닫아주기 위해 전통적으로 try-finally를 사용해왔다.

 

  • finalizer는 안전망의 역할로 제한적으로 사용된다.

* try-finally - 더이상 자원을 회수하는 최선의 방책이 아니다!

 

변경전 firstLineOfFile.java

static void firstLineOfFile(String src, String dst) throws IOException {
    BufferedReader br = new BufferedReader(new FileReader(path));
    try {
        return br.readLine();
    } finally {
        br.close();
    }
}

나쁘지 않지만, 자원을 하나 더 사용한다면 어떨까?

 

개선된 firstLineOfFile.java

static void firstLineOfFile(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 {
            if (out != null) out.close();
        }
    } finally {
        if (in != null) in.close();
    }
}

try-finally를 사용하여 자원을 닫아줄 때의 문제는 다음과 같다.

 

  • 사용 자원이 여러 개인 경우 코드가 중첩되어 지저분해진다.

    • 예외 처리를 위해 finally 블럭에서 특정 자원이 null이 아닐 때close()하는 조건문 등.
  • try 블록에서 예외가 발생했을 때 finally 블록의close()에서도 두 번째 예외가 발생하는 경우.

    • 두 번째 예외로 인해 첫 번째 예외 정보가 남지 않아 디버깅이 어려워진다.

2. try-with-resources

* try-with-resources - 자원을 회수하는 최선책!

TopLineWithDefault.java

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

AutoCloseable 인터페이스를 구현한 자원은 try-with-resources를 통해 간단하게 관리된다.

 

try 블록의 작업이 완료되거나 예외가 발생했을 때 자동으로close()가 호출되어 자원이 해제된다.

 

만약readLine()과close()모두 예외가 발생하면readLine()예외가 기록된다.

 

그러나close()예외 정보는 버려지지 않고readLine()예외에 담아진(suppressed)된 상태이기 때문에 언제든 확인할 수 있다.

 

Java 8 까지는 try-with-resources를 사용하기 위해 try 블록의 소괄호에 자원을 할당해야 했다.

 

* 복수의 자원을 처리하는 try-with-resources - 짧고 매혹적이다!

copy.java

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

* try-with-resources를 catch 절과 함께 스는 모습

TopLineWithDefault.java

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

Java 9 부터는 try 블록 밖에서 선언된 객체를 참조할 수 있다.

요약

꼭 회수해야 하는 자원을 다룰 떄는 try-finally 말고, try-with-resourcs를 사용하자.
예외는 없다. 코드는 더 짧고 부명해지고, 만들어지는 예외 정보도 훨씬 유용하다.

 

try-finally로 작성하면 실용적이지 못할 만큼 코드가 지저분해지는 경우라도 try-with-resourcs로는
정확하고 쉽게 저원을 회수할 수 있다.