[item#09] try-finally 보다는 try-with-resources 를 사용하라
try-finally 보다는 try-with-resources 를 사용하라
자바는 close메서드를 직접 달아줘야하는 자원이 많음 => 이걸 놓쳐서 성능문제로 이어지기도 함.
이를 위한 안전망으로 finalizer를 활용하는데 그리 믿을만하지 못함.
// 예제 1
static String firstLineOfFile(String path) throws IOException{
BufferedReader br = new BufferedReader(new FileReader(path));
try{
return br.readLine();
}finally{
br.close();
}
}
// 예제 2 : 자원이 둘 이상인 경우
static void copy(String src, String dst) throws IOEception{
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 문을 제대로 사용한 것이지만 미묘한 결점이 있다.
예외는 try 블록과 finally 블로 모두 발생할 수 있다.
예를 들어, 기기에 물리적 문제가 생기면 firstLineOfFile 메서드 안에서 readLine 메서드가 예외를 던지고,
같은 이유로 close 메서드도 실패함. => 두번째 예외가 첫번째 첫번째 예외를 집어 삼켜 디버깅을 어렵게함(첫번째예외의 로그가 안남음)
해결책 try-with-resource
자바 7에서 생긴 try-with-resource가 이런 문제를 해결해줌.
try-with-resource를 사용하기 위해서는 해당 자원이 AutoCloseable 인터페이스를 구현해야함.
단순히 void를 반환하는 close 메서드 하나만 저으이한 인터페이스임.
// try-with-resource 적용 예제1
static String firstLineOfFile(String path) throws IOException{
try(BufferedReader br = new BufferedReader(new FileReader(path))){
return br.readLine();
}
}
// try-with-resource 적용 예제2
static void copy(String src, String dst) throws IOException{
try(InputStream in - new FiileInputStream(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-resource 를 사용하므로써 코드도 읽기 수월하고 문제 진단도 훨씬 좋음.
적용예제1을 보면 readLine 과 close를 호출할때 둘다 예외가 발생하면 둘다 예외가 기록됨(숨겨진 예외는 suppressed 를 달고 출력)
또 자바 7에서 추가된 Throwable의 getSuppressed 메서드를 이용하면 프로그램 코드에서 가져올 수도 있음.
한번에 close 하면 안되나?
static void copy(String src, String dst) throws IOException {
InputStream in = new FileInputStream(src);
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 {
in.close();
out.close();
}
}
이렇게 사용할 경우 자원의 구멍이 생길 수 있음(leak).
in을 반납할 때 오류가 생겨버리면 out은 반납을 못함.
예외가 먹히는 상황
public class BadBufferedReader extends BufferedReader {
public BadBufferedReader(Reader in, int sz) {
super(in, sz);
}
public BadBufferedReader(Reader in) {
super(in);
}
@Override
public String readLine() throws IOException {
throw new CharConversionException();
}
@Override
public void close() throws IOException {
throw new StreamCorruptedException();
}
}
static String firstLineOfFile(String path) throws IOException {
BufferedReader br = new BadBufferedReader(new FileReader(path));
try{
return br.readLine();
}finally {
br.close();
}
}
readLine 과 close 둘다 오류가 떨어지는 상황에서 마지막에 발생한 오류만 보이게됨.
하지만 try-with-resource로 변경하면 예외가 다 보임.
퍼즐러 예외 처리 코드 실수
static void copy(String src, String dst) throws IOException {
InputStream in = new FileInputStream(src);
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 {
try {
out.close();
} catch (IOException e) {
// TODO 이렇게 하면 되는거 아닌가?
}
try {
in.close();
} catch (IOException e) {
// TODO 안전한가?
}
}
}
마지막 close 도 try-catch로 감싸져있으면 괜찮지 않나? => catch로 잡는 예외 외의 다른 예외인 경우에 문제가 생김
오히려 자원 하나당 try로 잡는게 더 안전함.
try-with-resource
try-with-resource 코드는 어떻게 작동할까?
static String firstLineOfFile(String path) throws IOException {
try(BufferedReader br = new BadBufferedReader(new FileReader(path))) {
return br.readLine();
}
}
위의 코드를 컴파일 하면 아래와 같은 코드로 컴파일해줌(실제 바이트 코드는 다르게 생김)
static String firstLineOfFile(String path) throws IOException {
BufferedReader br = new BufferedReader(new FileReader(path));
String var2;
try {
var2 = br.readLine();
} catch (Throwable var5) {
try {
br.close();
} catch (Throwable var4) {
var5.addSuppressed(var4);
}
throw var5;
}
br.close();
return var2;
}
'Study > Effective-Java' 카테고리의 다른 글
[item#11] equals를 재정의하려거든 hashCode도 재정의하라 (0) | 2023.11.29 |
---|---|
[item#10] eqauls는 일반 규약을 지켜 재정의하라. (1) | 2023.11.27 |
[item#08] finalizer와 cleaner 사용을 피하라 (2) | 2023.11.27 |
[item#07] 다 쓴 객체 참조를 해제하라. (1) | 2023.11.27 |
[item#06] 불필요한 객체 생성을 피하라 (2) | 2023.11.27 |
댓글