목표
자바의 예외 처리에 대해 학습하세요.
학습할 것 (필수)
- 자바에서 예외 처리 방법 (try, catch, throw, throws, finally)
- 자바가 제공하는 예외 계층 구조
- Exception과 Error의 차이는?
- RuntimeException과 RE가 아닌 것의 차이는?
- 커스텀한 예외 만드는 방법
#자바에서 예외 처리 방법 (try, catch, throw, throws, finally)
예외처리란
정의 : 프로그램 실행 시 발생할 수 있는 예기치 못한 예외의 발생에 대비한 코드를 작성하는 것.
목적 : 은 예외의 발생으로 인한 실행 중인 프로그램의 갑작스러운 비정상 종료를 막고, 정상적인 실행상태를 유지하는 것
try-catch문은 예외처리를 위한 기본 구조다.
try{
...
} catch(예외1){
...
} catch(예외2){
...
...
}
-try블럭 내에서 예외가 발생한 경우,
1. 발생한 예외와 일치하는 catch블럭이 있는지 확인한다.
2. 일치하는 catch블럭을 찾게 되면, 그 catch블럭 내의 문장들을 수행하고 전체 try-catch문을 빠져나가서 그다음 문장을 계속해서 수행한다.
-try블럭 내에서 예외가 발생하지 않은 경우
1. catch블럭을 거치지 않고 전체 try-catch문을 빠져나가서 수행을 계속한다.
ex_ArithmeticException)
int c;
try{
c=4/0;
} catch(ArithmeticExcpetion ae) {
c=-1;
}
ArithmeticException(0으로 나눌 때 발생하는 예외)이 발생하면 c에 -1을 대입하도록 예외를 처리한 것이다.
catch블럭
catch블럭은 괄호()와 블럭{}으로 나눠져 있는데, 괄호() 내에는 처리하고자 하는 예외와 같은 타입의 참조 변수를 하나 선언해야 한다.
예외가 발생하면, 발생한 예외에 해당하는 클래스의 인스턴스가 만들어진다. 위 예(ex_ArithmeticException)에서 예외가 발생한 문장이 try블럭에 포함되어 있다면, 이 예외를 처리할 수 있는 catch블럭이 있는지 찾게 된다. catch블럭을 차례로 내려가면서 catch블럭의 괄호()내에 선언된 참조변수의 종류와 생성된 예외클래스의 인스턴스에 instanceof 연산자를 이용해서 검사하게 되는데, 검사결과가 true인 catch블럭을 만날 때까지 검사는 계속된다. 검사결곡 true면 catch블럭에 있는 문자아들을 모두 수행한 후에 try-catch문을 빠져나가ㅏ고 예외는 처리되지만, true인 catch블럭이 없으면 예외는 처리되지 않는다.
멀티 catch블럭
JDK1.7부터 여러 catch블럭을 '|'기호를 이용해서 하나의 catch블럭으로 합칠 수 있게 되었다.
| JDK1.7 이전 | JDK1.7 이후 |
| try{... } catch(ExceptionA e){ e.printStackTrace(); } catch(ExceptionB e2){ e2.printStackTrace(); } |
try{... } catch(ExceptionA | ExceptionB e){ e.printStackTrace(); } |
만일 멀티 catch블럭의 '|'기호로 연결된 예외 클래스가 조상과 자손의 관계에 있다면 컴파일 에러가 발생한다. 두 예외 클래스가 조상과 자손의 관계에 있다면, 그냥 조상 클래스만 써주는 것과 똑같기 때문이다. 불필요한 코드는 제거하라는 뜻에서 에러가 발생하는 것이다.
멀티 catch는 하나의 catch블럭으로 여러 예외를 처리하는 것이기 때문에, 발생한 예외를 멀티 catch블럭으로 처리하게 되었을 때, 멀티 catch블럭 내에서는 실제로 어떤 예외가 발생한 것인지 알 수 없다. 그래서 참조 변수 e로 멀티 catch블럭에 '|'기호로 연결된 예외 클래스들의 공통 분모인 조상 예외 클래스에 선언된 멤버만 사용할 수 있다.
예외 발생시키기 throw
키워드 throw를 사용해서 예외를 고의로 발생시킬 수 있다.
1. 연산자 new를 이용해서 발생시키려는 예외 클래스의 객체를 만듦
Exception e = new Exception("고의로 발생시켰음");
2. 키워드 throw를 이용해서 예외를 발생
throw e;
위 두 가지 문장을 throw new Exception 한 줄로 쓸 수 있다.
Exception 인스턴스를 생성할 때, 생성자에 String을 넣어 주면, 이 String이 Exception인스턴스에 메세지로 저장된다. 이 메세지는 getMessage()를 이용해서 얻을 수 있다.
메서드에 예외 선언하기 throws
예외를 처리하는 방법 중 예외를 메서드에 선언하는 방법이 있다.
메서드에 예외를 선언하려면, 메서드의 선언부에 키워드 throws를 사용해서 메서드 내에서 발생할 수 있는 예외를 적어주기만 하면 된다. 예외가 여러 개일 경우에는 쉼표(,)로 구분한다.
void method() throws Exception1, Exception2, .... ExceptionN{
//메서드의 내용
}
만일 모든 예외의 최고 조상인 Exception 클래스를 메서드에 선언하면, 이 메서드는 모든 종류의 예외가 발생할 가능성이 있다는 뜻이다. 메서드의 선언부에 예외를 선언함으로써 메서드를 사용하려는 사람이 메서드의 선언부를 보았을 때, 이 메서드를 사용하기 위해서는 어떠한 예외들이 처리되어야 하는지 쉽게 알 수 있다.
메서드에 예외를 선언할 때 일반적으로 RuntimeException 클래스들은 적지 않는다. throws에 선언한다고 해서 문제가 되지는 않지만, 보통 반드시 처리되어야 하는 예외들만 선언한다.
예외를 메서드의 throws에 명시하는 것은 예외를 처리하는 것이 아니라, 자신을 호출한 메서드에게 예외를 전달하여 예외처리를 떠맡기는 것이다. 예외를 전달받은 메서드는 자신을 호출하는 또 다른 메서드에 전달할 수 있으며, 이런 식으로 계속 호출 스택에 있는 메서드들을 따라 전달되다가 마지막에 main메서드에서도 예외가 처리되지 않으면, main메서드가 종료되면서 프로그램 전체가 종료된다. 결국 예외는 처리되는 것이 아닌 단순히 전달만 되는 것이므로 결국 어느 한 곳에서는 try-catch문으로 처리를 해주어야 한다.
finally 블럭
프로그램 수행 도중 예외가 발생하면 플로그램이 중지되거나 예외 처리에 의해 catch 구문이 실행된다. 하지만 어떤 예외가 발생하더라도 반드시 실행되어야 하는 부분이 있을 때는 finally 블럭을 사용한다.
try{
//예외가 발생할 가능성이 있는 문장들
} catch(Exception e1) {
//예외처리를 위한 문장을 적는다.
} finally {
//예외의 발생여부에 관계없이 항상 수행되어야 하는 문장들을 넣는다
//finally 블럭은 try-catch문의 맨 마지막에 위치해야한다
}
#자바가 제공하는 예외 계층 구조

