개발/Java

Java Exception

Debin 2022. 5. 24.
반응형

백기선님이 과거에 진행했던 Java 스터디 9주차 스터디 입니다.

자바에서 예외 처리 방법 (try, catch, throw, throws, finally)

예외처리란, 프로그램 실행 시 발생할 수 있는 예기치 못한 예외의 발생에 대비한 코드를 작성하는 것이다.

예외처리의 목적은 예외의 발생으로 인한 실행 중인 프로그램의 갑작스런 비정상 종료를 막고,

정상적인 실행상태를 유지할 수 있도록 하는 것이다.

발생한 예외를 처리하지 못하면, 프로그램은 비정상적으로 종료되며,

처리되지 못한 예외는 JVM 예외처리기가 받아서

예외의 원인을 화면에 출력한다.

try-catch문

  • 하나의 try 블럭 다음에는 하나 이상의 catch 블럭이 온다.
  • 발생한 예외의 종류와 일치하는 catch 블럭이 없으면 예외는 처리되지 않는다.
try{
    //예외가 발생할 가능성이 있는 코드를 넣는다.
} catch (Exception1 ex1){
    //Exception1이 발생할 경우, 이를 처리하기 위한 코드를 넣는다.
} catch (Exception2 ex2){
    //Exception2가 발생할 경우, 이를 처리하기 위한 코드를 넣는다.
} catch (Exception3 ex3){
    //Exception3이 발생할 경우, 이를 처리하기 위한 코드를 넣는다.
}

구체적인 흐름은 다음과 같다.

  • try 블럭에서 예외가 발생한 경우
    1. 발생한 예외와 일치하는 catch 블럭이 있는지 확인한다.
    2. 일치하는 catch 블럭을 찾게 되면, 그 catch블럭 내의 문장들을 수행하고 전체 try-catch문을 빠져나가서 그 다음 문장을 계속해서 수행한다. 만일 일치하는 catch블럭을 찾지 못하면, 예외는 처리되지 못한다.
  • try 블럭에서 예외가 발생하지 않은 경우
    1. catch 블럭을 거치지 않고 전체 try-catch문을 빠져나가서 수행을 계속한다.

catch블럭에서 짚고 넘어가야할 부분이 있다.

catch블럭은 괄호()와 블럭{} 두 부분으로 나눠져 있다.

괄호()내에는 처리하고자 하는 예외와 같은 타입의 참조변수 하나를 선언해야 한다.

여기에서 중요한 부분이 있다. 아래 코드를 살펴보자.

try{
    String str = null;
    str.length() // NullPointerException 발생
} catch (Exception ex){
    System.out.println("예외 발생"); //출력 된다.
}

catch문에서는 Exception 클래스의 참조 변수를 선언해 놓았다.

그런데 try 문에서는 NullPointerException이 발생했는데 catch문이 실행되었다. 왜 그런것일까??

모든 예외 클래스는 Exception 클래스의 자손이므로, catch블럭의 괄호()에 Exception클래스 타입의 참조변수를 선언해 놓으면 어떤 종류의 예외가 발생하더라도 이 catch 블럭에 의해서 처리된다.

즉 try 블럭에서 발생한 예외의 부모 클래스 예외를 catch문에서 참조 변수로 선언해 놓으면  catch 블럭에서 처리할 수 있다.

throw

키워드 throw를 사용해서 프로그래머가 고의로 예외를 발생시킬 수 있다.

  1. 먼저, 연산자 new를 이용해서 발생시키려는 예외 클래스의 인스턴스를 만든 다음,
  2. 키워드 throw를 이용해서 예외를 발생시킨다.
try{
    throw new RuntimeException("강제 예외 발생");
} 
catch (RuntimeException ex){
    System.out.println("예외 발생");
    System.out.println(ex.getMessage());
}

예상한대로 출력되었다.

throws (메서드에 예외 선언하기)

예외를 처리하는 방법에는 try - catch문 말고도, 예외를 메서드에 선언하는 방법이 있다.

메서드에 예외를 선언하려면, 메서드의 선언부에 키워드 throws를 사용해서 메서드 내에서 발생할 수 있는 예외를 적어주기만 하면 된다.

그리고 예외가 여러 개일 경우네느 쉼표로 구분한다.

void method() throws Exception1, Exception2,... ExceptionN{
}

void exampleMethod() throws Exception{
    //메서드의 내용
}

위의 exampleMethod를 보면 throws Exception라고 코드를 작성했는데, 

이 메서드는 모든 종류의 예외가 발생할 가능성이 있다는 뜻이다.

이렇게 예외를 선언하면, 이 예외뿐만 아니라 그 자손타입의 예외까지도 발생할 수 있다는 점을 주의하자.

앞서 오버라이딩에서 살펴본 것과 같이, 오버라이딩할 때는 단순히 선언된 예외의 갯수가 아니라 상속관계도 고려해야 한다.

 

