[아이템 9] try-finally 보다는 try-with-resources를 사용하라

  • 자바 라이브러리에는 close 메서드를 호출해 직접 닫아줘야 하는 자원이 많음(InputStream, OutputStream, java.sql.Collection 등)

  • finalizer를 사용해도 되지만 아이템 8에서 봤듯이 믿을만한 요소가 아님

  • 전통적으로는 try-finally를 사용함

static String firstLineOfFile(String path) throws IOException {
		BufferedReader br = new BufferedReader(new FileReader(path));
		try {
				return br.readLine();
		} finally {
				br.close();
		}
}
  • 여기서 위의 try-finally에서 자우너을 하나 더 사용하면 아래와 같이 됨(하나 쓴 걸로 지저분해짐)
static String firstLineOfFile(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를 통해서 해결할 수 있음, 이 구조를 사용하기 위해서 해당 자원은 AutoCloseable 인터페이스를 구현해야함, 단순히 void를 반환하는 close 메서드하나만 정의한 인터페이스임

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

  • try-with-resource로 고쳐쓰면 아래와 같이 쓸 수 있음

static String firstLineOfFile(String path) throws 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 InputStream(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);
		}
}
  • 위와 같이 처리한다면 문제를 진단하기 수월해짐, 만약 readLine과 close에 예외가 발생하면 close에서 발생한 예외는 숨겨지고, readLine에서 발생한 예외는 기록됨

  • 이렇게 보여줄 예외 하나만 보존되고 여러 개의 다른 예외가 숨겨질 수 있음, 이렇게 숨겨진 예외들도 그냥 버려지지 않고 스택 추적 내역에 숨겨졌다는 꼬리표를 달고 출력됨, Throwable에 추가된 getSuppressed 메서드를 이용해서 코드를 가져올 수 있음

  • 여기서 catch를 추가해서 쓸 수 있음, try문을 중첩하지 않고도 다수의 예외를 처리 가능함

static String firstLineOfFile(String path) throws IOException {
		try (BufferedReader br = new BufferedReader(new FileReader(path))) {
				return br.readLine();
		} catch (IOException e) {
				return defaultVal;
		}
}
  • 꼭 회수해야 하는 자원을 다룰 때는 try-finally 말고 try-with-resources를 사용하자, 예외는 없음, 코드는 더 짧고 분명해지고, 만들어지는 예외 정보도 훨씬 유용함

  • try-finally로 작성하면 실용적이지 못할 만큼 코드가 지저분해지는 경우라도, try-with-resources로는 정확하고 쉽게 자원을 회수할 수 있음

좋은 웹페이지 즐겨찾기