자바 라이브러리에는 close 메서드를 호출해 직접 닫아줘야 하는 자원이 많다. ex) InputStream, OutputStream, DB Connection ...
item8에서 알 수 있듯이 안전망으로 finalizer를 활용할 수 있으나, finalizer는 확실하게 믿을 순 없다
이를 보완하기 위해 try-with-resources를 사용하도록 하자
// 둘 이상의 자원에서 try-finally: 중첩 되어 지저분해 보이고, exception이 일어난 경우 디버깅이 어렵다
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();
}
}
위 예시에서 예외는 try 블록과 finally 블록 모두에서 발생할 수 있다.
예를 들어 FileOutputStream 메서드가 예외를 던지고, 같은 이유로 close 메서드도 실패할 때,
두 번째 예외가 첫 번째 예외를 완전히 집어삼켜 버린다. => 스택 추적 내역에 첫 번째 예외에 관한 정보X
// try-with-resources: 자원을 회수하는 올바른 선택, 코드는 더 짧고 분명해지며, 예외 케이스도 유용하게 볼 수 있다.
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);
}
}
}
Java7 부터 Closeable리소스 와 관련하여 아래와 같이 키워드 뒤의 괄호 안에 리소스를 만들 수 있다.
try (initialize resources here) {
...
}
그리고 코드 블록이 완료되면 자동으로 닫히기 때문에 finally가 필요 없다 .
이 구조를 사용하려면 해당 자원이 AutoCloseable 인터페이스를 구현해야 한다.
AutoCloseable은 단순히 void를 반환하는 close 메서드 하나만 덩그러니 정의한 인터페이스다.
이 경우는 try-finally와는 다르게 양쪽에서 예외가 발생하면, close에서 발생한 예외는 숨겨지고 맨 처음에 발생한 예외가 기록된다.
단, 이렇게 숨겨진 예외들도 그냥 버려지지는 않고, 스택 추적 내역에 ‘숨겨졌다 (suppressed)’는 꼬리표를 달고 출력된다.
또한, 자바 7에서 Throwable에 추가 된 getSuppressed 메서드를 이용하면 프로그램 코드에서 가져올 수도 있다.
catch (Throwable e) {
Throwable[] suppExe = e.getSuppressed();
for (int i = 0; i < suppExe.length; i++) {
System.out.println("Suppressed Exceptions:");
System.out.println(suppExe[i]);
}
}
보통의 try-finally에서처 럼 try-with-resources에서도 catch 절을 쓸 수 있다. catch 절 덕분에 try 문을 더 중첩하지 않고도 다수의 예외를 처리할 수 있다.
참고로, 스트림을 닫을 때에는 IOUtils를 쓰는 방법도 있다. (대신 이 방법을 사용할 경우 Exception이 제대로 나오지 않음)
closeQuietly는 Closeable의 varargs 타입을 파라미터로 받고, 이 closeable들을 모두 닫는다.
- try-catch-finally
package com.example.sypark9646.item09;
import static org.apache.tomcat.util.http.fileupload.IOUtils.copy;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public class TryCatchFinallyTest {
private static String root =
"/Users/soyeon/Documents/GitHub/Effective_Java_Study/sypark9646/src/main/java/com/example/sypark9646/item09";
private static String inputFileName = root + "/testin.txt";
private static String outputFileName = root + "/testout_try_catch_finally.txt";
public static void main(String[] args) throws IOException {
try {
final InputStream in = new FileInputStream(inputFileName);
try {
final OutputStream out = new FileOutputStream(outputFileName);
try {
copy(in, out);
out.flush();
} finally {
out.close();
}
} finally {
in.close();
}
} catch (IOException exc) {
throw new IOException(exc);
}
}
}
- try-with-resources
package com.example.sypark9646.item09;
import static org.apache.tomcat.util.http.fileupload.IOUtils.copy;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public class TryWithResourcesTest {
private static String root =
"/Users/soyeon/Documents/GitHub/Effective_Java_Study/sypark9646/src/main/java/com/example/sypark9646/item09";
private static String inputFileName = root + "/testin.txt";
private static String outputFileName = root + "/testout_try_with_resources.txt";
public static void main(String[] args) throws IOException {
try (
final InputStream in = new FileInputStream(inputFileName);
final OutputStream out = new FileOutputStream(outputFileName);
) {
copy(in, out);
out.flush();
} catch (IOException exc) {
throw new IOException(exc);
}
}
}
'책을 읽자 > Effective Java' 카테고리의 다른 글
Item11: equals를 재정의하려거든 hashCode도 재정의하라 (0) | 2021.02.20 |
---|---|
Item10: equals는 일반 규약을 지켜 재정의하라 (0) | 2021.02.20 |
Item8: finalizer와 cleaner 사용을 피하라 (0) | 2021.02.16 |
Item7: 다 쓴 객체 참조를 해제하라 (0) | 2021.02.13 |
Item6: 불필요한 객체 생성을 피하라 (0) | 2021.01.30 |