티스토리 뷰

JAVA/정리

Exception 예외처리

란텔 2013. 12. 30. 23:43

예외처리는 프로그램 실행도중 예외가 발생 하였을 때 프로그램의 비정상적 종료를 막기 위해 적절한 처리를 해주는 것입니다.

 

에러는 컴파일에러와 런타임에러로 나뉩니다.

 

컴파일 에러는 말 그대로 프로그램의 컴파일시 나는 에러이고

런타임 에러는 프로그램의 실행도중에 발생하는 에러입니다.


모든 예외 클래스들의 조상은 Exception클래스입니다.

Exception클래스의 조상은 Throwable이구요

Throwable의 조상은 최고 조상인 Object입니다.


Exception클래스들의 자손은 checked예외와 unchecked예외가 있습니다.

Exception클래스의 RuntimeException과 그 자손들만이 unchecked예외이고

그 외 나머지 Exception클래스의 자손은 checked예외입니다.


자바 api에서 참고하여 상속 관계를 도식화한 그림입니다.



두 차이점은 unchecked예외의 경우 컴파일시 컴파일러가 예외처리를 하도록 강제하지 않는 것이고,

 checked예외는 예외처리를 하지 않으면 컴파일시 오류가 발생합니다.

 


 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class ExceptionEx1 {
    public static void main(String args[]){
        
        String s = null;
        
        try{
            System.out.println(1);
            System.out.println(2);
            System.out.println(s.codePointAt(1));
            System.out.println(3);
        }catch(NullPointerException e){
        System.out.println(4);
        }
        System.out.println(5);
        
    }
}

 

예외처리는 try catch문을 사용합니다.

try영역 안에는 예외가 발생할 수 있는 코드들이 들어가고

catch영역에는 매개변수에는 예외가 발생할 익셉션타입의 클래스가 위치하고, 영역 안에서는 예외발생시 처리해줄 코드를 집어 넣습니다.

 

 

위의 코드는 try catch문의 예입니다. 실행해 보면 콘솔에 1 2 4 5가 뜨게 됩니다...

try영역 안에서 예외가 발생하여 3은 뜨지 않습니다.

여기서 알수 있는 것은 예외가 발생하면 해당 try영역에서는 더이상 처리를 진행하지 않고 catch문의 예외처리를 한 후, try catch문을 벗어나서 그 다음 구문을 실행시킨다는 것을 알 수 있습니다.




예외 맡기기

메서드 단에서 다음과 같은 형식으로 예외를 정의하면 이 메서드를 호출하는 곳에서는 이 예외에 대한 처리를 하도록 강제합니다.

public void method() throws Exception{
     .............
}

method()라는 메서드에 throws Exception을 정의 하였습니다.

이 뜻은 '이 메서드에서는 Exception예외가 발생할 수 있으니 이 메서드를 호출하는 곳에서는 반드시 예외처리를 해주어라' 그리고 '이 메서드에서는 Exception이 발생할 수 있지만 예외처리는 이 메서드를 호출하는 녀석에게 위임한다' 입니다.

그러므로 다음과 같이 method()를 호출하는 곳에서는 예외 처리를 해주어야 합니다. 해주지 않으면 컴파일시 에러가 발생합니다.

public void methodCall(){
   try{ 
     method();
   }catch(Exception e){
     e.printStackTace();
   }
}

method()를 호출하였기 때문에 try catch예외처리를 하였습니다. 만약 하지 않으면 컴파일시 에러가 발생합니다.

그리고 만약 methodCall()에서도 예외처리를 위임하고 싶다면 똑같이 메서드 선언부에 throws Exception을 붙이면 됩니다.

이런식으로 계속 예외를 위임하게 된다면 결국 최종에는 main메서드에서 예외처리를 해주어야 합니다.

마지막으로 예외를 맡기는 것은 예외가 처리된 것이 아닙니다. 결국 어느 한 곳에서는 예외처리를 반드시 해주어야 합니다.




finally 블럭

finally블럭은 try{}catch(){}에서 예외 발생 및 처리 여부와 상관없이 항상 실행되어야 할 코드를 포함시키기 위해 사용합니다.

