언어/Effective Java

이펙티브 자바 Item 9 - try-finally 대신 try-with-resources를 사용하라

now0204 2024. 5. 28. 12:04

 

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

ex)InputStream, OutputStream, java.sql.Connection

 

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

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

 


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();
	}
}
  • 자원이 많아지면 지저분해짐!! 가독성도 떨어진다.

try-with-resources 

 

이 구조를 사용하려면, 해당 자원이 AutoCloseable인터페이스를 구현해야한다. 

 

이 메서드는 단순히 close 메서드 하나만 정의한 인터페이스이다. 닫아야 하는 자원을 뜻하는 클래스를 작성한다면 해당 인터페이스를 구현해야 한다.

 

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);
	}
}
  • 간결하게 선언 및 사용이 가능하다.

* AutoCLoseable 구현 

public class MyResource implements AutoCloseable {
	public void doSomething() throws FirstError {
		System.out.println("doing something");
		throw new FirstError();
	}

	@Override
	public void close() throws SecondException {
		System.out.println("clean my resource");
		throw new SecondError();
	}
}

public class MyResource implements AutoCloseable {
	public void doSomething() throws FirstError {
		System.out.println("doing something");
		throw new FirstError();
	}

	@Override
	public void close() throws SecondException {
		System.out.println("clean my resource");
		throw new SecondError();
	}
}



public class AppRunner {
	public static void main(String[] args) {
		MyResource myResource = new MyResource();
		try {
			myResource.doSomething();
		} finally {
			myResource.close();
		}
	}
}
  • 이와 같이 작성하면, try블럭과 finally 블록에서 모두 예외가 발생할 수 있는데
  • finally 예외가 try의 예외를 집어삼켜 버린다.
  • 스택 추적에서 첫 번째 예외에 관한 정보가 남지 않게 되어, 실제 시스템에서 디버깅이 어렵다.
public class AppRunner {
	public static void main(String[] args) {
		try (MyResource myResource = new MyResource()){
			myResource.doSomething();
		}
	}
}
  • try-with-resources문을 사용하면, close의 예외가 숨겨지고 첫 번쨰 예외가 기록된다.
  • 실전에서는 예외 하나만 보존되고 여러 개의 예외가 숨겨질 수 있다.
  • 이렇게 숨겨진 예외들은 스택 추적 내역에 suppressed 꼬리표를 달고 출력된다.
  • 자바7에서 Throwable에 추가된 getSuppressed 메서드를 쓰면 프로그램 코드에서 가져올 수 있수도 있다.!

출처: https://marxsoftware.blogspot.com/2012/04/java-7s-support-for-suppressed.html

 


참고자료 

 

https://marxsoftware.blogspot.com/2012/04/java-7s-support-for-suppressed.html

 

Java 7's Support for Suppressed Exceptions

A new constructor and two new methods were added to the Throwable class (parent of Exception and Error classes) in JDK 7 . The new constr...

marxsoftware.blogspot.com

https://velog.io/@lychee/%EC%9D%B4%ED%8E%99%ED%8B%B0%EB%B8%8C-%EC%9E%90%EB%B0%94-%EC%95%84%EC%9D%B4%ED%85%9C-9.-try-finally-%EB%8C%80%EC%8B%A0-try-with-resources%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EB%9D%BC

 

[이펙티브 자바] 아이템 9. try-finally 대신 try-with-resources를 사용하라

자바 라이브러리에는 close 메서드를 호출해 직접 닫아줘야 하는 자원이 많다.ex) InputStream, OutputStream, java.sql.Connection자원 닫기는 클라이언트가 놓치기 쉬워서 예측할 수 없는 성능 문제로 이어지

velog.io