- 자바 라이브러리에는 InputStream, OutputStream 그리고 java.sql.Connection과 같이 close를 이용해 직접 닫아줘야 하는 리소스가 많은데, 그런 리소스를 사용하는 클라이언트 코드가 보통 리소스 정리를 잘 안하거나 잘못하는 경우가 있다.
- 안전망으로 finalizer을 사용하는 경우가 많으나, item8과 같은 다양한 문제들이 있을 수 있다.
=> 전통적인 방법 : try-finally
- 자원이 제대로 닫힘을 보장하는 수단이다.
예외가 발생하거나, 메서드에서 반환되는 경우도 포함한다.
- 자원이 여러개가 되는 경우, try~finally의 사용은 코드를 지저분하게 만들 수 있다.
- 또한, 예외는 try, finally모두에서 발생할 수 있는데, 이런 상황에서 두번째 예외가 첫번째 예외를 집어삼킨다. 이는 스택 추적 내역에서 첫 번째 예외에 관한 정보를 남지 않게 하여 실제 시스템 상에서 디버깅을 어렵게 한다.
- 의도적으로 예외가 발생함에도 불구하고, 두 번째 예외만 표시되는 것이다.
=> 해결 방안 : try-with-resources !
- try-with-resources를 구조를 사용하기 위해서는 AutoCloseable 인터페이스를 구현해야 한다.
- 단순히 void를 반환하는 close method 하나만 정의한 인터페이스.
- 닫아야 하는 자원을 가지는 클래스를 작성한다면, AutoCloseable를 반드시 구현해야 한다.
[ 자원이 하나, 두개일 때 try-finally와 try-with-resources의 구현 차이]
//자원이 하나인 경우
static String firstLineOfFile(String path) throws IOException {
BufferedReader br = new BufferedReader(new FileReader(path));
try {
return br.readLine();
} finally {
br.close();
}
}
- readline()에서 첫 번째 예외가, close()에서 두 번째 예외가 발생한다.
- close에서 발생한 예외가 readline()에서 발생한 예외를 덮는다. 따라서 스택추적 내역에서 readline(), 첫 번째 예외의 정보가 남지 않게 된다.
//자원이 하나인 경우 - 수정
static String firstLineOfFile(String path) throws Exception {
try (BufferedReader br = new BufferedReader (
new FileReader(path))) {
return br.readLine();
}
}
- readline()에서 첫 번째 예외가, close()에서 두 번째 예외가 발생한다.
- close()에서 발생한 예외는 숨겨지고, readline()에서 발생한 예외가 기록된다.
- 프로그래머에게 보여줄 예외 (readline())하나만 보존되더라도, 숨겨진 다른 예외(close())는 버려지지 않고 스택 추적 내역에 숨겨져있다는 (suppressed)표시를 달고 출력된다.
- catch절을 쓸 수 있기 때문에, try문을 더 중첩하지 않고도 다수의 예외처리가 가능하다.
//자원이 두개인 경우
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();
}
}
//자원이 두개인 경우 - 수정
static void copy(String src, String det) 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 를 사용하자. → 반드시. 예외 없음.
- 코드는 더 짧아지고 분명해지고, 만들어지는 예외정보도 훨씬 유용하다.
- try-finally 보다 정확하고 쉽게 자원을 회수할 수 있다.
'JAVA > Effective Java' 카테고리의 다른 글
item 11. equals를 재정의하려거든 hashCode도 재정의하라 (0) | 2022.04.19 |
---|---|
item 10. equals는 일반 규약을 지켜 재정의하라 (0) | 2022.04.19 |
item 8. finalizer과 cleaner 사용을 피하라. (0) | 2022.04.16 |
item7. 다 쓴 객체 참조를 해제하라 (0) | 2022.04.16 |
item 6. 불필요한 객체 생성을 피하라 (0) | 2022.04.12 |
댓글