Java 예외 처리(Exception)
예외 처리(Exception)
예외(Exception)란 프로그램 실행 중에 발생되는 비정상적인 상황을 말한다.
프로그램밍에서 에러(Error)는 2가지가 있다. 컴파일(Complie) 시 발생하는 에러와 런타임(Runtime) 시에 발생하는 에러이다. 컴파일 시에 발생하는 에러는 문법적인 오류이거나 문맥에 맞지 않은 코드를 작성하여 발생하는 에러이기 때문에 프로그램 자체가 실행조차 하지 못한 상태이다. 그에 비해 런타임시 발생하는 에러는 프로그램이 실행하다가 예기치 않는 상황에서 발생하는 에러이기에 오작동하거나 프로그램이 실행 종료되는 현상이 발생하게 된다.
예를 들면, 객체를 선언만 하고 생성하기 전에 참조를 한 경우, 0으로 정수를 나눴을 경우, 파일을 접근을 하려고 하는데 실제 파일이 없는 경우, 배열의 크기에 벗어난 인덱스을 접근한 경우 등 많은 예외 상황이 존재한다.
자바는 이런 런타임 시에 발생하는 에러을 예외(Exception)이라고 부르고, 예외 상황을 처리할수 있게 해준다.
예외 관련 클래스
- java.lang.Object
- java.lang.Throwable
- java.lang.Error
- java.lang.Exception
- java.lang.RuntimeException
- Unchecked Exception
- Other Exception
- Checked Exception
- java.lang.Throwable
예외(Exception)도 결국은 클래스이다. 그러기에 모든 예외(Exception) 클래스는 Throwable 클래스를 상속받고 있으며, Throwable은 최상위 클래스 Object의 자식 클래스이다.
Throwable 상속받는 클래스는 Error와 Exception이 있다. Error는 주로 하드웨어 관련 예외 처리하는 클래스로써 시스템 레벨의 심각한 수준의 에러이다. 예를 들면, 메모리 문제(OutofMemoryError), 스택 오버플로우, 스레드가 인터럽트가 발생한 경우 등의 에러가 있다. 이런 종류의 예외는 코드에에서 처리하기 보다는 시스템에 변화를 주어 문제를 처리해야 하는 경우가 일반적이다.
Exception는 크게 RuntimeException을 상속 받은 예외와 상속을 받지 않은 예외로 나눌 수 있다. RuntimeException을 기준으로 나누는 이유는 예외 지정을하는가 안하는 하는가에 따른다. RuntimeException과 관련된 예외들은 따로 지정을 하지 않아도 크게 문제가 되지 않는데, 예외 지정에 소요되는 노력이 예외를 지정하여 얻는 효율보다 크기 때문이다. 이런 예외 처리는 JVM에게 맡기는 것이 더 효율적이다.
RuntimeException과 관련된 예외들도 Error와 같이 일반적으로 따로 지정하지 않는 경우가 많다. 일일히 소요되는 노력이 예외를 지정하여 얻는 효율보다 더 크기 때문이다.
Checked Exception, Unchecked Exception
- Checked Exception
- 반드시 예외 처리 해야 한다.
- 트랜잭션 Rollback이 되지 않는다.
- RuntimeException을 상속하지 않는다. (X)
- IOException, SQLExcepiton
- Unchecked Exception
- 예외 처리를 하지 않아도 된다.
- 트랜잭션 Rollback이 진행된다.
- RuntimeException을 상속한다. (O)
- NullPointerException, IllegalArgumentException
기본적인 예외처리 - try~catch~finally
예외 처리를 메소드 내에서 직접 처리하기 위해서는 try, catch, finally 키워드를 이용한다.
try {
// try 블럭
} catch (예외타입1 매개변수1) {
// try 예외 처리 블럭1
} catch (예외타입2 매개변수2) {
// try 예외 처리 블럭2
}
...
} catch (예외타입N 매개변수N) {
// try 예외 처리 블럭N
} finally {
// finally 블럭
}
try 블럭은 예외가 발생할 가능성이 있는 범위를 지정하는 블럭이다. 프로그램 실행 중에 예외가 발생할 수 있는 범위를 try 블럭으로 지정하면 되다. try 블록은 최소한 한개 이상의 catch 블럭이 있어야 한다.
catch 블럭은 try블럭에서 예외가 발생할 시에 실행이 되는 블럭이다. catch 블럭의 예외타입의 매개변수는 try 블럭에서 발생한 예외에 관한 정보를 담고 있다. try 블럭에서는 다양한 예외가 발생할 수 있으며, 이러한 예외의 종류에 따라 여러 개의 catch 블럭을 지정될 수 있다.
finally 블럭은 선택사항으로 예외가 발생과 상관없이 무조건 실행이 되는 블럭이다. 즉, try 블럭이 실행되면서 예외가 발생을 하던 예외가 하지 않던가에 무조건 실행이 되는 블럭이다.
보통 객체를 생성하고 close를 꼭 해야 하는 경우에 많이 사용이 된다. 예를 들면 파일을 열고 닫거나 데이터베이스를 열고 꼭 닫아야 하는 경우 등이 있다.
package com.devkuma.tutorial.exception;
public class TryCatch {
public static void main(String[] args) {
try {
int i = Integer.parseInt(args[0]);
System.out.println("i=" + i);
} catch (NumberFormatException e) {
System.out.println("NumberFormatException");
throw e;
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("ArrayIndexOutOfBoundsException");
throw e;
}
}
}
향상된 try~catch 에서 다중 예외
JDK1.7의 향상된 기능으로 try~catch 에서 다중 예외를 받아 처리할 수 있게 되었다. 서로 다른 상속 계층구조에 있는 예외끼리 가능하다. 예를 들어 NumberFormatException과 ArrayIndexOutOfBoundsException은 동시 처리불가.
JDK1.7 이전에는 아래와 같은 방법으로 예외를 처리했다면
...중략...
} catch (NumberFormatException e) {
System.out.println("NumberFormatException");
throw e;
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("ArrayIndexOutOfBoundsException");
throw e;
}
JDK1.7 7 이후에는 아래와 같은 방법이 가능하게 되었다.
...중략...
} catch (NumberFormatException | ArrayIndexOutOfBoundsException e) {
System.out.println("Exception");
throw e;
}
예외처리 넘겨주기 - throws
예외를 처리 해주는 방식은 try~catch 절로 메소드 내에서 처리하는 방식과 발생된 예외를 호출한 메소드에게 넘겨주는 방식이 있다.
이런 예외처리 방식은 한 메소드에서 모두 처리할 때 유용하며 throws 키워드 이용하여 예외를 지정한다. 예외 처리가 많을 시에는 콤마(,)로 구분하여 나열한다.
public void method() throws 예외클래스[, 예외클래스...]
인위적인 예외 발생 - throw
JVM은 자바 프로그램 수행 중에 예외가 발생하면 자동으로 해당되는 예외 객체를 발생시킨 다음 예외를 처리하기 위해 catch 루틴을 찾는다.
예외 발생은 JVM에 의해 실샣중 발생할 수도 있지만, 사용자 프로그램에서 인위적으로 예외를 발생시킬 수 있다. 자바는 예외를 인위적으로 발생 시키기 위해 throw 키워드를 사용한다.
throw 예외객체;
또는
throw new 예외클래스(매개변수);
사용자 정의 예외 클래스
사용자는 자바에서 제공하는 예외 클래스 외에 추가적으로 사용자가 직접 클래스를 만들 수도 있다. 사용자 정의 예외 클래스를 만들려면 Exception 클래스를 상속 받아서 새로운 예외 클래스를 만들면 된다.