[Java] 예외 처리 01
Java예외 처리 Exception Handling
프로그램 실행 시 발생할 수 있는 예기치 못한 예외의 발생에 대비한 코드를 작성하는 것.
프로그램의 비정상 종료를 막고, 정상적인 실행 상태를 유지할 수 있도록 한다.
처리되지 못한 예외(Uncaught Exception)는 JVM의 예외처리기(UncaughtExceptionHandler)가 받아서 예외의 원인을 화면에 출력한다.
프로그램 오류
- 컴파일러 에러 Compile-time Error
컴파일 시에 발생하는 에러
컴파일러가 소스코드(*.java)에 대해 오타나 잘못된 구문, 자료형 체크 등의 기본적인 검사를 수행하여 오류를 알려준다.
컴파일을 성공적으로 마치고 나면, 클래스 파일(*.class)이 생성되고, 생성된 클래스 파일을 실행할 수 있게 된다.
- 런타임 에러 Runtime Error
실행 시에 발생하는 에러
실행 중에 에러에 의해서 잘못된 결과를 얻거나 프로그램이 비정상적으로 종료되는 것.
에러 Error : 메모리 부족(OutOfMemoryError), 스택오버플로우(StackOverflowError) 등 발생하면 복구할 수 없는 심각한 오류.
예외 Exception : 프로그램 코드에 의해서 수습될 수 있는 다소 미약한 오류.
예외 클래스의 계층구조
실행 시 발생할 수 있는 오류(Error, Exception)를 클래스로 정의.
모든 예외의 최고 조상은 Exception 클래스.
Exception | RuntimeException |
---|---|
IOException | ArithmeticException |
DataFormatException | ClassCastException |
FileNotFoundException | NullPointerException |
… | … |
* RuntimeException | IndexOutOfBoundException |
- Exception 클래스들
외부의 영향으로 발생하는 예외(프로그램 사용자의 실수 등)
Exception 클래스와 그 자손 클래스들(RuntimeException 클래스들 제외)
- 존재하지 않는 파일의 이름을 입력하거나(FileNotFoundException)
- 실수로 클래스의 이름을 잘못 적었거나(ClassNotFoundException)
- 입력한 데이터 형식이 잘못된(DataFormatException) 경우 등
- RuntimeException 클래스들
프로그래머의 실수로 발생하는 예외
RuntimeException 클래스와 그 자손 클래스들
- 배열의 범위를 벗어나거나(IndexOutOfBoundException)
- 값이 null인 참조변수의 멤버를 호출하려 했거나(NullPointerException)
- 클래스간의 형변환을 잘못하거나(ClassCastException)
- 정수를 0으로 나누는(ArithmeticException) 등
예외 처리하기 try-catch문
발생한 예외의 종류와 일치하는 단 한 개의 catch블럭만 수행된다.
일치하는 catch 블럭이 없으면 예외는 처리되지 않는다.
블럭 내에 포함된 문장이 하나뿐이어도 괄호{}를 생략할 수 없다.
try {
// 예외 발생 가능성이 있는 문장
} catch (Exception e) {
// e가 발생했을 경우, 이를 처리하기 위한 문장
}
흐름
- try블럭 내에서 예외가 발생한 경우
발생한 예외와 일치하는 catch블럭이 있는 지 확인한다.
일치하는 catch블록을 찾으면, 그 블럭 내의 문장들을 수행하고 전체 try-catch문을 빠져 나와 그 다음 문장을 계속해서 수행한다.
일치하는 catch블럭을 찾지 못하면, 예외는 처리되지 못한다.
try블럭에서 예외가 발생한 위치 이후에 있는 문장들은 수행되지 않는다.
try블럭에 포함시킬 코드의 범위를 잘 선택해야 한다.
public class Test {
public static void main(String[] args) {
System.out.println("0"); // 0
try {
System.out.println("1"); // 1
System.out.println("error" + 2 / 0);
System.out.println("3");
} catch (Exception e) {
System.out.println("4"); // 4
}
System.out.println("5"); // 5
}
}
- try블럭 내에서 예외가 발생하지 않은 경우
catch블럭을 거치지 않고 전체 try-catch문을 빠져 나와 그 다음 문장을 계속해서 수행한다.
public class Test {
public static void main(String[] args) {
System.out.println("0"); // 0
try {
System.out.println("1"); // 1
} catch (Exception e) {
System.out.println("2");
}
System.out.println("3"); // 3
}
}
catch블럭
괄호()와 블럭{} 두 부분으로 나눠져 있다.
괄호() 안에 처리하고자 하는 예외와 같은 타입의 참조변수를 하나 선언한다.
예외가 발생하면, 발생한 예외에 해당하는 클래스의 인스턴스가 만들어진다.
첫 번째 catch블럭부터 차례대로 내려가면서 참조변수의 종류와 생성된 예외 클래스의 인스턴스를 instanceof 연산자를 통해 검사한다.
Exception클래스 타입의 참조변수를 선언하면 어떤 종류의 예외가 발생하더라도 그 catch블럭에 의해 처리되도록 할 수 있다.
public class Test {
public static void main(String[] args) {
System.out.println("0"); // 0
try {
System.out.println("1"); // 1
System.out.println("error" + 2 / 0);
System.out.println("3");
} catch (ArithmeticException e) {
System.out.println("ArithmeticException"); // ArithmeticException
if(e instanceof ArithmeticException) {
System.out.println("true"); // true
}
} catch (Exception e) {
System.out.println("Exception");
}
System.out.println("4"); // 4
}
}
예외가 발생했을 때 생성되는 예외 클래스의 인스턴스에는 발생한 예외에 대한 정보가 담겨져 있다.
예외가 여러 개 발생했을 경우 마지막 예외에 대한 내용만 출력된다.
- printStackTrace()
예외 발생 당시의 호출스택(Call Stack)에 있었던 메서드의 정보와 예외 메시지를 화면에 출력한다.
- getMessage()
발생한 예외 클래스의 인스턴스에 저장된 메시지를 얻을 수 있다.
public class Test {
public static void main(String[] args) {
System.out.println("0"); // 0
try {
System.out.println("1"); // 1
System.out.println("error" + 2 / 0);
System.out.println("3");
} catch (ArithmeticException e) {
System.out.println(e.getMessage()); // / by zero
e.printStackTrace(); // java.lang.ArithmeticException: / by zero at Test.main(Test.java:7)
}
System.out.println("4"); // 4
}
}
- 멀티 catch블럭
JDK1.7부터 여러 catch블럭을 '|' 기호를 이용하여 하나의 catch블럭으로 합칠 수 있다.
개수에 제한이 없다.
멀티 catch블럭에 선언된 참조변수는 상수다.
두 예외 클래스가 조상과 자손의 관계에 있다면, 조상 클래스만 써주는 것과 똑같기 때문에 컴파일 에러가 발생한다.
try {
...
} catch (ParentException | ChildException e) { // error
...
}
try {
...
} catch (ParentException e) {
...
}
finally블럭
try-catch문과 함께 예외의 발생여부에 상관없이 실행되어야 할 코드를 포함시킬 목적으로 사용한다.
public class Test {
public static void main(String[] args) {
System.out.println("0"); // 0
try {
System.out.println("1"); // 1
System.out.println("error" + 2 / 0);
System.out.println("3");
} catch (ArithmeticException e) {
System.out.println("4"); // 4
} finally {
System.out.println("5"); // 5
}
System.out.println("6"); // 6
}
}
return문을 만나도 finally블럭의 문장들은 수행된다.
public class Test {
public static void main(String[] args) {
System.out.println("0"); // 0
try {
System.out.println("1"); // 1
return;
} catch (ArithmeticException e) {
System.out.println("3");
} finally {
System.out.println("4"); // 4
}
System.out.println("5");
}
}
참고 서적: 자바의 정석 3판