아이템 9 - try-finally 보다 try-with-resouces 를사용하라 - 핵심 정리
이 글은 백기선 님의 이펙티브 자바 강의와 이펙티브 자바 3 / E 편을 참고하여 작성하였습니다.
try-finally 는 자바7 버전 부터 더이상 최선의 방법이 아니다.
try-with-resouces 를 사용해야 한다.
public class TopLine {
// 코드 9-1 try-finally - 더 이상 자원을 회수하는 최선의 방책이 아니다! (47쪽)
static String firstLineOfFile(String path) throws IOException {
BufferedReader br = new BufferedReader(new FileReader(path));
try {
return br.readLine();
} finally {
br.close();
}
}
public static void main(String[] args) throws IOException {
String path = args[0];
System.out.println(firstLineOfFile(path));
}
}
위의 코드는 finally 블럭에서 자원을 반납하는 코드이다.
이 코드에서 딱히 문제가 있는 건 아니다.
자원을 반납하려는 객체가 여러개인 경우에는 어떻게 할까?
public class Copy {
private static final int BUFFER_SIZE = 8 * 1024;
// 코드 9-2 자원이 둘 이상이면 try-finally 방식은 너무 지저분하다! (47쪽)
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();
}
}
public static void main(String[] args) throws IOException {
String src = args[0];
String dst = args[1];
copy(src, dst);
}
}
자원을 반납하려는 객체가 여러개인 경우에는 위처럼 코드를 작성하면 된다.
먼저 OutputStream 을 반납하고 그 뒤에 InputStream 을 반납한다.
public class Copy {
private static final int BUFFER_SIZE = 8 * 1024;
// 코드 9-2 자원이 둘 이상이면 try-finally 방식은 너무 지저분하다! (47쪽)
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();
}
}
public static void main(String[] args) throws IOException {
String src = args[0];
String dst = args[1];
copy(src, dst);
}
}
만약 위처럼 try - catch 블럭을 중첩하지 않고 하나로만 작성하게 된다면
자원이 반납처리되지 않을 수 있다.
만약 in.close();
에서 에러가 난다면 그 다음 줄인 out.close();
은 실행되지 않는다.
try - catch 블럭을 중첩 하면 in.close();
에서 에러가 나더라도 out.close();
를 실행하게 된다.
public class TopLine {
// 코드 9-3 try-with-resources - 자원을 회수하는 최선책! (48쪽)
static String firstLineOfFile(String path) throws IOException {
try (BufferedReader br = new BufferedReader(
new FileReader(path))) {
return br.readLine();
}
}
public static void main(String[] args) throws IOException {
String path = args[0];
System.out.println(firstLineOfFile(path));
}
}
권장하는 방법은 try-with-resouces 를 사용하는 것이다.
코드가 훨씬 간결해지게 된다.
close()
를 우리가 실행하지 않아도 된다.
BufferedReader 가 AutoCloseable 를 상속한 Closeable 을 구현했기 때문이다.
이런 방법은 자원할 리소스가 여러개 일때 더 빛을 발한다.
public class Copy {
private static final int BUFFER_SIZE = 8 * 1024;
// 코드 9-4 복수의 자원을 처리하는 try-with-resources - 짧고 매혹적이다! (49쪽)
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);
}
}
public static void main(String[] args) throws IOException {
String src = args[0];
String dst = args[1];
copy(src, dst);
}
}
InputStream 과 OutputStream 의 객체 둘다 close 메서드를 실행해준다.
이것보다 중요한 장점이 있다.
바로 예외를 잡아먹지 않는다는 것이다.
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();
}
}
BufferedReader 를 상속한 BadBufferedReader 클래스를 작성한다.
readLine(), close()
를 실행할 때 에러를 발생시키도록 한다.
public class TopLine {
// 코드 9-1 try-finally - 더 이상 자원을 회수하는 최선의 방책이 아니다! (47쪽)
static String firstLineOfFile(String path) throws IOException {
BufferedReader br = new BadBufferedReader(new FileReader(path));
try {
return br.readLine(); // 에러 발생
} finally {
br.close(); // 에러 발생
}
}
public static void main(String[] args) throws IOException {
String path = args[0];
System.out.println(firstLineOfFile(path));
}
}
BadBufferedReader 를 사용해 BufferedReader 객체를 생성한다.
br.readLine();
실행할 때 에러가 발생하게 되고, br.close();
를 실행할 때도 에러가 발생하게 된다.
코드를 실행해보면 br.close();
에서 발생한 에러만 보이게된다.
가장 나중에 발생한 예외만 보인다.
가장 처음에 발생한 예외가 중요할 때가 많지만 가장 나중에 발생한 예외만 보이게 된다는 것이다.
이게 try - finally 를 사용했을 때 발생하는 예외가 먹히는 문제이다.
물론 try - catch - finally 를 이용해 가장 먼저 발생한 예외가 안먹히게 작성할 수 있지만
코드가 많이 지저분해진다.
public class TopLine {
// 코드 9-1 try-finally - 더 이상 자원을 회수하는 최선의 방책이 아니다! (47쪽)
static String firstLineOfFile(String path) throws IOException {
try(BufferedReader br = new BadBufferedReader(new FileReader(path))) {
return br.readLine();
}
}
public static void main(String[] args) throws IOException {
System.out.println(firstLineOfFile("pom.xml"));
}
}
try-with-resouces 를 사용하면 예외가 먹히지 않는다.
가장 먼저 발생한 예외가 보이고 후속으로 발생한 예외도 보인다.
감춰진 예외는 Suppressed 뒤에 붙어서 로그에 출력된다.
물론, try-with-resouces 를 사용해도 catch, finally 블록을 사용할 수 있다.
'개발 공부 > Java' 카테고리의 다른 글
이펙티브 자바 아이템 10 - equals 는 일반 규약을 지켜 재정의하라 - 핵심 정리 (0) | 2022.10.19 |
---|---|
이펙티브 자바 아이템 9 - try-finally 보다 try-with-resouces 를 사용하라 - 완벽 공략 (0) | 2022.10.14 |
이펙티브 자바 아이템 8 - finalizer 와 cleaner 사용을 피하라 - 완벽 공략 (0) | 2022.10.14 |
이펙티브 자바 아이템 8 - finalizer와 cleaner 사용을 피하라 - 핵심 정리 (0) | 2022.10.12 |
이펙티브 자바 아이템 7 - 다 쓴 객체 참조를 해제하라 - 완벽 공략 (0) | 2022.10.07 |
댓글