예외처리

2018-10-04

예외처리

예외의 종류와 특징

img

Error

java.lang.Error 클래스의 서브클래스 들이다. 에런느 시스템에 뭔가 비정상적인 상황이 발생했을 경우 사용 된다.

그래서 주로 자바 VM에서 발생 시키는 것이고 애플리케이션 코드에서 잡으려고 하면 안된다.

OutOfMemoryError나 ThreadDeath같은 에러는 catch 블록으로 잡아봤자 아무런 대응 방법이 없이 때문이다.

따라서 시스템 레벨에서 특별한 작업을 하는 게 아니라면 애플리케이션에서는 이런 에러에 대한 처리는 신경 쓰지 않아도 된다.

Exception과 체크 예외

java.lang.Exception 클래스와 그 서브클래스로 정의되는 예외들은 에러와 달리

개발자들이 만든 애플리케이션 코드의 작업 중에 예외상황이 발생했을 경우에 사용 된다.

Exception클래스는 다시 체크 예외 (복구 가능한 오류들) 언체크 예외 ( 복구 불가능한 오류들)로 구분 된다.

체크 예외는 Exception 클래스의 서브 클래스 들이면서, RuntimeException 클래스를 상속하지 않은 것들을 말하고,

언체크 예외는 RuntimeException을 상속한 클래스들을 말한다.

일반적으로 예외라고 하면 Exception 클래스 중에 RuntimeException을 상속을 했느냐 안했느냐의 차이로 나누어 진다.

체크 예외를 발생할 수 있는 메소드를 사용할 경우 반드시 예외를 처리하는 코드를 함께 작성해야 한다.

이를 catch 문으로 잡든지, 다시 throws를 정의해서 메소드 밖으로 던져야 한다. 그렇지 않으면 컴파일 에러가 발생 한다.

RuntimeException과 언체크/런타임

java.lang.RuntimeException 클래스를 상속한 예외들은 명시적인 예외처리를 강제하지 않기 때문에

언체크 예외라고 불리거나 대표 클래스 이름을 따서 런타임 예외라고도 한다.

에러와 마찬가지로 이 런타임 예외는 catch 문으로 잡거나 throws로 선언해줘도 되고,

물론 명시적으로 잡거나 throws로 선언해줘도 상관은 없다.

런타임 예외는 주로 프로그램의 오류가 있을 때 발생하도록 의도 된 것 들인데 , 할당되지 않은 객체에

접근해서 NullPointerException이 발생하는 등이 있다.

이런 예외는 미리 조건을 체크해서 주의 깊게 만든다면 피할 수 있지만 개발자가 부주의해서

발생할 수 있는 경우에 발생하도록 만든 것이 런타임 예외다.

따라서 런타임 예외는 예상하지 못했던 예외상황에서 발생하는 것이 아니기 때문에

굳이 catch나 throws를 사용하지 않아도 되도록 만든 것 이다.

예시

다음은 0으로 나누는 코드이다.

package ex.pratice.donghyeon.test;

public class TestMain {
    public static void main(String[] args) {
        int num = 35;
        System.out.println(num/0);
    }
}

Output

Exception in thread "main" java.lang.ArithmeticException: / by zero

at ex.pratice.donghyeon.test.TestMain.main(TestMain.java:8)

(0으로 나눌수 없다는 오류가 뜬다.)

java.lang.ArithmeticException은 RuntimeException을 상속받은 클래스이다.

public class ArithmeticException extends RuntimeException

RuntimeException은 위에서 설명했듯이 UncheckedException이므로

프로그래머가 오류를 안잡아줘도 컴파일러에서 오류를 잡아주기 때문에 문법적으로 오류는 없다.

예외를 처리하는데에는 크게 3가지 방법이 있다.

예외복구

public class TestMain {

    public static void main(String[] args) {

        int num = 35;
        try {
            System.out.println(num / 0);
        } catch (ArithmeticException e) {
            System.out.println("0으로 나눌수 없습니다.");
        }
    }
}

Output

0으로 나눌수 없습니다.

예외처리 회피

예외처리 회피는 나를 호출한 곳으로 예외를 던져서 호출한 곳에서 예외를 처리해주는 것이다.

public class TestMain {
    public static void main(String[] args) throws ArithmeticException {
        int num = 35;
        System.out.println(num/0);
    }
}

이 방법은 예외처리를 다른 곳으로 미루는 일이 되므로 호출한 곳에서 예외처리가 되었는지 확인을 해야한다.

예외 전환

public class TestMain {
    public static void main(String[] args) {
        int num = 35;
        try {
            System.out.println(num / 0);
        } catch (ArithmeticException e) {
            throw new NotFoundStudentUncheckedException();
        }
    }
}

예외전환은 그 예외가 발생하면 다른 예외로 throw를 던져주는 것이다.

사용자 예외 클래스 만들어보기

CheckedException 만들어보기

public class NotFoundStudentCheckedException extends Exception{
}

UncheckedException 만들어보기

public class NotFoundStudentUncheckedException extends RuntimeException{
}

사용자 임의 예외 클래스를 만드는 방법은 어렵지 않다.

클래스를 생성한 후 Exception이나 RuntimeException을 상속받으면 끝이다.

NotFoundStudentUncheckedException.java

public class NotFoundStudentUncheckedException extends RuntimeException{
    public NotFoundStudentUncheckedException(String string) {
        super(string);
    }
} 

테스트

public class TestMain {
    public static void main(String[] args)  {
       Student mathStudent = new MathStudent();
       String findStudent = mathStudent.find();
       if(findStudent == null) 
           throw new NotFoundStudentUncheckedException("학생을 찾지 못했습니다.");
    }
}

String의 값이 null이면 NotFoundStudentUncheckedException으로 던지는 코드이다.

Output

Exception in thread "main" ex.pratice.donghyeon.test.NotFoundStudentUncheckedException: 학생을 찾지 못했습니다.at ex.pratice.donghyeon.test.TestMain.main(TestMain.java:12)