[기본 개념] 3 | (1.4) 사용자 정의 예외, 예외 되던지기, 연결된 예외

728x90

[기본 개념] 3 | (1.4) 사용자 정의 예외, 예외 되던지기, 연결된 예외

1 프로그램 오류

2 예외 클래스의 계층구조

3 예외처리하기 - try-catch문

4 try-catch문에서의 흐름

5 예외의 발생과 catch블럭

6 예외 발생시키기

7 메서드에 예외 선언하기

8 finally블럭

9 자동 자원 반환 - try-with-resources문

10> 사용자 정의 예외 만들기

11> 예외 되던지기(exception re-throwing)

12> 연결된 예외(chained exception)

10. 사용자 정의 예외 만들기

 필요에 따라 새로운 예외 클래스를 정의하여 사용할 수 있다. 보통 Exception클래스 또는 RuntimeException클래스로부터 상속받아 클래스를 만들지만, 예외 클래스를 선택할 수 있다.

 

class MyException extends Exception {

        MyException(String msg) {    // 문자열을 매개변수로 받는 생성자

                super(msg) ;     // 조상인 Exception클래스의 생성자를 호출한다.

        }

}

 

 필요하다면, 멤버변수나 메서드를 추가할 수 있다. 사용자 정의 예외 클래스도 메시지를 저장할 수 있으려면, 위와 같이 String을 매개변수로 받는 생성자를 추가해야 한다.

 

        class MyException extends Exception {

            // 에러 코드 값을 저장하기 위한 필드를 추가했다.

            private final int ERR_CODE ;   // 생성자를 통해 초기화한다.

 

            MyException(String msg, int errCode) {     // 생성자

                 Super(msg) ;

                 ERR_CODE = errCode ;

            }

 

            MyException(String msg) {       // 생성자

                this(msg, 100) ;                 // ERR_CODE를 100(기본값)으로 초기화한다.

            }

 

            public int getErrCode( ) {         // 에러코드를 얻을 수 있는 메서드도 추가했다.

                return ERR_CODE ;             // 이 메서드는 주로 getMessage( )와 함께 사용될 것이다.

            }

        }

 

 메시지와 에러코드 값도 저장할 수 있도록 추가하였다. 이렇게 함으로써 MyException이 발생했을 때, catch블럭에서 getMessage( )와 getErrCode( )를 사용해서 에러코드와 메시지를 모두 얻을 수 있을 것이다.

 

 요즘은 예외처리를 강제로 하는 것(checked예외)이 아니라 선택적(unchecked예외)으로 할 수 있도록 RuntimeException을 상속받아서 작성하는 쪽으로 바뀌고 있다.

11. 예외 되던지기(exception re-throwing)

 한 메서드에서 발생할 수 있는 예외가 여럿인 경우, 몇 개는 자체적으로 처리하고 나머지는 이 메서드를 호출한 메서드에서 처리하게 함으로써 양쪽에 나눠서 처리되도록 할 수 있다.

 

예외를 처리한 후 인위적으로 다시 발생시키는 방법을 '예외 되던지기(exception re-throwing)'라고 한다.

 

 먼저 try-catch문에서 예외처리를 해주고, throw문을 통해 예외를 다시 발생시킨다. 그리고 다시 발생한 예외는 이 메서드를 호출한 메서드에게 전달되어 호출한 메서드의 try-catch문에서 또 예외처리를 한다.

 

 이때, 예외가 발생할 메서드에서 try-catch문을 사용해서 예외처리를 해주며 동시에 메서드의 선언부에 발생할 예외를 throws에 지정해줘야 한다는 것이다.

 

예제/ExceptionEx17.java

public class ExceptionEx17 {
    public static void main(String[] args) {
        try {
            method1();
        } catch (Exception e) {
            System.out.println("main메서드에서 예외처리가 되었습니다.");
        }
    }

    static void method1() throws Exception {
        try {
            throw new Exception();
        } catch (Exception e) {
            System.out.println("method1메서드에서 예외처리가 되었습니다.");
            throw e;        // 다시 예외를 발생시킨다.
        }
    }
}
실행결과

method1메서드에서 예외처리가 되었습니다.
main메서드에서 예외처리가 되었습니다.

 

method1( )와 main메서드 양쪽의 catch블럭이 모두 수행되었다. 

 

 반환값이 있는 return문의 경우, catch블럭에도 return문이 있어야 한다. 예외가 발생했을 경우에도 값을 반환해야 하기 때문이다.

 

        static int method1( ) throws Exception {   // 예외를 선언해야 함

            try {

                System.out.println("method1( )이 호출되었습니다.") ;

                return 0 ;       // 현재 실행 중인 메서드를 종료한다.

            } catch (Excaption e) {

                e.printStackTrace( ) ;

           //   return 1 ;       // catch블럭 내에도 return문이 필요하다.

                throw new Exception ( ) ;   // return문 대신 예외를 호출한 메서드로 전달

            } finally {

                System.out.println("method1( )의 finally블럭이 실행되었습니다.") ;

            }

        }   

12. 연결된 예외(chained exception)

예외 A가 예외 B를 발생시켰다면, A를 B의 '원인 예외(cause exception)'라고 한다.

 

        try {

            startInstall( ) ;         // SpaceExcetion 발생

            copyFilse( ) ;

        } 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클래스에 정의되어 있으므로 모든 예외에서 사용 가능하다.

 

Throwable initCause(Throwable cause)      지정한 예외를 원인 예외로 등록

Throwable getCause( )                                          원인 예외를 반환

 

 이처럼 원인 예외로 등록해서 다시 예외를 발생시키는 이유는 여러 가지 예외를 하나의 큰 분류의 예외로 묶어서 다루기 위해서이다.

 


    static void startInstall( ) throws SpaceException, MemoryException {
        if (!enoughSpace( ))        // 충분한 공간이 없으면 . . .
            throw new SpaceException("설치할 공간이 부족합니다.") ;

        if (!enoughMemory( ))
            throw new MemoryException("메모리가 부족합니다.") ;
    }


 ㅣ

  l 
 ▼


    static void startInstall( ) throws SpaceException {
        if (!enoughSpace( ))        // 충분한 공간이 없으면 . . .
            throw new SpaceException("설치할 공간이 부족합니다.") ;

        if (!enoughMemory( ))
            throw new RuntimeException (new MemoryException("메모리가 부족합니다.")) ;
    }

 

 이처럼 MemoryException은 Exception의 자손이므로 반드시 예외처리를 해야 하는데 이 예외를 RuntimeException으로 감싸버렸기 때문에 unchecked예외가 되었다. 그래서 더 이상 startInstall( )의 선언부에 MemoryException을 선언하지 않아도 된다.

 

참고로 위의 코드는 initCause( )대신 RuntimeException의 생성자를 사용했다.

 

RuntimeException(Throwable cause)  // 원인 예외를 등록하는 생성자

 

 

 

 

 

출처 | Java의 정석 (남궁 성)

728x90