KoreanFoodie's Study

10-2. 자바 예외처리 : 자동 리소스 닫기, 예외 떠넘기기 본문

Tutorials/Java

10-2. 자바 예외처리 : 자동 리소스 닫기, 예외 떠넘기기

GoldGiver 2021. 9. 19. 09:12

자동 리소스 닫기

자바 7부터는 try-with-resources를 사용하면 예외 발생 여부와 상관없이 상요해던 리로스 객체(각종 입출력 스트림, 서버 소켓, 각종 채널)의 close() 메소드를 호출해서 안전하게 리소스를 닫아준다.

리소스라는 말이 여러 가지 의미가 있겠지만 여기서는 데이터를 읽고 쓰는 객체라고 생각해 두자. 

FileInputStream fis = null;
try {
	fis = new FileInputStream("file.txt");
} catch (IOException e) {
	...
} finally {
	if (fis != null) {
		try {
			fis.close();
		} catch (IOException e) {}
	}
}

파일을 사용할 때는 try-catch를 사용해서 close( ) 메소드를 예외 처리해야 하므로 다소 복잡하다. try-with-resources를 사용하면 다음과 같이 간단해진다.

try(FileInputStream fis = new FileInputStream("file.txt") {

} catch (IOException e) {
	...
}

만일 복수 개의 리소스를 사용해야 한다면 다음과 같이 쓸 수 있다.

try(
    FileInputStream fis = new FileInputStream("file.txt");
    FileOutputStream fos = new FileOutputStream("file2.txt")
    ) {

} catch (IOException e) {
	...
}

try-with-resources를 사용하기 위한 조건이 있는데, 리소스 객체는 java.lang.AutoCloseable 인터페이스를 구현하고 있어야 한다. API 도큐먼트에서 AutoCloseable 인터페이스를 찾아 "All Known Implementing Classes: "를 보면 어떤 리소스를 사용할 수 있는지 알 수 있다.

public class FileInputStream implements AutoCloseable {
	private String file;
	
	public FileInputStream(String file) {
		this.file = file;
	}
	
	public void read() {
		System.out.println(file + "을 읽습니다.");
	}
	
	@Override
	public void close() throws Exception {
		System.out.println(file + "을 닫습니다.");
	}

}
public class TryWithResourceExample {

	public static void main(String[] args) {
		try (FileInputStream fin = new FileInputStream("file.txt")) {
			fin.read();
			throw new Exception();
		} catch(Exception e) {
			System.out.println("예외 처리 코드가 실행되었습니다.");
		}
	}
}

 

예외 떠넘기기

메소드 내부에서 예외가 발행할 수 있는 코드를 작성할 때 try-catch 블록으로 예외를 처리하는 것이 기본이지만, 경우에 따라서는 메소드를 호출한 곳으로 예외를 떠넘길 수도 있다. 이때 사용하는 키워드가 throws이다. throws 키워드는 메소드 선언부 끝에 작성되어 메소드에서 처리하지 않은 예외를 호출한 곳으로 떠넘기는 역할을 한다. throws 키워드 뒤에는 떠넘길 예외 클래스를 쉼표로 구분해서 나열해주면 된다.

리턴타입 메소드명(매개변수, ...) throws 예외클래스1, 예외클래스2, ... {
}

발생할 수 있는 예외의 종류별로 throws 뒤에 나열하는 것이 일반적이지만, 다음과 같이 throws Exception만으로 모든 예외를 간단히 떠넘길 수도 있다.

리턴타입 메소드명(매개변수, ...) throws Exception {
}

throws 키워드가 붙어있는 메소드는 반드시 try 블록 내에서 호출되어야 한다. 그리고 catch 블록에서 떠넘겨 받은 예외를 처리해야 한다. 다음 코드는 throws 키워드가 있는 method2()를 method1()에서 호출하는 방법을 보여준다.

public void method1() {
	try {
		method2();
	} catch(ClassNotFoundException e) {
		// 예외 처리 코드
		System.out.println("클래스가 존재하지 않습니다.");
	}
}

public void method2() throws ClassNotFoundException {
	Class clazz = Class.forName("java.lang.String2");
}

method1()에서도 try-catch 블록으로 예외를 처리하지 않고 throws 키워드로 다시 예외를 떠넘길 수 있다. 그러면 method1()을 호출하는 곳에서 결국 try-catch 블록을 사용해서 예외를 처리해야 한다.

public void method1() throws ClassNotFoundException {
    method2();
}

main() 메소드에서도 throws 키워드를 사용해서 예외를 떠너길 수 있는데, 결국 JVM이 최종적으로 예외 처리를 하게 된다. JVM은 예외의 내용을 콘솔에 출력하는 것으로 예외 처리를 한다.

public class TryCatchFinallyExample {
	public static void main(String[] args) throws ClassNotFoundException {
 
	}
}

하지만 main에서 throws Exception을 붙이는 것은 좋지 못한 습관이다. 프로그램이 알 수 없는 예외 내용을 출력하고 종료하기 보다는 try-catch 블록으로 처리해주는 것이 바람직하다.


해당 포스팅은 이것이 자바다 (신용권 저) 를 참고하였습니다

 

Comments