자바 7에서 새로 추가된
try-with-resources
를 사용하면 예외 발생 여부와 상관없이 사용했던 리소스 객체(각종 입출력 스트림, 서버 소켓, 소켓, 각종 채널)의close()
메소드를 호출해서 안전하게 리소스를 닫아준다.
여기에서 리소스는 데이터를 읽고 쓰는 객체를 말한다.
파일의 데이터를 읽는 FileInputStream
객체와 파일에 쓰는 FileOutputStream
은 리소스 객체라고 할 수 있다.
리소스 객체를 안전하게 닫기 위해서 자바 6 이전까지는 다음과 같이 복잡한 코드를 사용해야 했다.
FileInputStream fis = null;
try {
fis = new FileInputStream(“file.txt”);
…
} catch(IOException e) {
…
} finally {
if(fis != null) {
try {
fis.close();
} catch(IOException e) { }
}
}
finally
블록에서 다시 try-catch
를 사용해서 close()
메소드를 예외 처리해야 하므로 다소 복잡하게 보인다.try-with-resources
를 사용한 방법자바 7에서 추가된
try-wirh-resources
문을 사용법은 다음과 같다.try (파일을열거나자원을할당하는명령문){ ... }
try-with-resources
문을 사용하면 다음과 같이 간단해진다.
try(FileInputStream fis = new FileInputStream(“file.txt”) {
…
} catch(IOException e) {
…
}
try
블록이 정상적으로 실행을 완료했거나 도중에 예외가 발생하게 되면 자동으로 FileInputStream
의 close()
메소드가 호출된다. try {}
에서 예외가 발생하면 우선 close()
로 리소스를 닫고 catch
블록을 실행한다. 만약 복수 개의 리소스를 사용해야 한다면 다음과 같이 작성하면 된다.
try(
FileInputStream fis = new FileInputStream(“file1.txt”);
FileOutputStream fos = new FileOutputStream(“file2.txt”)) {
…
} catch(IOException e) {
…
}
try-with-resources
를 사용하기 위해서는 리소스 객체가 java.lang.AutoCloseable
인터페이스를 구현하고 있어야 한다. try-with-resources
는 AutoCloseable
에 정의되어있는 close()
를 자동으로 호출한다.
메소드 내부에서 예외가 발생할 수 있는 코드를 작성할 때 try-catch
블록으로 예외를 처리하는 것이 기본이지만, 경우에 따라서는 throws
키워드를 이용하여 메소드를 호출한 곳으로 예외를 떠넘길 수도 있다. 이렇게 하면 해당 메소드를 사용할 때 발생할 수 있는 예외를 사용자가 충분히 인지할 수 있으며, 그에 대한 처리까지도 강제할 수 있다.
throws
키워드 사용 방법은 다음과 같다.리턴타입 메소드명(매개변수, …) throws 예외클래스1, 예외클래스2, … { }
throws
키워드 뒤에 떠넘길 예외 클래스를 쉼표로 구분해서 나열해주면 된다.
위와 같이 발생할 수 있는 예외의 종류별로 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();
}
API
도큐먼트자바 API 도큐먼트를 보면 클래스 생성자와 메소드 선언부에 throws
키워드가 붙어있는 것을 흔히 볼 수 있다. 이러한 생성자와 메소드를 사용하고 싶다면, 반드시 try-catch
블록으로 예외 처리를 해야 한다. 아니면 throws
를 다시 사용해서 예외를 호출한 곳으로 떠넘겨야 한다. 그렇지 않으면 컴파일 오류가 발생한다.
애플리케이션 서비스와 관련된 예외를 애플리케이션 예외(Application Exception
)이라고 한다. 애플리케이션 예외는 개발자가 직접 정의해서 만들어야 하므로 사용자 정의 예외라고도 한다.
사용자 정의 예외 클래스는 컴파일러가 체크하는 일반 예외로 선언할 수도 있고, 컴파일러가 체크하지 않는 실행 예외로 선언할 수도 있다.
Exception
을 상속하면 되고, RuntimeException
을 상속하면 된다.public class XXXException extends [ Exception | RuntimeException ] {
public XXXException() { }
public XXXException(String message) { super(message); }
}
사용자 정의 예외 클래스 이름은 Exception
으로 끝나는 것이 좋다. 사용자 정의 예외 클래스도 필드, 생성자, 메소드 선언들을 포함할 수 있지만 대부분 생성자 선언만을 포함한다.
생성자는 두 개를 선언하는 것이 일반적인데
String
타입의 매개 변수를 갖는 생성자이다.String
타입의 매개 변수를 갖는 생성자는 상위 클래스의 생성자를 호출하여 예외 메시지를 넘겨준다. catch {}
블록의 예외 처리 코드에서 이용하기 위해서이다.사용자 정의 예외 또는 자바 표준 예외를 코드에서 발생시키는 방법은 다음과 같다.
throw new XXXException(); throw new XXXException(“메시지”);
예외 객체를 생성할 때는 기본 생성자 또는 예외 메시지를 갖는 생성자 중 어떤 것을 사용해도 된다.
만약 catch
블록에서 예외 메시지가 필요하다면 예외 메시지를 갖는 생성자를 이용해야 한다. 예외 발생 코드를 가지고 있는 메소드는 내부에서 try-catch
블록으로 예외를 처리할 수 있지만, 대부분은 자신을 호출한 곳에서 예외를 처리하도록 throws
키워드로 예외를 떠넘긴다.
public void method() throws XXXException {
throw new XXXException(“메시지”);
}
그렇기 때문에 throws
키워드를 포함하고 있는 메소드는 호출한 곳에서 다음과 같이 예외 처리를 해주어야 한다.
try {
method();
} catch(XXXException e) {
//예외 처리
}
try
블록에서 예외가 발생되면 예외 객체는catch
블록의 매개 변수에서 참조하게 되므로 매개 변수를 이용하면 예외 객체의 정보를 알 수 있다.
모든 예외 객체는 Exception
클래스를 상속하기 때문에 Exception
이 가지고 있는 메소드들을 모든 예외 객체에서 호출할 수 있다. 그중에서도 가장 많이 사용되는 메소드는 getMesage()
와 printStackTrace()
이다. 예외를 발생시킬 때 다음과 같이 String
타입의 메시지를 갖는 생성자를 이용했다면, 메시지는 자동적으로 예외 객체 내부에 저장된다.
throw new XXXException(“예외 메시지”);
예외 메시지의 내용에는 왜 예외가 발생했는지에 대한 간단한 설명이 포함된다. 좀 더 상세한 원인을 세분화하기 위해 예외 코드를 포함하기도 하는데, 예를 들어 데이터베이스에서 발생한 오류들은 예외 코드가 예외 메시지로 전달된다. 이와 같은 예외 메시지는 catch
블록에서 getMessage()
메소드의 리턴값으로 얻을 수 있다.
printStackTrace()
는 메소드 이름에서도 알 수 있듯이 예외 발생 코드를 추적해서 모두 콘솔에 출력한다. 어떤 예외가 어디에서 발생했는지 상세하게 출력해주기 때문에 프로그램을 테스트하면서 오류를 찾을 때 활용된다.