728x90
아이템9. try-finally보다는 try-with-resources를사용하라
자바 라이브러리에서는 close 메서드를 호출해 직접 닫아줘야 하는 자원이 많다.
InputStream, OutputStream, java.sql.Connection 등이 좋은 예다.
하지만 자원 닫기(close)는 클라이언트가 놓치기 쉬워 예측할 수 없는 성능 문제로 이어지는 경우가 많다.
그런 자원 중 상당수가 안전망으로 finalizer를 사용하곤 있지만 finalizer는 그리 믿을만하지 못하다.
전통적인 자원 회수 방식 : try-finally
- 보통 자원이 제대로 닫히는 것을 보장하는 수단으로 사용됨.
- 예외가 발생하거나 메서드에서 반환되는 경우를 포함하는 수단으로 try-finally가 쓰였다.
try-finally는 더 이상 자원을 회수하는 최선의 방책이 아니다!
public class Item9_1 {
static String firstLineOfFile(String path) throws IOException {
BufferedReader br = new BufferedReader(new FileReader(path));
try {
return br. readLineO;
} finally {
br.closeO;
}
}
}
자원을 둘 이상 사용한다면 try-finally 방식은 너무 지저분하다
public class Item9_2 {
private static final int BUFFER_SIZE = 0; // 편의상 0으로 지정
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();
}
}
}
- 많은 프로그래머들이 저지르는 실수이다. (2007년 당시 자바 라이브러리에서 close 메서드를 제대로 구현한 비율은 겨우 1/3)
- try-finally 문을 제대로 사용한 앞의 두 코드 예제에는 미묘한 결점이 있다.
- 예외는 try 블록과 finally 블록 모두에서 발생할 수 있는데, 예컨대 기기에 물리적인 문제가 생긴다면 firstLineOfFile 메서드 안의 readLine 메서드가 예외를 던지고, 같은 이유로close 메서드도 실패할 것이다.
- 이런 상황이라면 두 번째 예외가 첫 번째 예외를 완전히 집어삼켜 버린다.
- 그러면 스택 추적 내역에 첫 번째 예외에 관한 정보는 남지 않게 되어, 실제 시스템에서의 디버깅을 몹시 어렵게 한다(일반적으로 문제를 진단하려면 처음 발생한 예외를 보고 싶을 것이다).
try-with-resources - 자원을 회수하는 최선책!
- 자바 7 투척한 try-with-resources덕에 모두 해결되었다.
- 이 구조를 사용하려면 해당 자원이 AutoCloseable 인터페이스를 구현해야 한다.
- 단순히 void를 반환하는 close 메서드 하나만 덩그러니 정의한 인터페이스다.
-
닫아야 하는 자원을 뜻하는 클래스를 작성한다면 AutoCloseable을 반드시 구현하기 바란다.
public class Item9_1 { // 위의 9_1 예제에 try-with-resources를 적용한 모습
static String firstLineOfFile(String path) throws IOException {
try (BufferedReader br = new BufferedReader(new FileReader(path))) {
return br.readLine();
}
}
}
public class Item9_2 { // 위의 9_2 예제에 try-with-resources를 적용한 모습
private static final int BUFFER_SIZE = 0; // 편의상 0으로 지정
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 버전이 짧고 읽기 수월할 뿐 아니라 문제를 진단하기도 훨씬 좋다.
try-with-resources를 catch 절과 함께 쓰는 모습
public class Item9_3 {
static String firstLineOfFile(String path, String defaultVal) {
try (BufferedReader br = new BufferedReader(new FileReader(path))) {
return br.readLine();
} catch (IOException e) {
return defaultVal;
}
}
}
- 보통의 try-finally에서처럼 try-with-resources에서도 catch 절을 쓸 수 있다.
- catch 절 덕분에 try문을 더 중첩하지 않고 다수의 예외를 처리할 수 있다.
핵심 정리
꼭 회수해야 하는 자원올 다룰 때는 try-finally 말고,try-with-resources를 사용하자. 예외는 없다.
코드는 더 짧고 분명해지고, 만들어지는 예외 정보도 월씬 유용하다.
try- finally로 작성하면 실용적이지 못할 만큼 코드가 지저분해지는 경우라도,try-with- resources로는 정확하고 쉽게 자원을 회수할 수 있다.
'도서 > 정리' 카테고리의 다른 글
[이펙티브 자바] 아이템16. public 클래스에서는 public 필드가 아닌 접근자 메서드를 사용하라 (0) | 2023.07.06 |
---|---|
[이펙티브 자바] 아이템15. 클래스와 멤버의 접근 권한을 최소화하라 (0) | 2023.07.05 |
[이펙티브 자바] 아이템7. 다 쓴 객체 참조를 해제하라 (0) | 2023.05.17 |
[이펙티브 자바] 아이템6. 불필요한 객체 생성을 피하라 (0) | 2023.05.15 |
[이펙티브 자바] 아이템5. 자원을 직접 명시하지 말고 의존 객체 주입을 사용하라 (0) | 2023.05.09 |