포스트

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

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

자원 닫기, try-finally로 하다 보면

파일·소켓·DB 커넥션처럼 다 쓰면 close()를 호출해야 하는 자원이 많습니다. 전통적으로는 try-finally로 닫았는데, 자원이 둘 이상이면 코드가 금방 지저분해집니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// ❌ 자원 두 개 → 중첩 try-finally
InputStream in = new FileInputStream(src);
try {
    OutputStream out = new FileOutputStream(dst);
    try {
        byte[] buf = new byte[1024];
        int n;
        while ((n = in.read(buf)) >= 0) out.write(buf, 0, n);
    } finally {
        out.close();
    }
} finally {
    in.close();
}

더 큰 문제: 예외가 삼켜진다

try-finally엔 미묘한 함정이 있습니다. try 블록과 finally의 close()에서 둘 다 예외가 나면, 나중에 난 close() 예외가 앞의 진짜 원인을 덮어버립니다. 디버깅할 때 정작 중요한 첫 예외가 사라지는 거죠.

해결: try-with-resources

AutoCloseable을 구현한 자원이라면 try-with-resources로 훨씬 깔끔하게 쓸 수 있습니다.

1
2
3
4
5
6
7
8
// ✅ try-with-resources
try (InputStream in = new FileInputStream(src);
     OutputStream out = new FileOutputStream(dst)) {
    byte[] buf = new byte[1024];
    int n;
    while ((n = in.read(buf)) >= 0) out.write(buf, 0, n);
}
// 블록을 벗어나면 선언의 '역순'으로 close()가 자동 호출됨
  • 자원을 () 안에 선언하면, 블록이 끝날 때 자동으로 close() 됩니다(여러 개면 역순).
  • 코드가 짧고 명확해집니다.

억제된 예외(Suppressed)

try-with-resources는 예외 문제도 해결합니다. try 본문과 close() 양쪽에서 예외가 나면, 본문의 예외가 주(primary) 가 되고 close()의 예외는 “억제됨(suppressed)” 으로 함께 보존됩니다. 스택 트레이스에 Suppressed:로 표시되어 둘 다 볼 수 있죠.

1
2
3
4
5
6
7
try {
    // ...
} catch (IOException e) {
    for (Throwable sup : e.getSuppressed()) {
        // close()에서 발생해 억제된 예외들
    }
}

catch 절도 함께 쓸 수 있어, 자원 정리와 예외 처리를 한 구조로 묶을 수 있습니다.

정리

  • 자원 회수는 try-finally 대신 try-with-resources.
  • 코드가 짧고, 여러 자원도 깔끔하게(역순 자동 close).
  • 억제된 예외 메커니즘으로 진짜 원인 예외가 사라지지 않는다.
  • 전제: 자원이 AutoCloseable을 구현해야 한다.
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.

시리즈 · 이펙티브 자바 (4편)

  1. [이펙티브 자바] 아이템 6: 불필요한 객체 생성을 피하라
  2. [이펙티브 자바] 아이템 7: 다 쓴 객체 참조를 해제하라
  3. [이펙티브 자바] 아이템 8: finalizer와 cleaner 사용을 피하라
  4. [이펙티브 자바] 아이템 9: try-finally보다 try-with-resources를 사용하라 지금 보는 글