본문 바로가기
Java

Item9 try-finally보다는 try-with-resources를 사용하라

by wwns 2023. 2. 2.
반응형

자바 라이브러리에는 `close`메서드를 호출해 직접 닫아줘야 하는 자원이 많다

ex) `InputStream`, `OutputStream`, `java.sql.Connection`

자원을 닫는 것은 클라이언트가 놓치기 쉬워서 예측할 수 없는 성능 문제로 이어지기도 한다.

전통적으로 자원이 제대로 닫힘을 보장하는 수단으로  try-finally가 쓰였다.


try-finally

public static String firstLineOfFile(String path) throw IOException {
    BufferedReader br = new BufferedReader(new FileReader(path));
    try {
        return br.readLine();
    } finally {
        br.close();
    }
}

자원을 하나 더 사용 혹은 자원이 둘 이상이라면? -> try-finally 방식은 너무 지저분해진다.

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();
	}
}

위의 두 코드에서의 결점은 무엇일까?

firstLineOfFile 메서드 안의 readLine 메서드가 예외를 던지고, 같은 이유로 close 메서드도 실패할 것이다.

이런 상황이라면 두 번째 예외가 첫 번째 예외를 완전히 집어삼켜 버린다.

두 번째 예외가 첫 번째 예외를 집어 삼켜 시스템에서의 디버깅을 몹시 어렵게 한다.

try-with-resources

try-finally의 문제점을 해결한 구조.

 

try-with-resources를 사용하려면 해당 자원이 AutoCloseable 인터페이스를 구현해야 한다.

자바 라이브러리와 서드파티 라이브러리들의 수많은 클래스와 인터페이스가 이미 AutoCloseable을 구현하거나 확장해 뒀다.
닫아야 하는 자원을 뜻하는 클래스를 작성한다면 AutoCloseable을 반드시 구현하도록.

try-with-resources를 사용해 재작성한 코드

public static String firstLineOfFile(String path) throw IOException {
    try (BufferedReader br = new BufferedReader(new FileReader(path))) {
        return br.readLine();
    }
}

복수의 자원 처리

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);
	}
}

다시 한번 firstLineOfFile 메서드를 생각해 보자. readLine과 close 호출 양쪽에서 예외가 발생하면, close에서 발생한 예외는 숨겨지고 readLine에서 발생한 예외가 기록된다.

  • 실전에서는 프로그래머에게 보여줄 예외 하나만 보존되고 여러 개의 다른 예외가 숨겨질 수 있다.
  • 숨겨진 예외들도 그냥 버려지지는 않고 스택 추적 내역에 `숨겨졌다(suppressed)`는 꼬리표를 달고 출력된다.
public static String firstLineOfFile(String path) throw IOException {
    try (BufferedReader br = new BufferedReader(new FileReader(path))) {
        return br.readLine();
    } catch (Exception e) {
        return defaultVal;
    }
}

catch 절을 사용하면 try문을 중첩하지 않고도 여러 자원에 대한 다수의 예외를 처리할 수 있다.


정리

  • 꼭 회수해야 하는 자원을 다룰 때는 try-finally 말고, try-with-resources를 사용. 예외는 없다!
  • try-finally로 작성하면 실용적이지 못할 만큼 코드가 지저분해지는 경우라도 try-with-resources로는 정확하고 쉽게 자원을 회수할 수 있다.

try-catch 구문을 배운 적이 있는데, 항상 try의 안에서 자원을 열고 닫았던 기억이 있다. 이번에 책을 통해서 정리를 하니 try-catch 구문을 왜 사용했는지, 조금 어색하게 사용하고 있었구나라는 것을 깨달았다.. 

이제 이해했으니 제대로 사용하자!

 

반응형

'Java' 카테고리의 다른 글

모던 자바 인 액션 - PART1  (0) 2023.08.19
자바 Type-Erasure  (0) 2023.04.30
Item8 finalizer와 cleaner 사용을 피하라  (0) 2023.01.15
Item7 다 쓴 객체 참조를 해제하라  (1) 2023.01.12
Item6 불필요한 객체 생성을 피하라  (0) 2022.12.09