예외 처리

목표

  • 자바의 예외 처리에 대해 학습하세요.

학습할 것

  • 자바에서 예외 처리 방법 (try, catch, throw, throws, finally)
  • 자바가 제공하는 예외 계층 구조
  • Exception과 Error의 차이는?
  • RuntimeException과 RE가 아닌 것의 차이는?
  • 커스텀한 예외 만드는 방법

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


예외 처리란

자바 어플리케이션에서 발생하는 오류를 에러와 예외로 구분합니다. 이에 대한 구분은 아래에 설명하도록 하겠습니다.

  • 예외 처리의 정의 : 프로그램 동작시 예기치 못한 예외의 발생에 대비한 코드를 작성하는 것입니다.
  • 예외 처리의 목적 : 으로는 프로그램의 비정상 종료를 막고, 정상적인 프로그램 실행상태를 유지하기 위함입니다.
예외 처리 구조

기본적으로 try-catch문을 사용하며, 그 구조는 다음과 같습니다.

try {
  // 예외가 발생할 가능성이 있는 코드들
} catch (Exception1 e1) {
  // Exception1 이 발생할 경우, 이를 처리하기 위한 코드
} catch (Exception2 e2) {
  // Exception2 이 발생할 경우, 이를 처리하기 위한 코드
}
...

특징으로는 아래와 같습니다.

  • 하나의 try블럭 다음에는 여러 종류의 예외를 처리하도록 구현할 수 있습니다.
  • 예외 발생시에는 해당하는 단 한가지의 catch구문에서만 예외를 처리합니다.
  • 해당하는 Exception이 없을경우에는 예외는 처리되지 않습니다.

try-catch문에서 주의해야할 점으로는

  • try블럭 내에서 예외가 발생시, try에 남아있는 코드를 실행시키지 않고 바로 예외 코드를 실행하고 넘어갑니다.
예외 발생의 흐름
  1. 예외가 발생하게 되면, 해당 예외의 인스턴스가 생성됩니다.
  2. 그러면 이 예외를 처리할 수 있는 catch구문이 나올 때까지 instanceof연산자를 이용해 검사합니다.
  3. instanceof연산자의 결과가 true인 catch블럭이 나올때까지 검사합니다.

모든 예외클래스들은 Exception클래스의 자손이므로, catch구문에 Exception을 명시하면 어떤 종류의 예외가 발생하더라도 처리할 수 있습니다.

try {
  // 예외가 발생할 가능성이 있는 코드들
} catch (Exception e) {
  // 어떤 종류의 Exception이 발생하더라도 처리함
}
유용한 함수들
  • printStackTrace() : 예외발생 당시의 호출스택(call stack)에 있었던 메서드의 정보와 예외 메세지를 출력합니다.
  • getMessage() : 발생한 예외클래스의 인스턴스에 저장된 메시지를 얻습니다.
try {
  // 예외가 발생할 가능성이 있는 코드들
} catch (Exception e) {
  e.printStackTrace();
  e.getMessage();
}

접근방법으로는 catch구문에 선언한 Exception의 참조변수를 통해 접근 할 수 있습니다. 이 참조변수는 catch구문 안에서만 사용될 수 있습니다.

throw 키워드

throw 키워드를 통해 예외를 고의로 발생시킬 수 있습니다.

순서로는 아래와 같습니다.

  1. 연산자 new를 통해 예외클래스를 생성합니다.
     Exception e = new Exception("testing exception");
    
  2. 키워드 throw를 이용해 예외를 발생시킵니다.
     throw(e);
    
