자바공부<3> - 예외처리

Posted by lib oimb
2018. 7. 3. 16:15 JAVA


1. 에러


프로그램이 실행중 어떤 원인에 의해서 오작동을 하거나 비정상적으로 종료되는 경우가 있다. 이러한 결과를 초래하는 원인을 프로그램 에러 또는 오류라고 한다.


이는 또 발생시점에 따라 컴파일 에러 , 런타임 에러 로 나뉘고  추가적으로 논리적 에러가 있다


컴파일 에러는 컴파일 할 때 발생하는 에러 이고 프로그램의 실행도중에 발생하는 에러를 런타임 에러 라고 한다.

논리적 에러는 컴파일도 잘되고 실행도 잘되지만 이 프로그램의 의도와 다르게 동작하는 것을 말한다.


컴파일을 에러 없이 성공적으로 마쳤다고 해서 프로그램 실행 시에도 에러가 발생하지 않는 것은 아니다. 실행시 발생할 수 있는 에러를 컴파일이 전부 감지를 할 수 있는것은 아니기 때문인데 이 때문에 모든 경우를 대비하는 것이 필요하다.


자바는 실행시 발상 할 수 있는 프로그램 오류에러와 예외 두가지로 구분 하였다.

에러는 메모리 부족, 스택오버플로우와 같이 일단 발생하면 복구할 수 없는 심각한 오류 이고 예외는 발생하더라도 수습 할수 있는 비교적 덜심각한것 이다.

에러가 발생하면 프로그램의 비정상 프로그램 종료를 막을 수 없지만 예외는 적절한 코드를 미리 작성함으로서 대비할 수 있다.








위 그림을 보면 2가지로 나뉘는 것을 볼수 있는데 1. Exception 클래스와 자손들  2. RuntimeException 클래스와 그 자손들이다.

1은 보통 사용자의 실수 , 외적 요인에 의해 발생한다.  - FileNotFoundException , ClassNotFoundException, DataFormatException 

2는 프로그래머의 실수로 발상한다. - 배열의 범위 벗어나거나  형변 환 등등

참고로 이 둘을을 checked예외 unchecked 예외로 나뉘게되는데

checked예외는 반드시 예외처리를 해줘야 되며 Exception을 상속 받은 예외 클래스에 해당 된다. 

그리고 uncheched 예외로는 RuntimeException 예외를 상속받는 예외 클래스에 해당되며 예외처리 여부를 선택할 수 있는 장점이 있다.


이 예외를 처리하기 위한 문장이 있는데  바로 try catch 문이다



2. try catch


try{


// 일반적인 라인 수행


}

catch(Exception1 e){  // 참조변수는 하나만 선언한다.


// 해당 에러 발생시 라인 수행


}



이러한 형태로 되어있으며 try 실행 도중 예외 발생시 catch 문으로 넘어가고 예외 인스턴스가 catch에 정의된 객체와 일치하거나 자손일 경우(instanceof) catch문을 실행 후에 빠져나가게 된다. 만약 알맞는 catch문이 존재 하지 않다면 예외는 처리되지 못하고 프로그럼은 비정상 종료하게 되어진다.

에러가 발생시에 catch 문에서 에러에 대한 정보를 얻기 위한 방법이 있는데


  • printStackTrace() 예외 발생시  당시의 호출 스택에 이었던 메서드의 정보와 예외 메시지를 화면에 출력 (스택참고 : http://history1994.tistory.com/1?category=668240 )
  • getMessage()     발생한 에외 클래스의 인스턴스에 저장된 메시지를 얻을 수 있다.



다음으로 매서드를 통한 예외처리를 알아보자.



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

// 이 메서드에 대한 내용

// 에러 발생시 에러를 날린다!

}



와 같이 정의한다.

이는 해당 예외가 발생시 이 매서드를 호출한 곳으로 예외를 보낸후에 이 매서드는 종료된다 라는것을 의미한다. 또 이 매서드는 해당 예외가 발생할 수 있다는 정보를 알려주는 의미이기도 하다. 이 메서드를 호출한 메서드는 반드시 try catch 문을 작성하여야 한다.



2.1 try catch 사용시 주의사항 


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import java.io.IOException;
public class test2 {
    
