본문 바로가기
JAVA/Effective Java

item 9. try-finally 보다는 try-with-resources를 사용하라

by Garonguri 2022. 4. 16.
728x90

- 자바 라이브러리에는 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 보다 정확하고 쉽게 자원을 회수할 수 있다.
728x90

댓글