모든 클래스의 조상은 Object클래스 이므로 Exception과 Errror의 클래스 역시 Object클래스를 상속받고 있다.
Throwable 클래스를 상속 받는 클래스는 Error와 Exception이 있는데 모든 예외의 최고 조상 클래스는 당연히 Exception이다. Error자체는 시스템 레벨의 심각한 영향을 주는 에러이기 때문에 시스템에 변화를 주어 문제를 처리해야 하는 경우가 일반적이다. 하지만 Exception은 개발자가 충분히 로직을 추가하여 처리할 수 있는 부분이다.
체크 예외(checked Exception)
Runtime Excetion을 상속하지 않은 예외들을 말하는데, 체크 예외가 발생할 수 있는 메서드를 사용할 경우, 복구가 가능한 예외들이기 때문에 반드시 예외를 처리하는 코드를 작성해야 한다. catch문으로 예외를 잡거나, throws로 예외를 자신을 호출한 클래스로 던지는 방법으로 해결해야 한다. 이때 해결하지 않으면 컴파일 시 체크 예외가 발생한다. 체크 예외는 Java컴파일러와 JVM이 규칙을 준수하는지 확인하기 때문에 Exception이 호출된다.
대표적인 Exception - IOException , SQLException
언체크 예외(unchecked Exception)
RuntimeException을 상속한 예외들을 말하는데, 언체크 예외라고 불리는 이유는 명시적으로 예외처리를 강제하지 않기 때문이다. 언체크 예외는 따로 catch문으로 예외를 잡거나, throws로 선언하지 않아도 된다. 프로그램에 오류가 있을 때 발생하도록 의도된 것이다.
대표적인 Exception - NullPointerException, IllegalArgumentExcption
| Checked Exception | Unchecked Exception | |
| 클래스 | Excption 클래스의 자손들 중 Runtime Exception을 제외한 모든 클래스 | RuntimeException 클래스와 자손 클래스 |
| 처리여부 | 반드시 예외 처리 해야함 | 명시적 처리 강제하지 않음(선택) |
| 확인시점 | 컴파일 단계 | 실행 단계 |
| 예외 발생 시 트랜잭션 처리 | roll back 하지 않음 | roll back |
| 대표 예외 | IOException SQLException |
NullPointerException IllegalArgumentException IndexOutOfBoundException SystemException |
#Exception과 Error의 차이는?
에러(Error) : 프로그램 코드에 의해서 수습될 수 없는 심각한 오류
예외(Exception) : 프로그램 코드에 의해서 수습될 수 있는 다소 미약한 오류
| 비교의 근거 | 오류 Error | 예외 Exception |
| 기본 | 시스템 자원이 부족하여 오류가 발생했다. | 코드로 인한 예외가 발생했다. |
| 회복 | 오류는 복구 할 수 없다. | 예외는 복구 가능하다 |
| 키워드 | 프로그램 코드에서 오류를 처리할 수 있는 방법은 없다. | 예외는 3개의 키워드 "try", "catch", 및 "throw"를 사용하여 처리하면 된다. |
| 결과 | 오류를 감지하면 프로그램이 비정상적으로 종료 | 예외가 감지되면 throw및 catch키워드에 따라 예외가 발생 |
| 유형 | 오류는 검사되지 않은 유형으로 분류 | 예외는 체크 된 또는 확인되지 않은 유형으로 분류 |
| 꾸러미 | Java에서 오류는 "java.lang.Error"패키지로 정의 | Java에서 예외는 "java.lang.Exception"에 정의 |
| 예 | OutOfMemory, StackOverFlow | 확인 된 예외(Checked) : NoSuchMethod, ClassNotFound 확인되지 않은 예외(UnChecked) : NullPoint, IndexOutOfBounds |
#RuntimeException과 RE가 아닌 것의 차이는?
Exception 클래스의 하위 클래스들은 RuntimeException과 그 외 Exception 클래스의 자식 클래스 2가지로 구분할 수 있다. 2가지로 구분하는 기준은 CheckedException 인지 UnCheckedException인지로 판단한다. CheckedException은 반드시 예외를 처리해야하고, UncheckedException은 예외 처리를 강제하지 않는다.
RuntimeException
RuntimeException은주로 프로그래머의 실수로 일어나고 런타임 시(실행하고 나서)에 예외를 발생시키며, 실행 전에는 컴파일 에러를 발생시키지 않는다. 즉, RuntimeException은 UncheckedException이다. 예외처리를 강제하지는 않지만 해당 내용이 런타임 시에 예외를 발생시킬 수 있음을 인지하면 예외를 처리해주는 것이 좋다.
대표적인 RuntimeException으론 NullPointException, IndexOutOfException, ArithmeticException 등이 있다.
그 외 클래스
RuntimeException 이외의 Exception클래스들은 주로 외부의 영향으로 발생할 수 있는 것들로서, 사용자들의 동작에 의해서 발생하는 경우가 많고 모두 CheckedException이다. 실행하기 전 컴파일 단계에서 에러를 발생시킨다. 따라서, 실행하기 전 예외를 반드시 처리해줘야 한다. 대표적인 예로 IOException, SQLException 등이 있다.
#커스텀한 예외 만드는 방법
기존의 정의된 예외 클래스 외에 필요에 따라 프로그래머가 새로운 예외 클래스를 정의하여 사용할 수 있다. 보통 Exception클래스 또는 RuntimeException클래스로부터 상속받아 클래스를 만들지만, 필요에 따라서 알맞은 예외 클래스를 선택할 수 있다.(가능하면 새로운 예외 클래스를 만들기 보다 기존의 예외클래스를 사용하는 것이 좋다)
class MyException extends Exception{
Exception(String msg){ //문자열을 매개변수로 받는 생성자
super(msg); //조상인 Exception클래스의 생성자를 호출
}
}
Exception클리스로부터 상속받아서 MyException클래스를 만들고 필요하다면 멤버 변수나 메서드를 추가할 수 있다. Exception클래스는 생성 시에 String값을 받아서 메세지로 저장할 수 있다. 직접만든 사용자정의 예외 클래스도 메세지를 저장할 수 있으려면, 위 코드와 같이 String을 매개변수로 받는 생성자를 추가해주어야 한다.
class MyException extends Exception{
private final int ERR_CODE;
MyException(String msg, int errCode){
super(msg);
ERR_CODE = errCode;
}
MyException(String msg){
this(msg, 100);
}
public int getErrCode(){
return ERR_CODE;
}
}
에러코드 값을 저장할 수 있도록 ERR_CODE와 getErrCode()를 멤버로 추가했다. 이렇게 함으로써 MyException이 발생했을 때, catch블럭에서 getMessage()와 getErrCode()를 사용해서 에러코드와 메세지를 모두 얻을 수 있다.
기존의 예외 클래스는 주로 Exception을 상속받아서 Checked Exception으로 작성하는 경우가 많았다고 한다. 요즘은 예외처리를 선택적으로 할 수 있도록 RuntimeException을 상속받아서 작성하는 쪽으로 바뀌어가고 있다고 한다. Checked Exception은 반드시 예외처리를 해주어야 하기 때문에 불필요한 경우에도 try-catch문을 넣어 코드가 복잡해지기 때문이다.
'Back-end > Java_T.I.L' 카테고리의 다른 글
| [TIL 220108] 자바의정석 6. 객체지향 프로그래밍 I (2) (0) | 2022.01.08 |
|---|---|
| [TIL 220107] 자바의정석 6. 객체지향 프로그래밍 I (1) (0) | 2022.01.08 |
| 8. 인터페이스 (0) | 2022.01.05 |
| 7.패키지, import, 클래스패스(classpath) (0) | 2022.01.05 |
| 6. 상속, 오버라이딩, 추상클래스, Object 클래스 (0) | 2022.01.03 |
댓글