try-catch-finally
의 순서로 구성됨.try {
// 예외가 발생할 가능성이 있는 문장 삽입.
} catch (Exception1 e1) {
// 예외처리를 위한 문장 삽입.
} finally {
// 예외의 발생여부에 관계 없이 항상 수행되야하는 문장들을 넣음.
// finally 블럭은 try-catch 문의 맨 마지막에 위치해야 함.
}
try → catch → finally
순으로 실행되고, 예외가 발생하지 않은 경우 try → finally
의 순으로 실행된다. try {
startInstall(); // 프로그램 설치에 필요한 준비를 함.
copyFiles(); // 파일들을 복사.
deleteTempFiles(); // 프로그램 설치에 사용된 임시 파일 삭제.
} catch (Exception e) {
e.printStackTrace();
deleteTempFiles(); // 프로그램 설치에 사용된 임시 파일 삭제
} // try-catch 끝
try {
startInstall(); // 프로그램 설치에 필요한 준비를 함.
copyFiles(); // 파일들을 복사.
} catch (Exception e) {
e.printStackTrace();
} finally {
deleteTempFiles(); // 프로그램 설치에 사용된 임시 파일 삭제
} // try-catch 끝
Exception
클래스 또는 RuntimeException
클래스로부터 상속받는 클래스를 만들지만, 필요에 따라 알맞은 예외 클래스를 선택 가능하다.class MyException extends Exception {
MyException(String msg) { // 문자열을 매개변수로 받는 생성자
super(msg); // 조상인 Exception 클래스의 생성자 호출.
}
}
Exception
클래스로부터 상속받아서 MyException 클래스를 만들었다. 멤버변수나 메서드를 추가할 수도 있다. Exception
클래스는 생성 시 String 값을 받아 메시지로 저장할 수 있다. 사용자 정의 예외 클래스도 메시지를 저장하려면, 위와 같이 String을 매개변수로 받는 생성자를 추가해주어야 한다. Exception
을 상속받아 check예외
로 작성하는 경우가 많았지만, 최근에는 예외처리를 선택적으로 할 수 있도록 RuntimeException
을 상속받아서 작성하는 쪽으로 바뀌어가고 있다.check 예외
는 반드시 예외처리를 해주어야 하기 때문에 예외처리가 불필요한 경우에도 try-catch문을 넣어 코드가 복잡해지기 때문이다.try-catch
문을 통해 메서드 내에서 자체적으로 처리하고, 나머지는 선언부에 지정하여 호출한 메서드에서 처리하도록 함으로써 양쪽에서 나눠 처리하도록 할 수 있다.예외 되던지기, exception re-throwing
라고 한다. try-catch
문을 사용, 예외를 처리해주고 →catch
문에서 필요한 작업을 행한 후 throw
문을 사용해서 예외를 다시 발생시킨다. →try-catch
문에서 예외를 또다시 처리한다.try-catch
문을 사용해서 예외처리를 해줌과 동시에 메서드의 선언부에 발생할 예외를 throws
에 지정해줘야 한다는 것이다. 원인 예외(cause exception)
이라고 한다. try {
startInstall(); // SpaceException 발생
copyFiles(); // 파일들을 복사.
} catch (SpaceException e) {
InstallException ie = new InstallException("설치 중 예외 발생"); // 예외 생성
ie.initCause(e); // InstallException의 원인 예외를 SpaceException으로 지정
throw ie; // InstallException을 발생.
} catch (MemoryException me) {
....
InstallException
을 생성한 후에, initCause()
로 SpaceException
을 InstallException
의 원인 예외로 등록한다. 그리고 throw
로 예외를 던진다.initCause()
는 Exception 클래스의 조상인 Throwable 클래스에 정의되어 있기 때문에 모든 예외에서 사용 가능하다. Trowable initCause (Throwable cause) : 지정된 예외를 원인 예외로 등록
Throwable getCause() : 원인 예외를 반환
InstallException
을 SpaceException
과 MemoryException
의 조상으로 해 catch블럭을 작성하면, 실제로 발생한 예외가 어떤 것인지 알 수 없고, 상속관계를 변경해야 한다는 것도 부담이다. try {
startInstall(); // SpaceException 발생
copyFiles();
} catch (InstallException e) { // InstallException은
e.printStackTrace(); // SpaceException과 MemoryException의 조상.
}
class SpaceException extends Exception {
~~
SpaceException
을 예외 필수 처리인 Exception
에서 선택처리인 RuntimeException
으로 바꾸고 싶다. 하지만 SpaceException을 여러 곳에서 사용하고 있다면 상속관계를 바꾸는 것이 쉬운 것이 아니다. 이럴 때 연결된 예외를 사용할 수 있다.RuntimeException
을 만들고 SpaceException
을 포함시키는 방식으로 말이다.static void startInstall() throws SpaceException, MemoryException {
if(!enoughSpace())
throw new SpaceException("공간 부족");
if(!enoughMemory())
throw new MemoryException("메모리 부족");
}
↓
static void startInstall() throws SpaceException {
if(!enoughSpace())
throw new SpaceException("공간 부족");
if(!enoughMemory())
throw new RuntimeException(new MemoryException("메모리 부족"));
}
RuntimeException
을 만들고 MemoryException
을 원인예외로 등록함으로써 MemoryException
를 예외 선택처리로 바꾸어주었기 때문에 위의 경우와 다르게 선언부에 선언해주지 않아도 된다.MemoryException
은 Exception
의 자식이므로 반드시 예외를 처리해야하는데, 이것을 RuntimeException
으로 감싸버렸기 때문에 unchecked 예외가 되었다. 더 이상 startInstall()
의 선언부에 MemoryException을 선언하지 않아도 된다. initCause()
를 사용해 원인예외로 등록할 수도 있고, 생성자를 사용해서 원인예외로 등록할 수도 있다.initCause()
대신 RuntimeException
의 생성자를 사용했다.RuntimeException(Throwable cause)
→ 원인예외를 등록하는 생성자