    public static void main(String[] args) {
        
        try {
            
            method2();
            method();
        }
    /*    예외의 순서는 하위 계층에서 상위계층 순으로 내려가야된다. 
     * 만약 상위(부모)클래스가 하위(자식)클래스보다위에 있다면 에러
        catch (Exception e) {
            
        }
        */
        catch (NullPointerException e) {
            System.out.println("null");
        }
        
        catch (IOException e) { // 이곳의 결과가 나온다.
            System.out.println("IOE");
        }
        
        catch (Exception e) {    // 
            System.out.println("EX");
        }
    
        
    }
        
    static void  method ()throws Exception { // 에러를 throw 할 때  매서드의 throws의 예외보다 같거나 하위클래스여야 한다.
        
        throw new IOException();
        
    }
    static void method2() { // 런타임 익셉션은 throws를 안해줘도 알아서 throws가 된다. => 즉 try catch를 작성해주지 않아도됨
        
        throw new NullPointerException();
    }
    
    
}
 
cs



위의 코드의 주석을 잘 보자 

저 규칙을 지키지 않으면 컴파일 에러가 발생한다.






3. finally


try{

//  정상 수행 라인

// 예외 발생가능

}

catch(Exception1 e){

//해당 예외에 대한 처리를 위한 라인

}

finally{

//예외의 발생여부에 상관 없이 항상 수행되어야 하는 문장들을 넣는다.

}


보시다시피 finally 블록은 예외가 발생 하든 하지 않든 상관없이 실행 되어야하는 코드를 삽입한다.





4. 자동 자원 반환 


 어떠한 자원에 있어서 사용한 후에는 반드시 꼭 닫아줘야(자원을 반납)하는 것들이 있다. 대표적으로 입출력 관련된 함수이다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public class test {
 
    private static File f;
 
    private static FileInputStream fis;
 
    public static void main(String[] args) {                
 
        try {
 
            f = new File("history.txt");
 
            f.createNewFile();
 
            fis = new FileInputStream(f);
 
        } catch (IOException e) {            
 
            e.printStackTrace();
 
        }finally {
 
            try {
 
                if(fis!=null)
 
                    fis.close();
 
            } catch (IOException e2) {
 
                e2.printStackTrace();
 
            }            
 
        }    
 
    }
 
}
cs

이러한 형태를 말하는것 인데  딱 보기에 어떠한가?  되게 어려워보이지 않은가?? 
그런데 이보다 더 좋지 않은 점이 존재한다. 만약 첫 번째 try문에서 예외가 발생하고  그리고 finally 내부에 있는 try문에서 또 예외가 발생하면 첫 번째 try문의 예외는 무시되어진다.
이를 개선하기 위해 생긴것이 자동 자원 반환이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class test implements AutoCloseable {
    private static File f;
 
    public test() { 
        f = new File("history.txt"); 
        try { 
            f.createNewFile(); 
        } catch (IOException e) { 
            e.printStackTrace(); 
        }
 
    } 
 
    public static void main(String[] args) {
 
        try (FileInputStream fis = new FileInputStream(f)) {
             // 내용 
        } catch (Exception e) { 
            // 내용 
        } 
    } 
    @Override 
    public void close() throws Exception {
        // 내용
    }  
}
 
cs

생성자로 파일을 생성 한뒤 나오는  main함수를 보면 코드가 한결 단순해진 것을 볼 수 있다. try문의 () 안에다 객체를 넣으면 이 객체는 따로 close를 호출 하지 않아도 

tyr문의 블럭을 벗어날때 자동적으로 close가 호출 되어진다.  다만 이를 위해 AutoCloseable 상속과 close() 구현이 필요하다.





5. 사용자 정의 예외 만들기


필요에 따라 프로그래머가 새로운 예외클래스를 정의 할 필요가 있다  .Exception  계열 클래스로부터 상속받는 클래스를 작성 하면 된다.


1
2
3
4
5
6
7
8
9
10
11
12
13
class IntegerException extends Exception { 
private final int ERR_CODE; // 
    public IntegerException(String msg, int err_code) {
        super(msg);
        ERR_CODE = err_code;
    }
    public IntegerException(String msg) {
        this(msg, 123);  //초기값 123
    }
    public int getERR_CODE() {
        return ERR_CODE;
    }
}
cs


//이후에 필요에 따라 catch문으로 호출하면 된다.


  if (!IsNumber.isNumber(input_number)) {  //  예외 : 숫자가 아닌경우

          throw new IntegerException("정수가 아닌 문자입력");




이외에도 많은 종류가 있지만 

오늘은 여기까지 정리 !




이 댓글을 비밀 댓글로
  1. 정말 섬세하게 정리하셨네요^^
    많은 도움이 되었습니다~