본문 바로가기

나 어제 배웠다/JAVA[Tip]

예외 처리(Exception)

  예외 처리   
 
1.1   예외 처리 정의
예외 처리 (exception handling) 란 무엇인가? 이 질문에 대답하기 전에 `예외란
무엇인가' 라는 문제를 먼저 고려해 보아야 할 것이다. 예외 (exception) 라는 것은
사전적인 의미로 `규칙이나 약속에서 벗어남' 을 의미한다. 프로그래밍 언어에서
예외라는 것은 프로그램이 알고리즘 (algorithm) 의 정상적인 흐름을 벗어나 다른
경로 (path) 로 폭주하는 경우를 말한다. 프로그램이 예상된 경로를 벗어나서
임의의 경로, 즉 비결정된 경로로 흐르는 것은 프로그램에 이상이 발생한 것이며,
에러 (error) 또는 버그 (bug) 가 있는 프로그램이라고 할 수 있다.

그러면 에러 (error) 와 예외 (exception) 사이의 관계는 어떠한가? 이러한 관계와
프로그래밍시 발생할 수 있는 결점 (fault) 에 관련된 용어를 정리해 보면 표 1 과
같다.

즉, 예외 (exception) 라는 것은 프로그램중에 발생할 수 있는 가벼운 에러 상태
(mild error condition) 이며 예외 처리는 예외가 발생한 상황에서 프로그램을
종료하지 않고 가능한 한 예외를 핸들링하여 프로그램의 흐름을 복구하고, 초기에
의도하던 방향으로 프로그램을 수행할 수 있게 하는 것을 의미한다.

  표 1  예외 관련 용어 목록
용 어 용어의 의미 버그 (bug) 버그, 잘못, 프로그램이나 하드웨어의 잘못된 동작, 또는 그 오동작의 원인이되는 부분. 결점 (fault) 고장, 오류, 전자 부품이나 컴퓨터 회로, 주변기기 등의 고장이나 오동작에 의해빚어지는 비정상적인 상태. 이러한 고장은 주로 하드웨어적인 것을 가리킨다. ?? error, mistake 실패 (failure) 실패, 고장, (1) 시스템에서 요구된 기능을 수행하는 각 기능단위와 능력이없어지거나 제대로 발휘되지 않는 것. (2) 프로그램 수행 도중 회복할 수 없는오류가 발생하여 프로그램의 수행을 계속할 수 없는 상태. 에러 (error) 오류, (1) 어떤 원인에 의해 발생되는 잘못을 가리키는 일반적인 말.(2) 컴퓨터의 연산처리 결과가 하드웨어나 소프트웨어의 잘못, 사용자의 실수 등 여러 요인에 의해 초기의 것과는 다른 결과로 나타나는 것. 예외 (exception) 예외 상황, 컴퓨터 시스템의 동작 도중 예기치 않았던 이상 상태가 발생하고 그로인해 수행중인 프로그램이 영향을 받는 것. 예를 들면 연산 도중 오버플로우에 의해 발생한 인터럽트 등이 여기에 해당한다. ?? interrupt, trap

그러면 자바에서 예외 처리 상황과 비교되는 에러란 무엇인가? 에러 (error) 는
프로그램의 복구가 불가능한 심각한 상태를 말하며 결국 프로그램이 종료되어야
하는 상황을 의미한다.

2 예외 처리 사용 목적
예외 처리 (exception handling) 를 사용하는 목적은 한마디로 "보다 안정적이고
오류에 강한 프로그램을 만드는 것" 이다. 이런 일반적인 이유 외에도 자바
프로그램의 특성을 이해하면 예외 처리의 필요성에 대해서 공감할 수 있을 것이다.

자바 프로그램은 인터넷 (internet) 또는 인트라넷 (intranet) 과 연계하여 수행이
가능한 프로그램이다. 인터넷과 인트라넷 네트워크에서는 물리적으로나 논리적으로
떨어져 있는 거리 (distance), 보안 (security), 위치 (location), 접근(access)
등의 이유로 많은 예외 상황이 존재한다. 따라서 자바 프로그램에서 인터넷이나
인트라넷에서 발생되는 많은 예외 상황에 대한 대처 및 고려가 필수적으로 필요하다.

3 예외 처리 사용 시기
예외 처리를 사용하는 시기는 그것을 사용하는 목적에 비추어 볼 때 보다
안정적이고 오류에 강한 프로그램을 만들려고 하는 경우라면 언제든지 사용할 수
있다. 이 중에서도 예외 상황이 사용되는 전형적인 경우를 살펴보면 다음과 같다.

  표 2  예외 발생 시기
예외 발생 시기 예외 발생 이유 파일을 디스크에서 개방시 ·파일이 존재하지 않는 경우·파일의 허가 권한이 적합하지 않은 경우 변수 사용시 ·정의 (definition) 안 된 변수를 사용할 경우·변수 선언의 구역 (scope) 이 부적절할 경우 산술 연산시 ·산술 연산의 결과가 부정 또는 불능일 경우 여러 보안 처리시 ·보안 상황을 위배하는 경우 URL 사용시 ·URL 의 형식이 잘못된 경우 질의어 사용시 ·질의어의 내용이 잘못된 경우 기타 여러 상황  

