Java

예외처리

voider 2020. 9. 10. 13:15

예외 Exception

이미지 출처 예외 클래스 다이어그램

자바에서 에러는 크게 두 갈래로 나눌 수 있다.

  • error

  • exception

예외exception는 처리할 수 있지만, 에러는 손 쓸 방법이 없다.

Exception은 RunTimeException과 그 외 Exception 두 갈래로 다시 나뉜다.
다이어그램을 자세히 보면 RuntimeException은 Unckecked, 그 외 Exception은 Checked라고 표시되어 있다.

checked - unchecked

  • check : 컴파일러가 예외 체크 (Exception클래스)

  • unchecked : 컴파일러가 예외 체크 x (RuntimeException)
    언체크드 예외인 RuntimeException은 선택적으로 처리할 수 있다. 그러니까 처리하고 싶지 않으면 하지 않아도 된다는 것이다.
    반면 체크드 예외는 반드시 예외를 처리해야 한다.
    이를테면 NullPorintException은 런타임 중에 발생하는 에러다. 굳이 예외 처리를 하지 않아도 프로그램은 문제 없이 돌아간다.
    그러나 IOException 같은 경우. 애초에 예외처리를 하지 않으면 컴파일 에러를 발생 시킨다.

    • Checked : 예외처리 필수
    • Unchecked : 예외처리 선택

예외 처리 try - catch

예외처리Exeception Handling는 프로그램의 비정상적인 종료를 사전에 막기 위해 작성한다.

 public class ExceptionTest {
    public static void main(String[] args) {
        int number = 5;
        int result = 0;

        for(int i = 0; i < 10; i ++) {
            result = number / (int)(Math.random() * 10);
            System.out.println(result);
        }
    }
}

어떤 수를 0으로 나누려고 하면 자바에서는 산술 예외인 java.lang.ArithmeticException를 발생시킨다.
따라서 위 코드는 산술 예외를 발생시킬 가능성이 매우 다분하다. 실행 중 예외가 발생하면서 프로그램이 죽을 것이다. 물론 런타임예외이므로 예외처리는 선택 사항이지만. 이 코드가 반드시 끝까지 정상적으로 작동해야만 하는 코드라고 상상해보면 어떨까? 예외처리를 해주어야 한다.

public class ExceptionTest {
    public static void main(String[] args) {
        int number = 5;
        int result = 0;

        for(int i = 0; i < 10; i ++) {
            try{
                result = number / (int)(Math.random() * 10);
                System.out.println(result);
            } catch (ArithmeticException ae) {
                System.out.println("예외는 처리되고 프로그램은 죽지 않고 끝까지 돈다");
            }
        }
    }
}

try - catch 흐름

  1. try블럭 내에서 예외 발생
  2. 발생한 예외와 일치하는 catch블럭으로 넘어간다.
  3. catch블럭에 정의된 구문대로 예외를 처리하고 try문을 빠져나가서 계속 실행된다.

멀티 catch

여러 개의 catch문을 쓰는 것도 가능하다.

try{
...
..
} catch (IOException ie) {
    ie.printStackTrace();
} catch (ClassNotFoundException) {
    ce.printStackTrace();
} catch (Exception e) {
    //그 외 모든 예외가 발생할 시
    e.printStackTrace();
}

여러 개의 catch블럭을 '|'를 구분자로 하여 한 블럭에 몰아 넣을 수도 있다.

...
catch(IOException | ClassNotFoundException e) {
    //어떤 예외인지 체크해야 함
    if(e instanceof IOException) { ...... }
    if(e instanceof ClassNotFoundException) {......}
}

내 생각엔 이럴 바엔 Exception을 선언해놓는 게 나을 것 같기도 하다.

printStackTrace() , getMessage()

  • printStackTrace()
    • 예외 발생 당시 호출스택에 있던 메서드의 정보와 예외 메세지 출력
  • getMessage()
    • 발생한 예외 클래스의 인스턴스에 저장된 메세지 출력

예외 발생 시키기

return할 때 예외를 발생시켜야 할 일이 있을 수 있다.

return throw new Exception("메세지...");

직접 Exception 객체를 만들어서 발생 시킬 수도 있다.

Exception e = new Exception("발생");
throw e;

메서드 예외 처리

Servlet클래스를 예를 들면,

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{ .......}

이처럼 메서드 선언부 옆에 예외를 선언할 수 있다.
이것은 처리가 아니라 처리를 해야 한다고 명시하는 것과 같다. 어쨌든 선언만 해놔도 컴파일은 된다. 만일 저 메서드에서 IOException 발생 시, 저 메서드를 호출한 메서드가 해당 예외를 처리해야 한다.

 
doGet()
doHandle()
method1()
호출스택

이게 현재 호출 스택이라고 생각해보자. doGet()에서 예외가 발생하면 doHandle()로 예외가 전파된다.
doHandle()에서 예외를 처리하지 않으면 다시 method1()로 전파된다. 거기서도 예외를 처리하지 않으면 프로그램은 종료된다.

finally

try{
    //예외 처리가 필요한 코드
} catch(Exception e) {
    //예외 처리
} finally {
    //예외 발생 여부와 관계없이 반드시 수행해야 하는 코드
}

'Java' 카테고리의 다른 글

자바 람다식(Java Lamda Expression)  (0) 2020.10.29
Hassing & HashFunction  (0) 2020.09.10
Stream  (0) 2020.09.10
람다Lambda  (0) 2020.09.09
제네릭 메서드Generic Method  (0) 2020.09.09