메서드에 예외를 선언할 때 일반적으로 RuntimException 클래스들은 적지 않는다.

이 들을 메서드의 선언부의 throws에 선언한다고 해서 문제가 되지는 않지만, 보통 반드시 처리해주어야 하는 예외들만 선언한다.

사실 예외를 메서드의 throws에 명시하는 것은 예외를 처리하는 것이 아니라,

자신을 호출한 메서드에게 예외를 전달하여 예외처리를 떠맡기는 것이다. 결국 try - catch로 예외처리를 해줘야 한다!

finally

finally블럭은 예외의 발생여부에 상관없이 실행되어야할 코드를 포함시킬 목적으로 사용한다.즉 무조건 실행되는 블럭이다.

try{
//예외가 터질 수 있는 코드
}
catch(Exception ex){
//예외처리 코드
}
finally{
//무조건 수행되는 코드들
}

자바가 제공하는 예외 계층 구조

예외와 에러도 역시 Object 클래스의 자손이다.

예외 클래스 계층도
Exception 클래스와 RuntimeException 클래스 중심의 상속 계층도

 

Exception과 Error의 차이는?

에러(Error)

  • 프로그램 코드에 의해서 수습될 수 없는 심각한 오류다.
  • 메모리 부족이나 스택오버플로우와 같이 발생하면 복구할 수 없는 심각한 오류를 말한다.

예외(Exception)

  • 프로그램 코드에 의해서 수습될 수 있는 다소 미약한 오류다.
  • 예외는 발생하더라도 프로그래머가 이에 대한 적절한 코드를 미리 작성해 놓음으로써 프로그램의 비정상적인 종료를 막을 수 있다.

RuntimeException과 RE가 아닌 것의 차이는?

  • RuntimeException 클래스와 그 자손 클래스들을 RuntimeException 클래스들이라 하고, 제외한 나머지 클래스들을 Exception 클래스들이라고 하겠다.
  • RuntimeException 클래스들은 주로 프로그래머의 실수에 의해서 발생될 수 있는 예외들로 자바의 프로그래밍 요소들과 관계가 깊다
  • 예를 들어 배열의 범위를 벗어난다, 값이 Null인 참조변수의 멤버 변수, 메서드를 호출하려 했다던가, 클래스 간의 형변환을 잘 못하는 등의 일이 있다.
  • Exception 클래스들은 주로 외부의 영향으로 발생하는 것으로, 프로그램의 사용자들의 동작에 의해서 발생하는 경우가 많다.
  • 예를 들어 존재하지 않는 파일을 입력했다던가, 실수로 클래스의 이름을 잘못 적었다던가, 또는 입력한 데이터 형식이 잘못된 경우에 발생한다.
  • 컴파일러가 예외처리를 확인하는Exception클래스들은 checked Exception이라고 한다.
  • 컴파일러가 예외처리를 확인하지 않는 RuntimeException 클래스들은 unchecked Exception라고 한다.

커스텀한 예외를 만드는 방법

보통 Exception 클래스 또는 RuntimException 클래스로부터 상속받아 클래스를 만들지만,

필요에 따라서 알맞은 예외 클래스를 선택하면 된다.

 

기존의 예외 클래스는 주로 Exception을 상속받아서 checked예외로 작성하는 경우가 많았지만,

요즘은 예외처리를 선택적으로 할 수 있도록 RuntimeException을 상속받아서 작성하는 쪽으로 바뀌어가고 있다.

checked예외는 반드시 예외처리를 해주어야 하기 때문에 예외처리가 불필요한 경우에도

try-catch 문을 넣어서 코드가 복잡해지기 때문이다.

요즘은 unchecked예외가 checked예외보다 환영받는다고 한다.

 

아래는 본인이 Custom한 예외 클래스다.

@Getter
public abstract class ApiBaseException extends RuntimeException{
    private final ErrorCode errorCode;
    private final HttpStatus httpStatus;

    public ApiBaseException(ErrorCode errorCode, HttpStatus httpStatus,String message) {
        super(message);
        this.errorCode = errorCode;
        this.httpStatus = httpStatus;
    }
}

 

이번에 예외 공부를 하면서 RuntimeException 예외가 발생해도,

throws를 메서드 선언부에 적지 않아도 되는 것을 제대로 공부할 수 있었다.

throws는 보통 반드시 처리해주어야 하는 예외들만 선언하므로,

보통 프로그래머의 실수로 발생하는 예외인 RuntimeException은 메서드 선언부에 적지 않는다고 한다.

Exception 클래스들은 메서드 선언부에 적지 않으면 컴파일 단계에서 에러가 발생!

 

이상으로 포스팅을 마칩니다. 감사합니다.

 

 

참고 문헌

자바의 정석(저자: 남궁성, 예외 처리)

반응형

댓글