4 예외 범주 및 종류
4.1 예외 범주
자바의 모든 클래스는 Object 라는 클래스의 하위 클래스 (sub class) 로 만들어지며
예외 처리를 지원하기 위해 Throwable 이라는 클래스를 모든 예외 처리를 위한 상위
클래스 (super class) 로 정의하고 있다.

즉, 모든 예외 클래스는  Throwable 클래스의 하위 클래스가 되고 자연스럽게
Throwable 클래스의 모든 내용을 상속받는다. Throwable 클래스의 하위에는
Error 와 Exception 클래스가 있고 Exception 클래스 하위에 Runtime-
Exception 클래스가 있다.

그림 1  Exception 클래스의 상속 형태

이렇게 자바의 예외 처리에 관련된 클래스는 크게 Error 와 Exception 그리고
RuntimeException 의 세 가지 범주로 나뉘어지게 된다. 이제 각각의 클래스의
쓰임에 대하여 알아보자.

Error 클래스

Error 클래스는 프로그램에서 발생하는 치명적인 문제점들을 정의한 클래스이다.
예를 들어, 메모리 부족 (out of memory) 등의 상황은 더 이상의 프로그램 진행을
불가능하게 만들고 에러가 발생하게 되면 프로그램의 흐름을 복구할 방법이 없다.
이런 경우에 프로그래머는 프로그램 자체를 수정해야 할 것이다. 따라서 Error
클래스에 대해서는 기타 지식이 필요하지 않다.

Exception 클래스

Exception 클래스는 모든 예외 클래스의 상위 클래스로 사용된다. 자바에서
사용하고 있는 모든 예외는 Exception 클래스의 후손이다. 사용자가 새로운
예외를 만들려면 Exception 클래스를 상속받게 된다

다음과 같이 작성하면 MyNewException 이라는 새로운 Exception 클래스가
만들어진다. Exception 은 클래스이고 클래스는 타입 (type) 이며 상속으로
만들어진 MyNewException 도 하나의 타입이 되어 예외로서 사용이 가능하다.

─────────────────────────────
    사용예 (Usage) : Exception 정의
─────────────────────────────
  import java.lang.*;

  public class MyNewException extends Exception {
    String reason;

    public MyNewException (String reason) {
      this.reason = reason;
    }

    public String toString ( ) {
      return reason;
    }
  }
────────────────────────────

RuntimeException 클래스

RuntimeException 클래스는 자바 프로그램의 설계나 구현시에 사용되어야 하는
예외로서 프로그램이 정상적으로 수행된다면 발생하지 않지만 프로그램의 수행
중에 예외 상황이 생기면 발생하는 예외이다.

예를 들어, 배열을 사용하는 프로그램에서 배열의 첨자 (subscript) 가 해당되는
배열의 크기 이내에서 수행되는 프로그램이라면 상관없으나 프로그래머의 실수로
인해 첨자를 배열의 크기보다 큰 수를 사용하는 경우에는 예외가 발생한다.

─────────────────────────────
  사용예 (Usage) : RuntimeException 발생 경우
