[Java] 예외 던지기

예외 던지기


1. 예외 되던지기


한 메서드에서 발생할 수 있는 예외가 여럿인 경우, 몇 개는 try-catch문을 통해서 메서드 내에서 자체적으로 처리하고, 그 나머지는 선언부에 지정하여 호출한  메서드에서 처리하도록 함으로써, 양쪽에서 나눠서 처리되도록 할 수 있다. 그리고 심지어는 단 하나의 예외에 대해서도 예외가 발생한 메서드와 호출한 메서드, 양쪽에서 처리하도록 할 수 있다.

이것은 예외를 처리한 후에 인위적으로 다시 발생시키는 방법을 통해서 가능한데, 이것을 '예외 던지기'이라고 한다.

먼저 예외가 발생한 가능성이 있는 메서드에서 try-catch문을 사용해서 예외를 처리해주고 catch문에서 필요한 작업으로 행한 후에 throw문을 사용해서 예외를 다시 발생시킨다.

다시 발생한 예외는 이 메서드를 호출한 메서드에게 전달되고 호출한 메서드의 try-catch문에서 예외를 또다시 처리한다.

이 방법은 하나의 예외에 대해서 예외가 발생한 메서드와 이를 호출한 메서드 양쪽 모두에서 처리해줘야 할 작업이 있을 때 사용된다.

이 때 주의할 점은 예외가 발생할 메서드에서는 try-catch문을 사용해서 예외처리를 해줌과 동시에 메서드의 선언부에 발생할 예외를 throws에 지정해줘야 한다는 것이다.


class ExceptionEx23 {

public static void main(String[] args) {

try  {

method1();

} catch (Exception e) {

System.out.println("main메서드에서 예외가 처리되었습니다.");

}

} // main메서드의 끝


static void method1() throws Exception {

try {

throw new Exception();

} catch (Exception e) {

System.out.println("method1메서드에서 예외가 처리되었습니다.");

throw e; // 다시 예외를 발생시킨다.

}

} // method1메서드의 끝

}

실행결과)

method1메서드에서 예외가 처리되었습니다.

main메서드에서 예외가 처리되었습니다.

결과에서 알 수 있듯이 method1()과 main메서드 양쪽이 catch블럭이 모두 수행되었음을 알 수 있다. method1()의 catch블럭에서 예외를 처리하고도 throw문을 통해 다시 예외를 발생시켰다.

그리고 이 예외를 main 메서드 한 번 더 처리 하였다.


2. 메서드에 예외 선언


메서드에 예외를 선언하려면, 메서드의 선언부에 키워드 throws를 사용해서 메소드 내에서 발생할 수 있는 예외를 적어주기만 하면 된다. 그리고, 예외가 여러 개일 경우에는 쉼표(,)로 구분한다.

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

.... 메서드의 내용

}

--> 이 메서드는 Exception1, Exception2....ExceptionN와 같은 Exception이 발생할 수 있으니, 이 메서드를 호출하고자 하는 메서드에서는  Exception1, Exception2....ExceptionN을 처리 해주어야 한다는 뜻이다. 자신을 호출한 메서드에 예외를 전가시키는 것. 예외를 발생시키는 키워드 throw와 예를 메서드에 선언할 때 쓰이는 throws를 잘 구별하자


이렇게 메서드의 선언부에 예외를 선언함으로써 메서드를 사용하려는 사람이 메서드의 선언부를 보았을 때, 이 메서드를 사용하기 위해서는 어떠한 예외들이 처리되어져야 하는 쉽게 알 수 있다.

자바에서는 메서드를 작성할 때 메서드 내에서 발생할 가능성이 있는 예외를 메서드의 선언부에 명시하여 이 메서드를 사용하는 쪽에서는 이에 대한 처리를 하도록 강요하기 때문에, 프로그래머들의 짐을 덜어 주는 것은 물론이고 보다 견고한 프로그램 코드를 작성할 수 있도록 도와 준다.


메서드에 예외를 선언할 때 일반적으로 RuntimeException클래스들은 적지 않는다. 이 들은 메서드 선언부의 throws에 선언한다고 해서 문제가 되지는 않지만, 보통 반드시 처리해주어야 하는 예외들만 선언한다. 이처럼 Java API 문서를 통해 사용하고자 하는 메서드의 선언부와 'Throws:'를 보고, 이 메서드에서는 어떤 예외가 발생할 수 있으며 반드시 처리해주어야 하는 예외는 어떤 것들이 있는지 확인하는 것이 좋다. 반드시 처리해주어야 하는 예외는 Exception 클래스들(RuntimeException 클래스들 이외의 예외 클래스들)이다.

사실 예외를 메서드의 throws에 명시하는 것은 예외를 처리하는 것이 아니라. 자신(예외가 발생할 가능성이 있는 메서드)을 호출한 메서드에게 예외를 전달하여 예외처리를 떠맡기는 것이다.

