예외처리
- 프로그램 실행 중 오류가 발생하면, 오류와 관련된 클래스에서 인스턴스가 생성된다.
이 인스턴스를 프로그래머가 처리하면 예외가 처리된 것으로 간주하여 프로그램을 종료하지 않는다.
그러나 이 인스턴스를 처리하지 않으면 프로그램은 그냥 종료된다.
try ~ catch문 (예외처리)
try {
관찰 영역
} catch ( Exception name ) {
처리 영역
} finally {
무조건 실행되는 영역
}
※ try 영역에서 발생한 예외 상황을 catch 영역에서 처리한다.
try 영역에서 예외가 발생하면 예외와 관련된 인스턴스가 생성이 되고 해당 인스턴스는
catch 영역의 매개변수로 전달된다.
※ finally 영역은 코드의 실행이 try 안으로 진입하면 무조건 실행이 된다.
try 영역에서 예외가 발생하건 안 하건, catch가 실행되건 안되건, 무조건 실행된다.
try로 감싸야 할 영역의 결정
try {
⑴ . . .
⑵ 예외 발생 지점
⑶ . . .
}
//둘 이상의 예외 처리
catch (ArithmeticException e) {
e.getMessage(); // 예외의 원인을 담고있는 문자열을 반환
}
catch (Exception e) {
. . .
}
⑷ 예외 처리 이후 실행 지점
. . .
. . .
// 자바 7부터 하나의 catch 구문에서 둘 이상의 예외 처리 가능
/*
catch (ArithmeticException | InputMismatchException e) {
e.getMessage(); // 예외의 원인을 담고있는 문자열을 반환
}
*/
※ ⑵에서 예외발생시 ⑶에서 진행되는 것이 아니라 ⑷부터 진행된다.
※ catch 구문을 여러 개 구성할 때, 첫번째 catch문에서 모든 예외를 처리한다면, 다음 위치의 catch문은
실행되지 않는다. 따라서 catch문을 구성할 때 순서를 고려하여 작성해야 한다.
Throwable 클래스와 예외처리의 책임 전가
- 예외 클래스의 최상위 클래스는 java.lang.Object를 제외하고 java.lang.Throwable 이다.
Throwable 클래스에는 발생한 예외의 정보를 알 수 있는 메소드가 정의 되어있다.
대표적인 메소드 둘은 다음과 같다.
- public String getMessage() //예외의 원인을 담고 있는 문자열을 반환
- public void printStackTrace() //예외가 발생한 위치와 호출된 메소드의 정보를 출력
class ExceptionMessage {
public static void md1( int n ) {
md2( n, 0 ); // 아래의 메소드 호출
}
public static void md2( int n1, int n2 ) {
int r = n1 / n2; // 예외 발생 지점
}
public static void main(String[] args) {
md1(3);
System.out.println(“Good bye~~~!”);
}
}
메소드 호출 흐름 ( main -> md1 -> md2 )
md2에서 예외 발생, 그러나 md2에서 해당 예외를 처리하지 않았다.
이러한 경우 가상머신은 md2를 호출한 md1에게 예외처리의 책임을 넘긴다.
그런데 md1에서도 예외처리를 하지 않았다.
따라서 가상머신은 md1을 호출한 main에게 예외처리의 책임을 넘긴다.
( 예외처리의 책임이 넘어가면 예외처리의 책임을 넘긴 메소드의 호출은 종료가 된다. )
그리고 그 끝은 main이다. 그런데 main 조차 예외처리를 하지 않으면 가상머신이 대신 예외를 처리한다.
가상머신이 예외를 처리하는 방법은 예외 관련 메시지의 출력과 프로그램의 종료이다.
java.lang.ArithmeticException: / by zero
: 0으로 / 연산을 하여 ArithmeticException 발생하였다.
at ExceptionMessage.md2(ExceptionMessage.java:6)
: ExceptionMessage 클래스의 md2에서 예외가 시작되었고
at ExceptionMessage.md1(ExceptionMessage.java:3)
: ExceptionMessage 클래스의 md1으로 예외가 넘어갔으며
at ExceptionMessage.main(ExceptionMessage.java:9)
: ExceptionMessage 클래스의 main으로 까지 예외가 넘어갔다.
※ 예외는 처리되지 않으면 그 책임이 넘어간다.
md1에서 넘어오는 예외를 main에서 처리하기 위해서는 md1의 호출문을 try ~ catch문으로 감싼다.
public static void main(String[] args) {
try {
md1(3); // 이 지점에서 md1으로부터 예외가 넘어온다.
}
catch(Throwable e) {
e.printStackTrace();
}
. . .
}
※ Throwable 클래스는 모든 예외의 최상위 클래스에 해당하기 떄문에 모든 예외를 받아서 처리할 수 있지만, 가능하면 발생가능성이 있는 예외를 지정해서 처리하는 것이 좋다.
Throwable을 상속하는 예외클래스는 세 부류로 나뉜다.
- Error 클래스를 상속하는 예외 클래스 ( 프로그래머가 처리할 수 없는 예외, 예외 처리의 대상이 아님 )
-> EX) 가상머신에 심각한 오류 발생, 하드디스크의 입출력과 관련한 물리적 오류 등
- Exception 클래스를 상속하는 예외 클래스
-> RuntimeException 클래스 상속하지 않는 Exception 클래스는 예외 처리를 필수로 해주어야 한다.
- RuntimeException 클래스를 상속하는 예외 클래스 ( 주로 잘못된 코드에서 발생, 대부분 예외 처리의 대상이 아님 )
-> RuntimeException 클래스는 Exception 클래스를 상속한다.
throws 키워드는 IOException 예외가 메소드 내에서 발생하면, md2를 호출한 영역으로 예외의 처리를 넘긴다는 뜻이다. ( 예외를 넘기는 순간 md2 메소드의 호출은 종료가 된다. )
예외를 전달받은 곳에서는 해당 예외를 넘길 것지, 처리할 것인지에 대한 부분이 있어야 한다.
//IOException 예외 넘긴다고 명시.
public static void md2() throws IOException {
. . .
}
//throws 선언도 둘 이상의 예외에 대해 처리를 넘긴다는 표시를 할 수 있다.
throws IOException, IndexOutofBoundsException {
. . .
}
※ Error 클래스를 상속하는 예외나 RuntimeException 클래스를 상속하는 예외의 경우 예외의 처리는 선택이다.
※ Exception 클래스를 상속하는(RuntimeException을 상속하지는 않는) 예외는 try ~ catch문으로 처리하거나,
다른 영역으로 넘긴다고(throws) 반드시 명시해야 한다.
프로그래머가 정의하는 예외
프로그래머가 직접 예외 클래스를 정의하고 이를 기반으로 특정 상황에서 예외가 발생하도록 할 수도 있다. 프로그래머가 정의하는 예외 클래스는 다음과 같은데, 이 클래스의 핵심은 Exception을 상속하는데 있다.
class ReadAgeException extends Exception {
public ReadAgeException() {
super(“유효하지 않은 나이가 입력되었습니다.”);
// 이 문자열은 Throwable 클래스에 정의된 다음 메소드 호출 시 반환이 된다.
// public String getMessage();
}
}
// throw new ReadAgeException();
// 예외 인스턴스 생성하고 이를 대상으로 throw 선언을 하면 예외가 발생이 된다.
[참고자료]
윤성우의 열혈 Java 프로그래밍
'Java > 기본' 카테고리의 다른 글
자바(Java) - JVM, JDK, JRE 차이점 이해하기 (0) | 2021.05.23 |
---|---|
자바(Java) - try-with-resources 구문 (0) | 2020.03.01 |
자바(Java) - 추상 클래스 (0) | 2020.02.29 |
자바(Java) - 인터페이스 (0) | 2020.02.27 |
자바(Java) - 인스턴스 접근 가능 멤버 결정 규칙 (0) | 2020.02.27 |