Exception (에러처리)

try, catch, finally, throw, throws

 

 

프로그램을 만들다가 예기치 않게 에러가 발생할수 있습니다. 아니면, 어떤에러가 발생할수도 있다고 예상할 수도 있습니다. 그것도 아니라면, 일부러 에러를 발생시킬 수도 있습니다.

 

이번 Exception 에러처리 부분을 공부하면서, 느낀점은 자바는 C언어보다 더 안전하게 프로그래밍을 할수 있도록 되있는 것 같습니다. 어떤 중요한 작업을 해야하는 부분에서는 Exception 에러처리를 의무적으로 사용을 해야지만 동작을 하는 곳까지 있습니다. 입출력(네트웍 입출력, 데이터베이스 입출력, 파일 입출력, 메모리 입출력 등)을 구현코드에 집어넣으려고할때 꼭 try, catch를 써야합니다. 안쓰면, 모든걸 제대로 작성했다고해도, 단순히, try, catch문을 쓰지 않았다고 에러가 납니다. ^_^

 

 

런타임 에러 (Runtime Error)

에러 처리 하는데, 컴파일 도중 나오는 컴파일 에러(Comile Error), 실행 도중 나오는 런타임 에러(Runtime Error) 두 종류가 있습니다. 여기서는 쫌 귀찬게 에러를 잡아줘야 하는 런타임 에러 방지 방법을 알아볼 것입니다. 런타입 에러의 경우, 중요한건! 실행 중에 에러가 발생했는데, catch로 예외처리를 하지 않았다면 실행도중 중지될 것입니다.

 

이번 Exception 장에서 필수 키워드는 try, catch, finally, throw, throws 이렇게 5가지 입니다.

 

 

try, catch, finally

먼저, try, catch, finally 먼저 보겠습니다.

try는 에러가 발생할 여지가 있는 코드를 작성하면 됩니다. catch try와 한쌍이 되서 사용되어야 하는데, try { .. } 괄호 안에서 에러가 발생하면 해당 에러를 catch 문으로 잡아서 처리할 수가 있습니다. catch 문은 여러 개로 사용될 수 있습니다. finally는 사용해도 되고 안해도 되는데, 마지막에 무조건 처리할 코드가 있을 떄 사용됩니다.

if

try, catch

if(조건){

} else if(
조건){

} else if(
조건){

} else{

}

try {
    //
이 영역의 문장을 실행하다가

// 에러발생하면, catch로 이동
} catch(ErrorA e){
   // try
영역에서 ErrorA라는 에러가

// 발생하면 여기로 이동
} catch(ErrorB e){
   // try
영역에서 ErrorB라는 에러가

// 발생하면 여기로 이동
} catch(Exception e){
   //
나머지 에러는 여기서 다 처리
} finally{
  //
에러가 걸리든 안걸리든 무조건

// 여기는 지나감
}

이해가 쫌 더 빨리 되게 위해 if문이랑 비교해봤습니다생긴게 좀 비슷하죠?^_^ 프로그램 실행 영역을 try로 감싸주기만 하고, 거기서 발생하는 에러를 catch로 잡아서 처리만 해주면 됩니다.

 

아래 몇 가지 런타입 에러의 상황을 예시로 사용법을 알아보겠습니다. 빨간색은 일부러 에러를 발생시킨 부분입니다.

 

예시 1)

NullPointerException

참조할 메모리가 없다는 오류

설명:

주황색 코드(try, catch)가 없다면, 실행 중에 에러가 발생합니다.