try {
  Exception t = new Exception("testing exception");
  throw(t);
  // 한줄로 축약시킬 경우 throw new Exception("testing exception");
} catch (Exception e) {
  e.printStackTrace();
  e.getMessage();
throws 키워드

예외를 처리하는 또다른 방법으로는 throws 키워드를 사용해 메서드가 예외를 처리하는 방법이 있습니다. 메서드에 예외를 선언하려면, 메서드 선언부에 throws 키워드를 추가하고 해당 예외클래스를 적어주면 됩니다.

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

메서드에 예외를 선언하면 몇 가지 유의해야할 점들이 있습니다.

  1. 메서드에 예외 선언시, 이 예외뿐만 아니라 그 자손타입의 예외도 발생할 수 있습니다. 메서드의 선언부를 보고 어떤 예외가 발생할 수 있는지 손쉽게 예측할 수 있습니다.
  2. 예외가 선언된 메서드를 호출시, 해당 예외를 반드시 호출하는 측에서 처리해야합니다.

메서드에 예외를 throws에 선언한다는 것은 예외를 처리하는 것이 아니라, 자신()을 호출한 메서드에게 예외를 전달하여 예외처리를 떠맡기는 것입니다.

class ExceptionTest {
  public static void main(String[] args) throws Exception {
    method1();
  }
  static void method1() throws exception {
    method2();
  }
  static void method2() throws Exception {
    throw new Exception();
  }
}

위의 코드를 실행하면

java.lang.Exception
      at ExceptionTest.method2(ExceptionTest.java:x)
      at ExceptionTest.method1(ExceptionTest.java:x)
      at ExceptionTest.main(ExceptionTest.java:x)

아래와 같은 결과를 볼 수 있습니다.

이를 통해 우리는

  1. 예외 발생시, 모든 메서드(method2, method1, main)이 호출스택에 있었고
  2. 예외가 발생한 곳은 제일 윗줄에 method2
  3. 호출 순서는 main -> method1 -> method2 라는 것을 알 수 있습니다.
finally 키워드

finally 키워드는 예외가 발생하든 안하든 실행시키고자 하는 구문이 있을 경우 사용하는 키워드입니다.

try {
  // 예외 발생가능한 구문
} catch (Exception1 e1) {
  // 예외처리
} finally {
  // 예외와 관계없이 항상 실행시키는 구문
}

예외가 발생하는 경우에는

  • try -> catch -> finally 예외가 발생하지 않는 경우에는
  • try -> finally 순서로 진행됩니다.

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

모든 예외의 최고 조상은 Exception 클래스입니다. Exception의 조상을 보면 아래와 같습니다.

Object
 |-- Throwable
      |-- Exception
      |-- Error

Exception과 Error의 차이는?


우리가 흔히 이야기하는 Error는 자바에서 3가지로 나뉜다.

  • 컴파일 에러 : 소스코드를 컴파일 할 때 컴파일러가 잡아낼 수 있는 에러, 컴파일이 성공하면 클래스파일(*.class)이 생성된다.
  • 런타임 에러 : 컴파일이 성공하여 클래스파일을 실행시킬 때 발생 할 수 있는 에러
  • 논리적 에러 : 실행은 되지만 의도와는 다르게 작동하는 에러

특히, 런타임 에러의 경우, 자바는 발생할 수 있는 오류를 Error와 Exception을 구분하였습니다.

Error(에러) : 프로그램 코드에 의해 수습될 수 없는 치명적 오류 Exception(예외) : 프로그램 코드에 의해 수습될 수 있는 마이너한 오류

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


상속계층도를 Exception클래스부터 도식화하면 아래와 같습니다.

Exception
 |-- IOException
 |-- ClassNotFoundException
 |-- ...
 |-- RuntimeException
      |-- ArithmeticException
      |-- ClassCastException
      |-- NullPointerException
      |-- ...
      |-- IndexOutOfBoundsExcpetion

위에서 보듯이 예외클래스들은 두 그룹으로 나눠질 수 있습니다.

  1. Exception클래스와 그 자손들
  2. RuntimeExeception클래스와 그 자손들
Exception클래스

Exception클래스들은 주로 외부의 영향으로 발생할 수 있는 것들입니다. 예를 들어,

  • 존재하지 않는 파일의 이름을 입력 (FileNotFoundException)
  • 실수로 클래스 이름을 잘못 적었을 경우 (ClassNotFoundException)
  • 입력한 데이터 형식이 잘못된 경우 (DataFormatException)
RuntimeException클래스

RuntimeException클래스는 주로 프로그래머의 실수에 의해서 발생될 수 있는 예외들로, 자바의 프로그래밍 요소들과 관련이 깊습니다.

  • 배열의 범위를 벗어난 경우 (ArrayIndexOutOfBoundsException)
  • 값이 null인 참조변수의 맴버를 호출하는 경우 (NullPointerException)
  • 클래스간의 형변환이 잘못된 경우 (ClassCastException)
  • 정수를 0으로 나누려고하는 경우 (ArithmeticException)

커스텀한 예외 만드는 방법


우리는 필요에 따라 커스텀 예외를 만들 수도 있습니다.

  • 가능하다면 새로운 예외 클래스를 만들기보다 기존의 예외클래스를 활용하는 것이 좋습니다.
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) {
    this(msg, 100);
  }

  public int getErrCode() {
    return ERR_CODE;
  }
}

위와 같이 예외클래스를 구현했을 때, MyException 발생시, catch블럭에서 getMessage()와 getErrCode()를 사용해서 에러코드와 메시지를 모두 얻을 수 있습니다.

출처

자바의 정석