Programming Language/Java

[Java] 16. 예외 처리(Exception handling)(5) - 예외 되던지기, 연결된 예외

lumana 2024. 7. 9. 05:39

예외 처리(5) - 예외 되던지기, 연결된 예외

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

  • 앞서 말했던 것처럼 한 메서드에서 발생할 수 있는 예외가 여럿인 경우 몇 개는 try-catch문을 통해서 자체적으로 처리하고, 나머지는 선언부에 지정하여 호출한 메서드에서 처리하도록 함으로써 양쪽에서 나눠서 처리되도록 할 수 있다.

  • 심지어 단 하나의 예외에 대해서도 예외가 발생한 메서드와 호출한 메서드 양쪽에서 처리하도록 할 수 있다.

    • 이것은 예외를 처리한 후에 인위적으로 다시 발생시키는 방법을 통해서 가능한데, 이것을 예외 되던지기라고 한다.
  • 먼저 예외가 발생할 가능성이 있는 메서드에서 try-catch문을 사용해서 예외를 처리해주고 catch문에서 필요한 작업을 행한 후에 throw문을 사용해서 예외를 다시 발생 시킨다

    • 이 예외는 이 메서드를 호출한 메서드에게 전달되고 호출한 메서드의 try-catch문에서 예외를 또 다시 처리한다.
  • 이 방법은 예외가 발생한 메서드와 이를 호출한 메서드 양쪽 모두에서 처리해줘야 할 작업이 있을 때 사용된다

    • 주의) 예외가 발생할 메서드에서는 try-catch문을 사용해서 예외처리를 해줘야하는 동시에 메서드의 선언부에 발생할 예외를 throws에 지정해줘야 한다는 것이다.
class ExceptionEx17 {
    public static void main(String[] args) {
        try {
            method1();
        } catch (Exception e) {
            System.out.println("main메서드에서 예외가 처리되었습니다.");
        }
    }    // main메서드의 끝

    static void method1() throws Exception { // re-throwing하는 경우 throws를 지정해줘야 한다
        try {
            throw new Exception();
        } catch (Exception e) {
            System.out.println("method1메서드에서 예외가 처리되었습니다.");
            throw e;    // 다시 예외를 발생시킨다.
        }
    }    // method1메서드의 끝
}

  • 반환값이 있는 return문의 경우 catch블럭에도 return문이 있어야 한다.

    • 예외가 발생해도 값을 반환해야 하기 때문이다.

    • catch블럭에서 예외 되던지기를 해서 호출한 메서드로 예외를 전달하면 catch블럭에 return문이 없어도 된다

static int method1() {
        try {
                // ~~
                return 0;
        } catch (Exception e) {
                e.printStackTrace();
                // return 1; // catch블럭 내에서도 원래는 return문이 필요하다
                throw new Exception(); // return문 대신 예외를 호출한 메서드로 전달한다.
        } finally {
                // ~~
        }
}

연결된 예외(chained exception)

  • 한 예외가 다른 예외를 발생시킬 수도 있다.

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

try {
        startInstall(); // SpaceException 발생
        copyFiles();
} catch (SpaceException e) {
        InstallException ie = new InstallException("설치 중 예외 발생"); // 예외 생성
        ie.initCause(e); // InstallException의 원인 예외를 SpaceException으로 지정
        throw iel // InstallException을 발생시킨다
} catch (MemoryException me) {
        ...
}
  • initCause()로 원인 예외를 등록한다

  • initCause()는 Exception 클래스의 조상인 Throwable클래스에 정의되어 있기 때문에 모든 예외에서 사용가능하다

왜 원인 예외로 등록해서 다시 예외를 발생시키나요?

  • 원인1) 여러가지 예외를 하나의 큰 분류의 예외로 묶어서 다루기 위해서이다.

    • 하나로 묶어서 다루기 위해 만약 catch의 참조변수에 부모 클래스 참조변수를 넣으면, 실제로 발생한 예외가 어떤 것인지 알 수 없는 문제가 발생한다.

    • 그래서 예외가 원인 예외를 포함시킬 수 있게 한 것이다.

  • 원인2) checked 예외를 unchecked예외로 바꿀 수 있도록 하기 위함이다.

    • 예외를 RuntimeException으로 감싸버리면 unchecked 예외가 된다.

    • throw new RuntimeException(new MemoryException("메모리 부족"));

      • initCause()대신 RuntimeException의 생성자를 사용할 수 있다.
    • 이렇게 하면 더 이상 메서드의 선언부에 memoryException을 선언하지 않아도 된다.

Example 8.23

class ChainedExceptionEx {
    public static void main(String args[]) {
        try {
            install();
        } catch(InstallException e) {
            e.printStackTrace();
        } catch(Exception e) {
            e.printStackTrace();
        }
    }    // main메서드의 끝

    static void install() throws InstallException {
        try {
            startInstall();        // 프로그램 설치에 필요한 준비를 한다.
            copyFiles();        // 파일들을 복사한다.
        } catch(SpaceException se) {
            InstallException ie = new InstallException("설치 중 예외 발생");
            ie.initCause(se);
            throw ie;
        } catch(MemoryException me) {
            InstallException ie = new InstallException("설치 중 예외 발생");
            ie.initCause(me);
            throw ie;
        } finally {
            deleteTempFiles();    // 프로그램 설치에 사용된 임시파일들을 삭제한다.
        }    // try의 끝
    }    // install메서드의 끝

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

        if(!enoughMemory()) {    // 충분한 메모리가 없으면
            throw new MemoryException("메모리가 부족합니다.");
//            throw new RuntimeException(new MemoryException("메모리가 부족합니다."));
        }
    }    // startInstall메서드의 끝

    static void copyFiles()            { /* 파일들을 복사하는 코드를 적는다. */ }
    static void deleteTempFiles()    { /* 임시파일들을 삭제하는 코드를 적는다.*/ }

    static boolean enoughSpace() {
        // 설치하는데 필요한 공간이 있는지 확인하는 코드를 적는다.
        return false;
    }

    static boolean enoughMemory() {
        // 설치하는데 필요한 메모리공간이 있는지 확인하는 코드를 적는다.
        return true;
    }
}    // ExceptionTest클래스의 끝

class InstallException extends Exception {
    InstallException(String msg) {
        super(msg);
    }
}

class SpaceException extends Exception {
    SpaceException(String msg) {
        super(msg);
    }
}

class MemoryException extends Exception {
    MemoryException(String msg) {
        super(msg);
    }
}

참조) Java의 정석 3rd edition(남궁성, 도우출판)