public class TryCatchMain{ 

public static void main(String[] args){

try{ 

String str = null

System.out.println(str.length()); 

} catch(NullPointerException e){ 

System.out.println(e.toString() + " 에러 발생"); 

System.out.println("에러처리 루틴 실행"); 

System.out.println("프로그램 종료"); 

} 

실행결과:

java.lang.NullPointerException 에러발생

에러처리 루틴 실행 

프로그램 종료

 

예시 2)

ArrayIndexOutOfBoundsException

배열 범위가 넘어갔다는 오류

설명:

주황색 코드(try, catch)가 없다면 실행 중에 에러가 발생하고, 파란색 코드(finally) catch 구문으로 들어오더라도 마지막에 처리하고 싶은 코드가 있다면 여기에 넣어서 사용하면 유용합니다.

public class BasicException { 

public static void main(String args[]) { 

try{ 

int[] ar = new int[]{0, 100, 200, 300}; 

for(int i=0; i<ar.length+1; i++){

System.out.println("ar["+i+"]=" + ar[i]); 

} catch(ArrayIndexOutOfBoundsException e) {

System.out.println(e.getMessage()); 

System.out.println(e.toString());

e.printStackTrace();

return;

} finally{

System.out.println("try, catch상관없이 여기로!");


}

실행결과:

ar[0]=0 
ar[1]=100 
ar[2]=200 
ar[3]=300 

java.lang.ArrayIndexOutOfBoundsException: 4 
java.lang.ArrayIndexOutOfBoundsException: 4 
at BasicException.main(BasicException.java:10) 
try, catch
상관없이 여기로!");

 

예시 3)

FileNotFoundException & ArrayIndexOutOfBoundsException

파일을 찾을 수 없다는 오류 & 배열 범위가 넘어갔다는 오류

설명:

주황색 코드(try, catch)가 없다면 실행 중에 에러가 발생하고. 초록색(catch(Exception e)) 코드 try 에서 발생하는 모든 예외(Exception)에 대해 catch가 받지 못한 에러들은 여기서 전부 받아서 처리한다. 혹시라도 직접 작성한 catch문으로는 부족하다고 생각하면 마지막에 Exception catch로 잡는 아래처럼 작성하기도 한다. 이게 가능한 이유는, Java에서 발생하는 모든 에러들은 Exception 클래스를 상속받았기 때문이다. (=업캐스팅, Upcastring)

import java.io.*;

public class LevelCatchMain{

public static void main(String[] args){

try{

FileReader f = new FileReader("Hi.java");

String s = null;

System.out.println(s.toString());

} catch(FileNotFoundException e1){

System.out.println("FileNotFoundException:" + e1);

} catch(ArrayIndexOutOfBoundsException e2){

System.out.println(e2);

} catch(Exception e3){

System.out.println(e3);

}

}

}

실행화면:

java.lang.NullPointerException

 

※ 참고

e.getMessage() : 에러 이벤트와 함께 들어오는 메시지를 출력한다.

e.toString() : 에러 이벤트의 toString()을 호출해서 간단한 에러 메시지를 확인한다.

e.printStackTrace() : 에러 메시지의 발생 근원지를 찾아서 단계별 에러를 출력한다.

 

 

throw, throws

이번에는 throw, throws를 보겠습니다.

throw는 에러를 일부러 발생시키는 것입니다. throws는 에러를 바로 처리하기 귀찬을 때나중에 처리할수 있도록 미룰 때 사용하는 것입니다. 이번에도 몇가지 예시를 살펴보겠습니다.

 

예시 1)

throw

직접 오류를 발생

설명:

빨간색으로 표시한 부분을 보면 일부러 오류를 발생시켰습니다. 여기서는 throw 사용방법을 보기 위해서 ~ throw 사용해서 오류를 발생시킨 것입니다. 직접 발생시킨 오류도 앞에서 설명한 것처럼 catch문으로 잡을 있습니다.

public class UseThrowMain { 
   public static void main(String args[]) { 
      try { 
         
throw new Exception("일부러 에러 발생"); 
      } catch(Exception e) { 
         System.out.println("
정보:" + e.getMessage()); 
         System.out.println("
정보:" + e.toString()); 
         e.printStackTrace(); 
         return; 
      } finally{ 
         System.out.println("
마지막은 무조건 여기"); 
      } 
   }
}

실행화면:

정보:일부러 에러 발생
정보:java.lang.Exception: 일부러 에러 발생
java.lang.Exception:
일부러 에러 발생 
at UseThrowMain.main(UseThrowMain.java:7) 
마지막은 무조건 여기

 

예시 2)

throws

에러를 호출하는 쪽으로 넘겨버림

설명:

makeURL 함수에서 에러가 발생했지만, makeURL 함수 안에서 try, catch문으로 처리하지 않고 호출하는 상위 쪽으로 넘겨버렸습니다. 그래서, 만약에 makeURL 내에서 에러가 발생하면 makeURL을 호출하고 있는 main함수 내에서 에러를 처리해주어야 합니다. 아래 예시는 일부러 makeURL 안에서 URL 포멧이 틀렸을 때 발생하는 오류인 MalformedURLException가 발생하도록 URL을 이상하게 적어서 실행했습니다.

import java.net.*; 
   public class ShiftCatch { 
     
 public URL makeURL(String urlstr) throws MalformedURLException {
         return new URL(urlstr); 
      }
 
      public static void main(String args[]) { 
         ShiftCatch p = new ShiftCatch(); 
         try{ 
            //
정확한 URL을 입력하지 않았기 때문에 에러발생 
            URL url = p.makeURL("htttttp://www.ya.co.kr"); 
         } catch(MalformedURLException e) { 
            e.printStackTrace(); 
         }finally{ 
            System.out.println("finally:
결국이리로 오는군요"); 
         } 
   } 
}

실행결과:

java.net.MalformedURLException: unknown protocol: htttttp 
at java.net.URL.<init>(URL.java:586) 
at java.net.URL.<init>(URL.java:476) 
at java.net.URL.<init>(URL.java:425) 
at ShiftCatch.makeURL(ShiftCatch.java:7) 
at ShiftCatch.main(ShiftCatch.java:13) 
finally:
결국이리로 오는군요

 

이렇게, try, catch, finally, throw, throws에 대해 알아봤습니다. 작은 클래스 한두개를 작성할 때는 이 키워드를 잘 사용하지 않고, 사용하더라도 대충 사용하는 경우가 많습니다. 하지만, 코드가 점점 커지고 하나의 프로젝트 규모가 될 정도로 커지면 예외처리는 엄청나게 중요해집니다. 개발자의 생산성과 속도로 직결되는 부분 중 하나입니다. 따라서, 처음부터 조금씩 조금씩 잘 사용하도록 습관을 들이는게 중요하다고 생각합니다.^_^