─────────────────────────────
  String message [ ] = {one", "two", "three", "four"};

  for ( int k = 0 ; k < 5 ; k++ ) {
    System.out.println (message [k]);
  }
─────────────────────────────

4.2   자바 예외 종류
자바에서의 예외는 java.lang.Exception 에 의해 상속받아서 만들어지고 자바의
패키지 (클래스들의 그룹) 를 만들 때 정의되어 각 메소드 단위로 사용되므로 그
수가 정해진 것이 아니고, 그 이름을 다 암기해서 사용할 수는 없으며 또 그렇게
할 필요도 없다.

큰 바다에 수많은 물고기가 있고 그 물고기의 이름을 모르더라도 물고기를 낚는
방법은 단 한가지인 것처럼 이름을 일일이 열거하는 것은 의미가 없다.

먼저 몇 가지 대표적인 예외 처리에 대하여 살펴보자.

▣ ArithmeticException : 변수를 0 으로 나누는 경우에 발생

─────────────────────────────
  사용예 (Usage) : ArithmeticException 발생 경우
─────────────────────────────
        int x = variable / 0;
─────────────────────────────

ArithmeticException 은 변수를 0 으로 나누는 경우에 발생하는데 프로그램에
이러한 스타일의 코딩을 한다면 컴파일시에 에러가 발생한다. 그러나 프로그램의
수행중에 이와 같은 상황으로 프로그램이 전개된다면 그때 Arithmetic- Exception
이 발생한다.

▣ NullPointerException : Object 를 실체화하지 않고 사용하는 경우에 발생

─────────────────────────────
  사용예 (Usage) : NullPointerException 발생 경우
─────────────────────────────
    Image im [ ] = new Image [4];
    g.drawImage (im [0], x, y, this);
─────────────────────────────

NullPointerException 은 객체 (object) 를 new 에 의해 실체화 (instantiation)
하지 않고 사용하는 경우에 발생한다. 자바는 포인터 연산을 지원하지는 않지만
포인터를 사용하고 있고 모든 객체는 new 키워드에 의해 실체화한 후에 사용
가능하다. 앞의 프로그램에서는 Image 클래스를 사용하여 배열을 선언하고 new
키워드를 이용하여 실체화했으나 각각의 Image 항목에 대한 실체화가 실현되지
않은 채로 사용되고 있기 때문에 NullPointerException 이 발생한다. 그러므로
앞의 프로그램을 적절히 수정하면 다음과 같다.

─────────────────────────────
  사용예 (Usage) : NullPointerException 발생 수정
─────────────────────────────
    String imageName [ ] = {"a.gif", "b.gif", "c.gif", "d.gif"};
    Image im [ ] = new Image [4];

    for ( int k = 0; k < im. length; k++ ) {
            im [k] = getImage(getDocumentBase( ), imageName [k]);
    }

    g.drawImage (im [0], x, y, this);
─────────────────────────────

▣ ClassCastException : 클래스를 불가능한 타입으로 변환시킬 때 발생

─────────────────────────────
  사용예 (Usage) : ClassCastException 발생
─────────────────────────────
    class Parent {
      ...
    }

    class Derived extends Parent {
      ...
    }

    Derived d1 = new Derived ( );
    Derived d2 = new Derived ( );
    Parent p = new Parent ( );

    d1 = p;
─────────────────────────────

ClassCastException 은 클래스를 불가능한 타입으로 변환시킬 때 발생한다.
위 프로그램에서는 임의 클래스 Parent 를 작성하고 하위 클래스로서 Derived
클래스를 작성한 후 각 클래스에서 널 (null) 이 아닌 객체 (object) d1, d2,
p 를 생성했다.

객체간의 변환 (casting) 을 행할시에 가능한 경우와 그렇지 않은 경우는 어떠
한가를 알아보자. 가능한 모든 조합을 생각해 보면 표 3 과 같다.

표 3 클래스간의 변환 (casting)
변환 방법 설  명 불/가 p = (Parent) d1; 후손 객체를 타입 명시하여 상위 객체로 변환 가 p = d1; 후손 객체를 타입 명시 없이 상위 객체로 변환 가 d1 = (Derived) p; 상위 객체를 타입 명시하여 하위 객체로 변환 불 d1 = p; 상위 객체를 타입 명시 없이 하위 객체로 변환 불 d1 = (Derived) d2 후손 객체를 타입 명시하여 다른 후손 객체로 변환 불 d1 = d2 후손 객체를 타입 명시 없이 다른 후손 객체로 변환 불

표 3 의 내용과 같이 상속된 클래스 객체간의 타입 변환시에는 하위 클래스에서 상위
클래스로만 자연스러운 변환이 일어난다는 것을 알 수 있다.
즉, 예제 프로그램에서는 상위 클래스의 객체를 하위 클래스의 객체로 형 변환을
시도했기 때문에 ClassCastException 이 발생한 것이다. 따라서 앞의 프로그램을
적절히 수정하면 다음과 같다.

─────────────────────────────
  사용예 (Usage) : ClassCastException 발생 수정
─────────────────────────────
        class Parent {
        ...
        }

        class Derived extends Parent {
        ...
        }

        Derived d1 = new Derived ( );
        Derived d2 = new Derived ( );
        Parent p = new Parent ( );

        p = d1;
─────────────────────────────

▣ IOException : 파일 입출력시에 실제로 파일이 없거나 네트워크
  I/O 시에 발생

─────────────────────────────────
  사용예 (Usage) : IOException 발생
─────────────────────────────────
  FileOutputStream fos = new FileOutputStream ("filename.txt") ;
  DataOutputStream dos = new DataOutputStream (fos) ;

  dos.writeUTF ("some string") ;
─────────────────────────────────

IOException 은 파일 입출력시에 실제로 파일이 없거나 파일이 있더라도 개방 (open)
할 수 없거나 네트워크 소켓 (socket) 으로부터 전송이나 수신을 할 수 없는 경우에
발생하며 자바에서 가장 많이 발생하는 예외 중의 하나이다. 그리고 중요한 점은
다른 많은 예외가 그렇듯이 IOExceptio 은 위 프로그램 수행중 IOException 이
발생하지 않더라도 예외 처리를 해주어야 한다는 점이다. 이것은 특정 프로그램
코드 (특히 입출력의 경우) 는 예외가 될 경우가 많기 때문이다. 그러므로 위
프로그램을 적절히 수정하면 다음과 같다.

─────────────────────────────
  사용예 (Usage) : IOException 발생 수정
─────────────────────────────
    FileOutputStream fos;
    DataOutputStream dos;

    try {
      fos = new FileOutputStream ("filename.txt");
      dos = new DataOutputStream (fos);
      dos.writeUTF ("some string");
    }catch (IOException e) {
      e.printStackTrace ( );
    }
─────────────────────────────

위 프로그램에서는 예외 처리에서 사용하는 키워드를 이용하여 예외 문제를 해결
하였는데 자세한 사항은 다음 절에서 설명하기로 한다.

▣ NegativeArraySizeException : 배열의 크기를 음수로 생성하는 경우에 발생

─────────────────────────────
  사용예 (Usage) : NegativeArraySizeException 발생
─────────────────────────────
        int array [ ] = new int [-30];
─────────────────────────────

NegativeArraySizeException 은 배열의 크기를 음수로 생성하는 경우에 발생하는데
실행시에 이러한 상황이 유발한 경우도 역시 같은 예외가 된다.

▣ ArrayIndexOutOfBoundsException : 배열의 실제 크기보다 더 큰 항목을
  참조하려고 하는 경우에 발생

────────────────────────────────────
  사용예 (Usage) : ArrayIndexOutOfBoundsException 발생
────────────────────────────────────
   int array [ ] = new int [10] ;

   for (int I = 0; i < = 10; i++) {
       System.out.println (array [i] );// I = 10 일 때 Exception 발생
   }
────────────────────────────────────

ArrayIndexOutOfBoundsException 은 배열의 실제 크기보다 더 큰 항목 (인덱스) 을
참조하려고 하는 경우에 발생하는 예외로 가장 많이 발생하는 예외 중의 하나이다.
위 프로그램의 경우 배열 array 의 크기는 10 이다. 즉 첨자의 범위는 0~9 까지이다.
그런데 i 가 10 이 되면 해당되는 배열 항목이 없게 되며 이런 경우가
ArrayIndexOutOfBoundsException 에 해당된다.

▣ MalFormedURLException : URL 을 사용할 때 URL 의 형식이 잘못된 경우에 발생

─────────────────────────────
  사용예 (Usage) : MalFormedURLException 발생
─────────────────────────────
  URL u = new URL ("http://xxx.domain.co.kr/filename.txt");
─────────────────────────────

MalFormedURLException 은 URL 을 사용할 때 URL 의 형식이 잘못된 경우에 발생한다.
더 정확히 말하면 사용하는 URL 형식이 잘못될 가능성이 많기 때문에 사용하는
Exception 이다. 즉, IOException 과 같은 경우라고 할 수 있다. 따라서 URL 의
형식이 바르게 되어 있는 경우라도 MalFormedURL- Exception 예외 처리를 해주어야
한다. 그러므로 앞의 프로그램을 적절히 수정하면 다음과 같다.

─────────────────────────────
  사용예 (Usage) : MalFormedURLException 발생 수정
─────────────────────────────
    URL u;

    try {
      u = new URL ("http://xxx.domain.co.kr/filename.txt");
    } catch (MalFormedURLException e) {
      e.printStackTrace (&nbs;);
    }
─────────────────────────────

이상으로 대표적인 예외 (exception) 에 대하여 살펴보았다. 이렇게 모든 예외를
숙지한 후에 사용한다면 자바 프로그램이 쉽다고 말할 수는 없을 것이다.

지금까지 설명한 내용은 바다에 수많은 고기 중에서 몇 종의 물고기만을 설명한
것에 불과하다. 이러한 방법으로는 자바의 예외를 다 처리할 수가 없다.
그렇다면 고기의 종류에 관계없이 모든 물고기를 낚을 수 있는 방법은 없는가?
대답은 `있다' 이다. 그 방법을 다음 절에서 소개한다.

5 예외 처리 사용법
자바에서는 C++ 의 예외 처리 (exception handling) 와 유사한 방법으로 예외
처리를 지원하고 있다. 예외 처리는 프로그램을 보다 안정적으로 수행할 수
있도록 도와준다. 자바는 기본적으로 네트워크 상황에서 작업하는 경우가 많고,
네트워크에서는 처리가 비정상적으로 흘러 예기치 못하는 경우가 많이 생기기
때문에 자바에서 예외 처리는 반드시 필요하다고 할 수 있다.

앞절에서 설명한 예외 처리는 소개한 코드를 수행시킬 때 발생할 수도 있고 하지
않을 수도 있다. 예외 처리를 편리하게 구분해 보면 실행 시간 (runtime) 의 예외
처리와 컴파일 시간 (compile time) 의 예외 처리로 나누어 볼 수 있다.

실행 시간의 예외 처리는 실행시의 상황에 따라 예외 처리가 발생할 수도 있고
하지 않을 수도 있다. 그러나 컴파일 시간의 예외 처리는 실제로 예외 처리가
발생하지 않더라도 프로그램 코딩은 예외 처리에 대한 핸들러 (handler) 가
정의되어 있어야 한다.

5.1 예외 발생
예외가 발생하는 프로그램을 예로 들어 예외를 처리하는 방법에 대하여 설명해 보자.

다음은 예외가 발생하는 프로그램이다. 배열은 세 개의 스트링을 가지고 있고 while
문은 반복적으로 스트링을 출력시키며 첨자를 증가시킨다. 스트링의 배열에는 배열의
항목이 세 개밖에 없으므로 첨자 i 값이 3 이 되었을 때
ArrayIndexOutOfBoundsException 이 발생하게 된다.

─────────────────────────────
  Program. ExceptionTest.java
─────────────────────────────
  public class ExceptionTest {

    public static void main (String args [ ]) {
       int i = 0;

        String messages [ ] = {
            "Hello, World!",
            "Don ' t Worry, Be Happy !",
            "Wonderful Tonight !"
        };

        while (i  <  4) { // i 가 3 이 될 때 예외 발생
            System.out.println (messages [i]);
            i ++;
        }
    }
  }
─────────────────────────────

앞의 프로그램을 수행했을 때의 결과는 그림 2 와 같다.

 

그림 2  ArrayindexOutBoundsException 발생 상황


5.2 예외 처리
앞의 프로그램을 예외 처리 기법을 이용하여 처리해 보자. 예외 처리를 하려면 몇
가지 자바의 키워드의 용도를 파악하는 것이 필요하다.

Try 예약어 (Reserved Word)

특정 예외 (exception) 를 찾아내려면 예외가 발생될 것이라고 예견되는 코드나 이미
특정 예외가 발생된 코드를 try 의 블록 안에 기술한다. 즉 try 블록은 보호된 코드
블록이 되는 것이다.

Catch 예약어 (Reserved Word)

catch 블록은 예외가 발생되었을 경우, 해당되는 예외를 잡아내어 프로그램의 흐름을
복구할 것인지 종료할 것인지를 결정하는 프로그램을 기술해 두는 장소이다. 정해진
try 블록에서 하나 이상의 예외가 발생한다면 catch 문을 연속적으로 작성할 수도
있고 catch 문 안에서 try 블록을 중첩 (nested) 되게 기술할 수도 있다.

─────────────────────────────
  사용예 (Usage) : try & catch 의 사용
─────────────────────────────
  try {
    AException 이 발생할 가능성이 있는 실행문;

  catch (AExceptionType 변수) {
    try {
      AException 의 복구 코드;
      CException 이 발생할 가능성이 있는 실행문;
    } catch (CExceptionType 변수) {
      CException 의 복구 코드;
    }
  } catch (BExceptionType 변수) {
    BException 의 복구 코드;
  }
─────────────────────────────

Finally 예약어 (Reserved Word)

finally 블록은 예외의 발생과 관계없이 항상 수행되는 코드를 기술해 두는 곳이다.
프로그램이 시작되어 일정한 일을 수행할 경우에 그것이 정상적으로 종료되든지
아니면 비정상적으로 종료되더라도 어떤 특정한 일은 반드시 종료되어야 하는
경우가 있다. 예를 들어 수도꼭지를 틀어 잔디에 물을 줄 경우, 잔디밭에 물을 다
주고 수도꼭지를 잠글 수도 있지만, 물이 안 나오는 예외상황이 발생해서 잔디밭에
물을 주지 못한다고 할지라도 물이 갑자기 나올 때를 대비해 수도꼭지를 잠그는
것이 꼭 필요하다. 이와 같은 상황을 기록하는 곳이 finally 블록이다.

─────────────────────────────
  사용예 (Usage) : finally
─────────────────────────────
  try {
     Exception 이 발생할 가능성이 있는 실행문;
  } catch ("특정 Exception 의 타입" 변수) {
     Exception 의 복구 코드;
  } finally {
     항상 수행되는 코드;
  }
───────────────────────────────

finally 블록은 예외의 발생과 관계없이 항상 수행되지만 System.exit ( ) 메소드가
이전에 수행되었다면 finally 블록의 내용은 수행되지 못하고 프로그램은 종료된다.

프로그램의 수정

앞의 프로그램을 예외 처리를 한 상태로 다시 작성하면 다음과 같다.
아래의 프로그램에는 try 블록이 하나 있고 catch 블록이 두 개 있다. 물론 하나의
try 블록에서 발생할 수 있는 예외는 하나 이상일 수 있으나 현재의 try 블록의
코드는 ArrayIndexOutOfBoundsException 외에 다른 예외는 발생하지 않을 것으로
보인다. 첫번째 catch 블록은 ArrayIndexOutOfBoundsException 을 처리하는 데
사용하게 되고 두번째 catch 블록은 catch 되는 타입이 Exception 을 사용하고 있다.
두번째 catch 블록의 의미는 해당되는 try 블록에서 ArrayIndexOutOfBoundsException
이 아닌 다른 예외가 발생하면 이를 처리해 주겠다는 의미로 생각할 수 있다.
왜냐하면 자바의 객체간의 타입 변환 규칙에 의해 파생 클래스는 상위 클래스의
타입으로 변환이 가능하고 모든 예외는 상위 클래스인 Exception 클래스로 변환이
가능하기 때문이다.

─────────────────────────────
  Program. ExceptionHandle.java
─────────────────────────────
  public class ExceptionHandle {
    public static void main (String args [ ]) {
      int i = 0;

      String messages [ ] = {
                "Hello, World !",
                "Don ' t Worry, Be Happy !",
                "Wonderful Tonight !"
      };

      while (i  <  4) {
         try {
            System.out.println (messages [i]);
         } catch (ArrayIndexOutOfBoundsException e) {
            System.out.println ("ArrayIndexOutOf BoundsException called");
            i  =  -1;
         } catch (Exception e) {
            System.out.println(e.toString ( ));
         } finally {
            System.out.println("Always Printed.");
         }

         i ++;
      } // while
    } // main ( )
  } // class
─────────────────────────────

수정한 프로그램을 수행했을 때의 결과는 그림 3 과 같다.

6  사용자 정의 예외 및 활용
6.1 사용자 정의 예외 활용법
사용자의 프로그램에 불안정한 요소가 있거나 프로그램을 보다 안정적으로 유지
하려면 사용자 예외를 정의하여 사용할 수 있다. 사용자 예외를 생성하는 방법은
상위 클래스인 Exception 클래스를 상속받아서 만드는 것이다. 이렇게 하면 예외의
또 하나의 클래스 (또는 타입) 가 생성된다.

새로운 타입을 생성한다는 것은 특정 예외 상황에 특정지을 수 있다는 것을
의미한다. 사용자의 특정한 상황에서 사용자 정의 예외를 사용하는 방법은 해당
상황이 되었을 때에 사용자 정의 예외의 인스턴스 (instance) 를 throw 예약어를
사용하여 발생시키는 것이다. 그렇기 위해서는 발생시킨 예외를 처리해 주는 방법이
필요한데 그것은 자바의 RuntimeException 을 처리하는 방법과 같이 try, catch,
finally 블록을 이용하면 된다.

 

그림 3  ArrayIndexOutOfBoundsException 예외 처리


─────────────────────────────
  사용예 (Usage) : 사용자 정의 예외
─────────────────────────────
  // 사용자 Exception 생성
  public class MyException extends Exception {
    String reason;
    public MyException (String reason) {
      this. reason = reason;
    }

    public void toString ( ) {
      return reason;
    }
  }
----------------------------------------------------------
  // Exception 의 인스턴스를 발생 (throw)
  throw new MyException ( );
─────────────────────────────

6.2 사용자 정의 예외 활용 예제
사용자 정의 예외를 사용하여 처리하는 예제를 살펴봄으로써 예외 처리에 대해
명쾌한 이해를 갖도록 하자.

다음의 예제 프로그램은 사용자 예외를 세 가지로 정의하여 이들이 어떻게 사용될
수 있는가를 보여 주고 있다.

사용자 정의 예외 선언

예제 프로그램은 사용자 예외를 다음과 같이 세 가지로 정의한다. 예외의 이름은
임의로 작성하여 MyNewException1, MyNewException2, MyNewException3 으로 한다.

─────────────────────────────
  Program. 사용자 정의 예외, MyNewException1.java
─────────────────────────────
  // 사용자 정의 예외 1
  public class MyNewException1 extends Exception {
    String reason;

    public MyNewException1 (String reason) {
      this.reason = reason;
    }

    public String toString ( ) {
      return reason;
    }
  }
─────────────────────────────

─────────────────────────────
  Program. 사용자 정의 예외, MyNewException2.java
─────────────────────────────
  // 사용자 정의 예외 2
  public class MyNewException2 extends Exception {
    String reason;

    public MyNewException2 (String reason) {
      this.reason = reason;
    }

    public String toString ( ) {
      return reason;
    }
  }
─────────────────────────────

─────────────────────────────
  Program. 사용자 정의 예외, MyNewException3. java
─────────────────────────────
  // 사용자 정의 예외 3
  public class MyNewException3 extends Exception {
    String reason;

    public MyNewException3 (String reason) {
      this.reason = reason;
    }

    public String toString ( ) {
      return reason;
    }
  }
─────────────────────────────

──────────────────────────────────────
  Note.
──────────────────────────────────────
사용자 정의 예외를 선언할 때 아래의 예제와 같이 작성하고 특정 메소드를
자유롭게 추가할 수 있다.
그러나 예외의 특성상 간단하게 선언하고 사용하여도 무방하다. 왜냐하면
예외는 발생된 내용보다는 발생 그 자체가 더 중요한 의미를 갖기 때문이다.
예를 들면 다음과 같다.
----------------------------------------------------------------------------
        class MyException extends Exception { }
──────────────────────────────────────

사용자 정의 예외 발생

사용자가 정의한 예외를 발생 (throw) 시키는 방법에 대해 알아보자.

다음의 메소드 "ExceptionThrowMtd (int pos)" 는 임의의 정수가 파라미터로
전달되면 해당되는 번호의 예외를 넘기는 메소드이다.
본래 예외는 특정 상황에서 발생하는 것이지만 현재 예제에서는 예외를
선택적으로 발생시키는 방법을 습득하기 위해 정의한 메소드이다.

─────────────────────────────
  사용예 (Usage) : 사용자 정의 예외 발생
─────────────────────────────
  // 예외를 넘기는 구문이다.
  // 해당 함수 안에서 발생되는 예외가 있다면 이를 throws 에 의해
  // 선언해 주어야 한다.
  public void ExceptionThrowMtd (int pos) throws MyNewException1,
              MyNewException2, MyNewException3
  {
    switch (pos) {
      case 1:
        // MyNewException1 예외를 발생시키는 구문이다.
       throw new MyNewException1 ("MyNewException1 발생");
      case 2:
        // MyNewException2 예외를 발생시키는 구문이다.
       throw new MyNewException2 ("MyNewException2 발생");
      case 3:
        // MyNewException3 예외를 발생시키는 구문이다.
       throw new MyNewException3 ("MyNewException3 발생" );
     } // switch
  } // throwExMtd ( )
─────────────────────────────

───────────────────────────────────────
  Note.
───────────────────────────────────────
throw 에 의해 발생되는 예외를 throws 해 줄 때, 위와 같이 세 가지 예외를 모두
선언하기 번거롭다고 생각하는 프로그래머는 모든 예외의 상위 클래스인 Exception
클래스로 선언 가능하다.
------------------------------------------------------------------------------
  public void ExceptionThrowMtd (int pos) throws Exception { ... }
───────────────────────────────────────

사용자 정의 예외 처리 방법

사용자 정의 예외를 처리하는 방법은 자바의 RuntimeException 을 처리하는 방법과
같이 try, catch, finally 블록을 이용하는 것이다.

───────────────────────────────────────
  Note.
───────────────────────────────────────
사용자 정의 예외 처리를 사용할 때 주지할 사항은 RuntimeException 은 해당되는
프로그램이 정상적으로 수행된다면 예외 처리를 사용할 필요가 없으나 사용자 정의
예외 (필자는 이것을 컴파일 시간 예외라고 정의) 는 반드시 예외 처리가 된
상태에서 해당 프로그램이나 메소드를 사용해야 한다는 것이다.
───────────────────────────────────────

다음의 프로그램은 while 블록을 이용하여 i 변수값을 증가시키면서
ExceptionThrowMtd (int pos) 메소드를 호출하고 있다. 프로그램의 내용상 예외가
발생할 수밖에 없도록 되어 있으며 i 의 값에 따라 해당되는 예외가 발생하게 된다.

그리고 System.out.println (e) 을 사용하여 예외 처리 객체의 내용을 출력하고
있는데 이때 나오는 메시지는 예외를 정의할 때 선언한 toString ( ) 메소드의
리턴값이다.

─────────────────────────────
  사용예 (Usage) : 사용자 정의 예외 처리 방법
─────────────────────────────
  UserException ue = new UserException ( );

  while (i < 4) {
    try {
        ue.ExceptionThrowMtd (i);
    } catch (MyNewException1 e1) {
        System.out.println (e1);
    } catch (MyNewException2 e2) {
        System.out.println (e2);
    } catch (MyNewException3 e3) {
        System.out.println (e3);
    } finally {
        System.out.println ("Always Printed.");
        i ++;
    }
  }
─────────────────────────────

사용자 예외 처리 종합

사용자 예외 처리에 대한 사항을 정리해 보면 다음과 같다.

   · 타입으로 사용할 예외 클래스를 선언한다.
   · 예외 상황이 되면 사용자 예외 클래스의 인스턴스를 발생 (throw) 시킨다.
   · 예외 클래스의 인스턴스를 전달하는 throws 구문을 작성한다.
   · throws 가 사용된 메소드를 호출하는 측에서는 반드시 try 블록으로
      정의하고 해당되는 타입을 처리하기 위한 catch 구문을 작성한다.

사용자 예외 처리 프로그램

사용자 예외 처리 프로그램의 전체 소스는 다음과 같다.

────────────────────────────────────
  Program. UserException.java
────────────────────────────────────
  // 사용자 정의 예외 1
  public class MyNewException1 extends Exception {
    String reason;

    public MyNewException1 (String reason) {
            this.reason = reason;
    }

    public String toString ( ) {
            return reason;
    }
  }
-------------------------------------------------------------------------
  // 사용자 정의 예외 2
  public class MyNewException2 extends Exception {
    String reason;

    public MyNewException2 (String reason) {
          this.reason = reason;
    }
   
    public String toString ( ) {
            return reason;
    }
  }
-------------------------------------------------------------------------
  // 사용자 정의 예외 3
  public class MyNewException3 extends Exception {
    String reason;

    public MyNewException3 (String reason) {
            this. reason = reason;
    }

    public String toString ( ) {
            return reason;
    }
  }
-------------------------------------------------------------------------
  import java. io. * ;

  public class UserException {
  static int i = 0;

  public static void main (String args [ ] ) {
    UserException ue = new UserException ( );

    while (i < 4) {
            try {
                    ue.ExceptionThrowMtd (i);/FONT>
            } catch (MyNewException1 e1) {
                    System.out.println (e1);
            } catch (MyNewException2 e2) {
                    System.out.println (e2);
            } catch (MyNewException3 e3) {
                    System.out.println (e3);
            } finally {
                    System.out.println ("Always Printed.");
                    i ++;
            }

            try {
                    System.in.read ( ) ;
            } catch (IOException e) { }
      } // while
    } // main ( )

    public void ExceptionThrowMtd (int pos) throws MyNewException1,
                            MyNewException2, MyNewException3 {
      switch (pos) {
        case 1:
          throw new MyNewException1 ("MyNewException1 발생");
        case 2:
          throw new MyNewException2 ("MyNewException2 발생");
        case 3:
          throw new MyNewException3 ("MyNewException3 발생");
      } // switch
    } // ExceptionThrowMtd (int pos)
  }
────────────────────────────────────

사용자 예외를 이용한 UserException 클래스를 수행한 결과는 그림 4 와 같다.

 

그림 4  사용자 정의 예외 처리 결과


7 예외 처리 활용 예제
지금까지 예외 처리에 대해 살펴본 여러 사항을 바탕으로 예외가 발생했을 때
예외의 흐름이 어떻게 되는지 예제를 통해 살펴보자.

7.1  예외 프로그래밍

──────────────────────────────────────
  Progrm. UserExceptionFlow.java
──────────────────────────────────────
     1 // java.lang 패키지는 디폴트 패키지이므로 import 하지 않아도 됨
     2 import java.lang.* ;
     3
     4 public class UserExceptionFlow {
     5 
     6 public static void main (String args [ ] ) {
     7     UserExceptionFlow uef = new UserExceptionFlow ( );
     8
     9     try {
    10             uef. ExceptionThrowMtd1 ( );
    11     } catch (MyNewException1 e1) {
    12             System.out.println (e1);
    13     } catch (Exception e2) {
    14             e2. printStackTrace ( );
    15     } finally {
    16             System.out.println ("Always Printed.");
    17     }
    18
    19 } // main ( )
    20
    21 public void ExceptionThrowMtd1 ( ) throws MyNewException1
    22 {
    23     ExceptionThrowMtd2 ( );
    24 }
    25
    26 public void ExceptionThrowMtd2 ( ) throws MyNewException1
    27 {
    28     ExceptionThrowMtd3 ( );
    29 }
    30
    31 public void ExceptionThrowMtd3 ( ) throws MyNewException1
    32 {
    33     throw new MyNewException1 ("MyNewException1 발생");
    34 }
    35 }
──────────────────────────────────────

이 프로그램을 설명하면 다음과 같다.

  · 9~17 번 줄은 main 메소드에서 ExceptionThrowMtd1 을 호출하고
     MyNewException1 에 대한 예외 처리를 하고 있다.

  · 13 번 줄의 클래스 Exception 은 try 블록에서 MyNewException1 이 아닌
     예외가 발생했을 때 예외를 처리하는 구문이 된다.

  · 14 번 줄의 printStackTrace ( ) 메소드는 Exception 클래스의 메소드로서
     예외가 발생된 경로 즉, 스택 정보를 출력하는 메소드이다.

  · 21, 26, 31 번 줄의 메소드는 MyNewException1 을 throws 구문에 의해
     넘기고 있다.

  · 23, 28 번 줄은 예외를 전달하는 함수를 호출하고 있는데 이곳에서는
     try, catch 블록이 사용되지 않고 있다.

  · 33 번 줄은 MyNewException1 을 발생시키고 있다.

이 프로그램에서 예외의 흐름을 살펴보면 MyNewException1 을 발생시키는
ExceptionThrowMtd3 을 ExceptionThrowMtd2 에서 호출하고 있고 다시
ExceptionThrowMtd2 를 ExceptionThrowMtd1 이 호출하고 있다. 마지막으로
ExceptionThrowMtd1 을 main 에서 try 블록으로 처리하여 마감하고 있다.

이와 같이 특정 예외가 발생하였을 때 직접 try, catch 를 사용하여 이를 처리 할
수도 있지만 단순히 throws 구문을 작성하여 처리 시점을 넘길 수 있다.

이 특정 예외를 처리하는 지점은 프로그램의 흐름상 처리의 책임이 있는 곳에서
행하는 것이 바람직할 것이다. 이러한 경우에 throws 와 try, catch 를 적절히
사용하는 것이 필요하다. 사용자 예외를 이용한 UserExceptionFlow 클래스를
수행한 결과는 그림 5 와 같다.

 

그림 5  UserExceptionFlow 클래스의 수행 결과


7.2 시스템 클래스에 정의된 예외 구문
시스템 클래스에 정의된 예외 구문

자바 프로그램을 작성할 때, 사용자가 작성한 클래스를 사용하기도 하고 시스템에
있는 준비된 클래스를 사용하기도 하는데 일반적으로 시스템의 클래스를 사용하는
경우가 많다.

 

그림 6  기정의 (built-in) 클래스에 정의된 예외 구문


그리고 시스템에 있는 클래스들도 면밀한 처리가 요구되는 부분에는 예외가 정의되어
있는데 이들 클래스의 메소드를 사용할 경우에는 반드시 try, catch 블록으로 설정된
상태에서 해당 클래스의 메소드를 사용하는 것이 필요하다.자바 시스템 클래스의
소스들은 자바 개발 도구와 함께 배포되는데 자바 베이스 디렉토리의 하위에는
"src" 디렉토리가 존재하며 각 서브디렉토리에는 여러 클래스들의 소스 프로그램이
들어있다. 이 중 한 클래스의 일부 내용을 살펴보면 그림 6 과 같다.

자바 도움말 파일의 이용

자바 프로그래머는 자바 프로그램을 만들 때 자바 도움말 페이지를 함께 사용하면
된다. 이렇게 자바 도움말을 사용할 때 해당 메소드의 프로토타입 (prototype) 을
살펴보면 어떠한 예외가 사용되고 또 선언되어 있는지 알 수 있다. 자바 도움말
페이지는 그림 7 과 같은 형식으로 자바의 클래스와 메소드를 보여주고 있다.

 

그림 7  자바의 도움말 페이지


─────────────────────────────
  Note.
─────────────────────────────
자바 프로그래밍 레퍼런스는 자바 개발 도구와 함께 문서로
배포된다. 도움말 문서는 두 가지가 있는데 HTML 형식의
문서와 윈도우즈 도움말 형식의 문서가 있다.
─────────────────────────────

'나 어제 배웠다 > JAVA[Tip]' 카테고리의 다른 글

StringTokenizer  (0) 2015.06.04
Object Array 방법  (0) 2010.12.07
iBatis IN 구문 구현 방법  (0) 2010.12.07
Exception in finally Test  (0) 2010.08.30
Iterator Design Pattern Sample  (0) 2010.08.03