사용자 정의 예외 생성
기존의 정의된 예외 클래스 외에 필요에 따라 프로그래머가 새로운 예외 클래스를 정의하여 사용할 수 있다.
class MyException extends Exception{
MyException(String msg){ // 문자열을 매개변수로 받는 생성자
super(msg); // 조상인 Exception클래스의 생성자를 호출한다.
}
}
Exception 클래스로부터 상속받아서 MyException클래스를 만들었다. 필요하다면, 멤버 변수나 메서드를 추가할 수 있다.
class MyException extends Exception{
// 에러 코드 값을 저장하기 위한 필드를 추가했다.
private final int ERR_CODE; // 생성자를 통해 초기화 한다.
MyException(String msg, int errCode) { // 생성자
super(msg);
ERR_CODE = errCode;
}
MyException(String msg) { // 생성자
super(msg);
ERR_CODE = errCode;
}
MyException(String msg) { // 생성자
this(msg, 100); // ERR_CODE를 100(기본값)으로 초기화한다.
}
public int getErrCode() { // 에러 코드를 얻을 수 있는 메서드도 추가했다.
return ERR_CODE; // 이 메서드는 주로 getMessage()와 함께 사용될 것이다.
}
}
메세지뿐만 아니라 에러코드 값도 저장할 수 있도록 ERR_CODE와 getErrCode()를 MyException클래스의 멤버로 추가했다. 이렇게 함으로써 MyException이 발생했을 때, catch블럭에서 getMessage()와 getErrCode()를 사용해서 에러 코드와 메시지를 모두 얻을 수 있을 것이다.
기존의 예외 클래스는 주로 Exception을 상속받아서 ‘checked예외’로 작성하는 경우가 많았지만, 요즘은 예외처리를 선택적으로 할 수 있도록 RuntimeException을 상속받아서 작성하는 쪽으로 바뀌어가고 있다. ‘checked예외’는 반드시 예외처리를 해주어야 하기 때문에 예외처리가 불필요한 경우에도 try-catch문을 넣어서 코드가 복잡해지기 때문이다.
예외 되던지기(Exception re-throwing)
한 메서드에서 발생할 수 있는 예외가 여럿인 경우, 몇 개는 try-catch문을 통해서 메서드 내에서 자체적으로 처리하고, 그 나머지는 선언부에 지정하여 호출한 메서드에서 처리하도록 함으로써, 양쪽에서 나눠서 처리되도록 할 수 있다.
그리고 심지어는 단 하나의 예외에 대해서도 예외가 발생한 메서드와 호출한 메서드, 양쪽에서 처리하도록 할 수 있다.
이것은 예외를 처리한 후에 인위적으로 다시 발생시키는 방법을 통해서 가능한데, 이것을 ‘예외 되던지기(exception re-throwing)'라고 한다.
- 예외가 발생할 가능성이 있는 메서드에서 try-catch문을 사용해서 예외를 처리
- catch문에서 필요한 작업을 행한 후에 throw문을 사용해서 예외를 다시 발생
- 다시 발생한 예외는 이 메서드를 호출한 메서드에게 전달되고 호출한 메서드의 try-catch문에서 예외를 또다시 처리
이 방법은 하나의 예외에 대해서 예외가 발생한 메서드와 이를 호출한 메서드 양쪽 모두에서 처리해줘야 할 작업이 있을 때 사용된다. 이 때 주의할 점은 예외가 발생할 메서드에서는 try-catch문을 사용해서 예외처리를 해줌과 동시에 메서드의 선언부에 발생할 예외를 throws에 지정해줘야 한다는 것이다.
class ReThrowing {
public static void main(String[] args) {
try {
method1();
} catch (Exception e) {
System.out.println("main메서드에서 예외가 처리되었습니다.");
}
} // main메서드의 끝
static void method1() throws Exception {
try {
throw new Exception();
} catch (Exception e) {
System.out.println("method1메서드에서 예외가 처리되었습니다.");
throw e; // 다시 예외를 발생시킨다.
}
} // method1메서드의 끝
}
반환 값이 있는 return문의 경우, catch블럭에도 return문이 있어야 한다. 예외가 발생했을 경우에도 값을 반환해야 하기 때문이다.
static int method() {
try {
System.out.println("method1()이 호출되었습니다.");
return 0; // 현재 실행 중인 메서드를 종료한다.
} catch (Exception e) {
e.printStackTrace();
return 1; // catch블럭 내에도 return문이 필요하다.
} finally {
System.out.println("method1()의 finally블럭이 실행되었습니다.")
}
} // method1메서드의 끝
연결된 예외(chained exception)
한 예외가 다른 예외를 발생시킬 수도 있다. 예를 들어 예외 A가 예외 B를 발생시켰다면, A를 B의 ‘원인 예외(cause exception)’라고 한다.
try {
startInstall(); // SpaceException 발생
copyFiles();
} catch (SpaceException e) {
InstallException ie = new InstallException("설치중 예외발생"); // 예외 생성
ie.initCause(e); // InstallException의 원인 예외를 SpaceException으로 지정
thorw ie; // InstallException을 발생시킨다.
} catch (MemoryException me)
먼저 InstallException을 생성한 후에, initCause()로 SpaceException을 InstallException의 원인 예외로 등록한다. 그리고 ‘throw’로 이 예외를 던진다.
initCause()는 Exception클래스의 조상인 Throwable클래스에 정의되어 있기 때문에 모든 예외에서 사용 가능하다.
Throwable initCause(Throwable cause) 지정한 예외를 원인 예외로 등록
Throwable getCause() 원인 예외를 반환
그래서 생각한 것이 예외가 원인 예외를 포함할 수 있게 한 것이다. 이렇게 하면, 두 예외는 상속관계가 아니어도 상관없다.
public class Throwable implements Serializable{
...
private Throwable cause = this; // 객체 자신(this)을 원인 예외로 등록
...
}
또 다른 이유는 checked예외를 unchecked예외로 바꿀 수 있도록 하기 위함이다.
checked예외로 예외처리를 강제한 이유는 프로그래밍 경험이 적은 사람도 보다 견고한 프로그램을 작성할 수 있도록 유도하기 위한 것이었는데, 지금은 자바가 처음 개발되던 1990년대와 컴퓨터 환경이 많이 달라졌다. 그래서 checked예외가 발생해도 예외를 처리할 수 없는 상황이 하나둘 발생하기 시작했다. 이럴 때 할 수 있는 일이라곤 그저 의미없는 try-catch문을 추가하는 것뿐인데, checked예외를 unchecked예외로 바꾸면 예외처리가 선택적이 되므로 억지로 예외처리를 하지 않아도 된다.
MemoryException은 Exception의 자손이므로 반드시 예외를 처리해야 하는데, 이 예외를 RuntimeException으로 감싸버렸기 때문에 unchecked예외가 되었다. 그래서 더 이상 startInstall()의 선언부에 MemoryException을 선언하지 않아도 된다. 참고로 위의 코드에서는 initCause() 대신 RuntimeException의 생성자를 사용했다.
RuntimeException(Throwable cause) // 원인 예외를 등록하는 생성자
'Server > 자바의정석' 카테고리의 다른 글
[Java 입문] throw, throws (0) | 2022.09.30 |
---|---|
[Java 입문] try - catch - finally (0) | 2022.09.30 |
[Java 입문] 오류 & 예외 (4) | 2022.09.29 |
[Java 입문] 디폴트 메서드 & Static 메서드, 내부 클래스 (1) | 2022.09.29 |
[Java 입문] 추상(abstract) & 인터페이스(Interface) (0) | 2022.09.28 |