try{
            throw new ClassCastException("고의로 예외 발생11111!!! caution!!!");
        }catch (ClassCastException e) {
            System.out.println(e.getMessage());
        }finally{
            System.out.println("예외가 발생되지 않아도 실행");
        }
        
        try{
            throw new ClassCastException("고의로 예외 발생22222!!! caution!!!");
        }catch (ArithmeticException e) {
            System.out.println(e.getMessage());
        }finally{
            System.out.println("예외가 발생되지 않아도 실행");
        }

위에서 정의한 두 try-catch-finally문장에서 finally블럭은 모두 실행 됩니다. finally는 예외 구문에서 적합한 catch문을 찾든지 찾지 못하든지 항상 실행됩니다.


 

JDK 1.7부터 try with resource문 추가

기존 예외처리를 할 때 입출력 같은 인스턴스를 생성해서 자원을 사용하고 반환해야 하는 코딩을 할 경우에 보통 다음과 같이 코딩하였을 것이다.

String path = "C:\\Users\\choons\\Desktop\\"; FileInputStream fis1 = null; try{ fis1 = new FileInputStream(path+"HelloWorld.txt"); }catch(IOException e){ e.printStackTrace(); }finally{ try { if(fis1 != null) fis1.close(); } catch (IOException e) { e.printStackTrace(); } }

이처럼 하면 안 좋은 점이 finally부분의 코드가 조금 지저분 하다는 점이다. 이의 개선을 위해 try with resource문이 추가 되었으며 코드는 다음과 같다.

       String path = "C:\\Users\\choonie\\Desktop\\";
        try(FileInputStream fis = new FileInputStream(path+"HelloWorld.txt");
                BufferedInputStream bfis = new BufferedInputStream(fis);){
            
            int data = 0;
            
            while((data = bfis.read()) != -1){
                System.out.print((char)data);
            }
            
            
        }catch(IOException | RuntimeException e){
            
            e.printStackTrace();
        
        }catch(Exception e){
        
            e.printStackTrace();
            
        }

try키워드 다음에 괄호()를 두고 자원을 해제 해야하는 객체의 인스턴스 생성을 이곳에 정의하면 try블럭을 벗어나는 순간 자동적으로 close()메서드가 수행된다. 그 다음으로 catch블럭이나 finally블럭이 수행된다.


이와 같이 try resource구문을 사용할 때 close()가 자동적으로 호출될 수 있으려면, 해당 클래스가 AutoCloseable인터페이스를 구현하고 있어야 한다.


다음은 이 인터페이스를 구현하는 클래스들의 목록을 볼 수 있는 api 주소이다.

https://docs.oracle.com/javase/7/docs/api/java/lang/AutoCloseable.html


보면 알겠지만 예제 코드에 있는 FileInputStream은 이미 AutoCloseable인터페이스가 구현된더 있기 때문에 따로 처리 해주지 않아도 try resource구문을 사용하면 바로 적용이 된다.


그럼 또 간단하게 AutoCloseable 구현 클래스를 만들어본다.

class ResourceAuto implements AutoCloseable{
    
    
    @Override
    public void close() throws Exception {
        System.out.println("자원이 닫히는 close()호출했음");
    }
    
}


public class ExceptionEx2 {
    public static void main(String[] args){
    
        
        try(ResourceAuto ra = new ResourceAuto();){
            System.out.println("try블럭 수행 끝남");
            throw new Exception("예외발생!");
        }catch(Exception e){
            e.printStackTrace();
            System.out.println("catch 블럭 수행!");
        }finally{
            System.out.println("finally 블럭 수행!");
        }
        
        System.out.println("예외처리 구문 벗어남");
        
        
    }
}

위 코드는 AutoCloseable인터페이스를 테스트 해봄과 동시에 close()가 호출되는 시점을 알기 위해 코딩 해본 것이다.


대략 순서는 다음과 같다.

1. try 블럭 구문 수행

2. close() 호출

3. catch 블럭 수행

4. finally 블록수행



'JAVA > 정리' 카테고리의 다른 글

java enum 열거형  (0) 2014.01.22
HashSet, TreeSet  (0) 2014.01.21
[Collection Framework] ArrayList, LinkedList  (0) 2014.01.16
2진수, 10진수, 8진수, 16진수 진수 변환  (0) 2013.12.17
증감 연산자, 부호 연산자, 비트 연산자  (0) 2013.12.10
Comments
최근에 올라온 글
최근에 달린 댓글
TAG
more
Total
Today
Yesterday