예외를 전달받은 메서드가 또다시 자신을 호출한 메서드에게 전달할 수 있으며, 이런 식으로 계속 호출스택에 있는 메서드들을 따라 전달되다가 제일 마지막에 있는 main 메서드에서도 예외가 처리되지 않으면, main 메서드 마저 종료되어 프로그램이 전체가 종료된다.


class ExceptionEx18 {

public static void main(String[] args) throws Exception {

method1(); // 같은 클래스내의 static멤버이므로 객체생성없이 직접 호출가능.

  } // main메서드의 끝


static void method1() throws Exception {

method2();

} // method1의 끝


static void method2() throws Exception {

throw new Exception();

} // method2의 끝

}

실행결과)

Exception in thread "main" java.lang.Exception

at example.ExceptionEx18.method2(ExceptionEx18.java:13)

at example.ExceptionEx18.method1(ExceptionEx18.java:9)

at example.ExceptionEx18.main(ExceptionEx18.java:5)

위의 실행 결과를 프로그램의 실행도중에 java.lang.Exception이 발생하여 비정상적으로 종료했다는 것과 예외가 발생했을 때 호출스택(Call Stack)의 내용을 알 수 있다.


위의 결과로 부터 다음과 같은 사실을 알 수 있다.

1 예외가 발생했을 때, 모두 3개의 메서드(main, method1, method2)가 호출 스택에 있었다

2 예외가 발생한 곳은 제일 윗줄에 있는 method2()이다.

3 main 메서드가 method1()를 , 그리고 method1()은 method2()를 호출했다는 것을 알 수 있다.


위의 예제를 보면, method2()에서 'throw new Exception();' 문장에 의해 예외가 강제적으로 발생했으나 try-catch문으로 예외처리를 해주지 않았으므로, method2()는 종료되면서 예외를 자신을 호출한 method1()에게 넘겨준다. method1()에서도 역시 예외처리를 해주지 않았으므로 종료되면서 main메서드에게 예외를 넘겨준다.

그러나 main메서드에서 조차 예외처리를 해주지 않았으므로 main 메서드가 종료되어 프로그램이 예외로 인해 비정상적으로 종료되는 것이다.

이처럼 예외가 발생한 메서드에서 예외처리를 하지 않고 자신을 호출한 메서드에게 예외를 넘겨줄 수는 있지만, 이것으로 예외가 처리된 것은 아니고 예외를 단순히 전달만 하는 것이다.


결국 이는 한 곳에서는 반드시 try-catch문으로 예외처리를 해주어야 한다.

class ExceptionEx19 {

public static void main(String[] args) {

method1(); // 같은 클래스내의 static멤버이므로 객체생성없이 직접 호출가능.

  // main메서드의 끝


static void method1() {

try {

throw new Exception();

} catch (Exception e) {

System.out.println("method1메서드에서 예외가 처리되었습니다.");

e.printStackTrace();

}

// method1의 끝

}

printStackTrace()를 통해 예외에 대한 정보를 화면에 출력하였다. 예외가 method1()에서 발생했으며, main 메서드가 method1()을 호출했음을 알 수 있다.


class ExceptionEx20 {

public static void main(String[] args) {

try  {

method1();

} catch (Exception e) {

System.out.println("main메서드에서 예외가 처리되었습니다.");

e.printStackTrace();

}

// main메서드의 끝


static void method1() throws Exception {

throw new Exception();

// method1()의 끝

} // class의 끝

ExceptionEx19는 method1()에서 예외처리를 했고, ExceptionEx20은 method1()에서 예외를 선언하여 자신을 호출하는 메서드(main 메서드)에 예외를 전달했으며, 호출한 메서드(main 메서드)에서는 try-catch문으로 예외처리를 했다.

ExceptionEx19 처럼 예외가 발생한 메서드(method1) 내에서 처리되어지면, 호출한 메서드(main 메서드)에서는 예외가 발생했다는 사실조차 모르게 된다.

ExceptionEx20처럼 예외가 발생한 메서드에서 예외를 처리하지 않고 호출한 메서드로 넘겨주면, 호출한 메서드에서는 method1()을 호출한 라인에서 예외가 발생한 것으로 간주되어 이에 대한 처리를 하게 된다. 이처럼 예외가 발생한 메서드 'method1()'에서 예외를 처리할 수도 있고, 예외가 발생한 메서드를 호출한 메서드 'main 메서드' 에서 처리할 수도 있다. 또는 두 메서드가 예외 처리를 분담할 수 있다.



'Programing > Java' 카테고리의 다른 글

[Java] 객체를 제거하는 방법  (1) 2015.05.19
[Java] Wrapper 클래스  (0) 2015.04.13
[Java] 예외 만들기  (0) 2014.12.25
[Java] 예외 처리 기본  (0) 2014.12.25
[Java] enum  (0) 2014.12.25