Java 예외처리

‍박태우·2024년 9월 4일

nbc_spring

목록 보기
9/28

예외 처리

1) 예외의 정의

  • 예외 (Exception)은 회복 불가능한 오류(Error) 와 달리 회복이 가능한 경우이며 우리가 예외 정의, 예외 발생 가능성 알림, 예외 핸들링 을 통해 적절하게 예외 처리를 진행 할 수 있다.

-예외 처리 예시

  • OurBadException class
// 예외 클래스를 만들어서 예외를 정의!!

public class OurBadException extends Exception {

    public OurBadException() {
        super("위험한 행동을 하면 예외처리를 꼭 해야함!!");
    }
}

위 코드를 통해 우리만의 예외를 정의한 것이다.

모든 예외는 Exception을 상속받고 있으며 세세한 예외를 우리가 핸들링하기 위해 상속받아서 사용하는 것이다.
위 경우 super(); 를 통해 부모의 생성자를 가져와서 처리하였다.

  • OurClass 클래스
package week04.sample01;

public class OurClass {
    private final boolean just = true;

    // throws
    public void thisMethodIsDangerous() throws OurBadException{ 
    // 위험하다는 flag 를 달아준 것
    
        if(just){
            throw new OurBadException();
            // 항상 예외가 발생한다.
        }
    }

}

예외가 발생가능한 메서드를 throws 를 통해 flag 를 세운다.
(해당하는 예외가 발생할 수 있다는 것을 알리기 위함)

예외를 발생하기위해서는 throw를 사용할 수 있다. (둘이 햇갈리지 말기)
위 코드는 무조건 예외가 발생하도록 유도하였다. (일부로)

  • 예외 처리 사용해보기 (StudyException 클래스)
public class StudyException {
    public static void main(String[] args) {
        OurClass ourClass = new OurClass();


        try{
            ourClass.thisMethodIsDangerous();
        }catch(OurBadException e){ // e 를 통해 인스턴스화 (보통 e)
            System.out.println(e.getMessage()); 
            // 우리가 예외 처리 생성시 super를 통해 등록한 메시지
        }
        finally {
            // 무조건 실행
            System.out.println("우리는 방금 예외를 핸들링 함, ");
        }
    }
}

try-catch-finally 문을 사용하였다.
try는 예외를 포함하며 예외 발생 시 catch 문을 실행 그렇지 않으면 진행 됨
finally는 예외가 발생을 하던지 하지 않던지 무조건 실행되는 문 (생략 가능)


2) 예외 클래스의 구조 이해하기

1. Checked Exception vs Unchecked Exception

컴파일시 vs 런타임시 (예측 불가)

2. 예외 클래스의 구현 구조 파악하기

예외 클래스 또한 Object 클래스로 부터 상속받아서 사용한다.
그 클래스가 Throwable 클래스로서 이 클래스의 자식으로
ErrorException 클래스가 있다.

그리고 각 클래스는 각각 IOError 클래스 (입출력), RuntimeException 클래스와 같이 구분되어 처리된다.

참고로
RuntimeException을 상속한 예외들 ==> UncheckedException
그렇지 않은 예외들 ==> CheckedException
으로 구현되어 있다.

위 처럼 실행되면서 발생하는 예외는 RuntimeException을 상속 받는다.

"이미 수많은 예외가 구현되어 있고 이걸 다 외울 필요는 없다. 우리가 필요한 예외가 없는 경우 custom 하게 정의하여 사용하면 된다."


3) Chained Exception, 실제 예외 처리하는 방법

1. 예외의 연결

  • 예외가 예외를 발생시키는 경우

-initCause() : 지정된 예외를 원인 예외로 등록하는 메서드
-getCause : 원인 예외를 반환하는 메서드

=> 왜 예외를 연결하나? :
1) 여러 가지 예외를 하나의 큰 분류 예외로 묶어서 다루기 위함
2) checked exception 을 unchecked exception으로 포장(wrapping) 하는데 유용하게 사용되기도 한다. (컴파일 시간 => 런타임시간)

예시 :

package week04.sample02;

// 연결된 예외
public class Main {
    public static void main(String[] args) {
        try {
            // 예외 생성
            NumberFormatException ex = new NumberFormatException("가짜 예외이유");

            // 원인 예외 설정(지정한 예외를 원인 예외로 등록)
            ex.initCause(new NullPointerException("진짜 예외이유"));

            // 예외를 직접 던집니다.
            throw ex;
        } catch (NumberFormatException ex) {

            // 예외 로그 출력
            ex.printStackTrace();

            // 예외 원인 조회 후 출력
            ex.getCause().printStackTrace();
        }

        // checked exception 을 감싸서 unchecked exception 안에 넣습니다.
        throw new RuntimeException(new Exception("이것이 진짜 예외 이유 입니다."));
    }
}

결과 :

1) try 문 안에서 initCause 를 이용하여 원인 예외를 만들었다. (ex의 원인 예외 만듬)
그리고 그 예외를 던졌다.


2) catch 문에서 try 문에서 발생한 예외를 감지하고 로그를 출력한다.

(1) 아래 결과는 ex.printStackTrace() 만을 실행했을 때의 결과이다.
(결과 예외 -> 원인 예외 순으로 출력된다.)

(2) 다음 결과는 ex.getCause().printStackTrace(); 만을 실행했을 때의 결과
(원인 예외만 출력되는 것을 확인 할 수 있다.)

(3) 맨 아래줄에 무조건 실행되는 예외인 RuntimeException 을 실행 시에는 아래오 같은 결과가 나온다.

=> 이 경우는 checkedException 을 unchekcedException 으로 wrapping 하기 위한 코드이다.

throw new RuntimeException(new Exception("이것이 진짜 예외 이유 입니다."));

=> RuntimeException 으로 예외를 감쌈으로서 wrapping 하였다.
저거 없애면 컴파일시 예외 (checkedException)로 처리되어서 빨간 줄이 뜬다.

=> 이를 해결하기 위해서는 해당 메서드에 throws Exception 을 입력하여 메서드가 예외 가능성이 있다는 플래그를 주고, 컴파일 시에도 예외가 처리되도록 할 수 있다.

=> 로그가 살짝 다르게 나오긴 한다. (알아두면 좋을 듯)

profile
잘 부탁드립니다.

0개의 댓글