[Effective Java] 챕터9. try-finally보다는 try-with-resources 를 사용하라

Posted by 김성철

Effective Java - 챕터9. try-finally보다는 try-with-resources 를 사용하라

자바 라이브러리에서는 close 메소드를 호출해 직접 닫아줘야 하는 자원들이 많음  
InputStream , OutputStream , java.sql.Connection 등이 있음  
자원 닫기는 클라ㅏ이언트가 놓치기 쉬워서 예측할 수 없는 성능 문제로 이어지기도 함  
이러한 자원 중 상당수가 안전망으로 finalizer 를 활용하고 있지만 finalizer는 그리 믿을만하지 못함 (챕터8 참고)  
  
전통적으로 자원이 제대로 닫힘을 보장하는 수단으로는 try-finally가 쓰였음  
  
	- try-finally 을 사용한 예제 코드이며, 최선의 방책이 아닌 코드  
	=================================================================================================================  
	static String firstLineOfFile(String path)throws IOException {  
		BufferedReader br = new BufferedReader(new FileReader(path));  
		try{  
			return br.readLine();  
  
		}finally{  
			br.close();  
		}  
	}  
	=================================================================================================================  
  
	- 위의 방식에서 자원을 추가로 사용한 코드  
	=================================================================================================================  
	static void copy(String src, String dst)throws IOException {  
  
		Input Stream 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 블록과 finally 블록에서 모두 발생할 수 있는데, 기기에 물리적인 문제가 생긴다면 firstLineOfFile 메서드 안에서  
readLine 메소드가 예외를 던지고 같은 이유로 close 메소드도 실패함  
  
이러한 상황이라면 두번째 예외가 첫 번째 예외를 완전이 집어삼켜 버림  
  
그러면 스택 추적 내영에서 첫 번째 예외에 관한 정보는 남지 않게 되어, 실제 시스템에서의 디버깅을 몹시 어렵게 함  
  
이러한 문자들은 try - with - resources 를 사용하면 해결 할 수 있음  
이 구조를 사용하려면 AutoCloseable 인터페이스를 구현해야 함.  
단순히 void를 반환하는 close 메소드를 하나만 덩그러니 정의한 인터페이스이며,  
자바 라이브러리와 서드파티 라이브러리들의 수많은 클래스와 인터페이스가 이미 AutoCloseable을 구현하거나 확장해줬음  
  
	- try-with-resources 코드  
	=================================================================================================================  
	static String firstLineOfFile(String path)throws IOException {  
		try(BufferedReader br = new BufferedReader(new FileReader(path))){  
			return br.readLine();  
		}  
	}  
	=================================================================================================================  
  
	- 복수의 자원을 처리하는 try-with-resources 코드  
	=================================================================================================================  
	static void copy(String src, String dst)throws IOException {  
		try(Input Stream 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 코드가 짧고 읽기 수월할 뿐 아니라 문제를 진단하기도 훨씬 좋다.  
catch까지 적용하면 아래의 코드가 된다  
  
	- try-with-resources 코드에 cathc 까지 적용  
	=================================================================================================================  
	static String firstLineOfFile(String path, String defaultVal) {  
		try(BufferedReader br = new BufferedReader(new FileReader(path))){  
			return br.readLine();  
		}catch(IOException e){  
			return defaultVal;  
		}  
	}  
	=================================================================================================================  

정리

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