프로그램이 실행 중 어떤 원인에 의해서 오작동을 하거나 비정상적으로 종ㄹ료되는 경우가 있다. 이러한 결과를 초래하는 원인을 프로그램 에러 또는 오류라고 한다.
오류의 종류
- 컴파일 에러 : 컴파일 시에 발생하는 에러
- 런타임 에러 : 실행 시에 발생하는 에러
- 논리적 에러 : 실행은 되지만, 의도와 다르게 동작하는 것
▶ 에러 : 메모리 부족이나 스택오버플로우와 같이 일단 발생하면 복구할 수 없는 심각한 오류
▶ 예외 : 발생하더라도 수습될 수 있는 비교적 덜 심각한 것
예외 클래스의 계층 구조
- Exception 클래스들 : 사용자의 실수와 같은 외적인 요인에 의해 발생하는 예외
- RuntimeException 클래스들 : 프로그래머의 실수로 발생하는 예외
예외 처리하기
예외처리란?
프로그램 실행 시 발생할 수 있는 예기치 못한 예외의 발생에 대비한 코드를 작성하는 것이며, 예외처리의 목적은 예외의 발생으로 인한 실행 중인 프로그램의 갑작스런 비정상 종료를 막고, 정상적인 실행상태를 유지할 수 있도록 하는 것이다.
* 발생한 예외를 처리하지 못하면, 프로그램은 비정상적으로 종료되며, 처리되지 못한 예외(uncaught exception)는 JVM의 '예외처리기'가 받아서 예외의 원인을 화면에 출력한다.
<예외처리 방법>
① try-catch문 (직접처리)
② 예외 선언하기(떠넘기기)
③ 덮기, 무시(은폐)
try-catch문
try-catch문에서 예외가 발생한 경우와 발생하지 않았을 때의 흐름(문장의 실행순서)이 달라지는데, 이 두 가지 경우에 따른 문장 실행 순서는 아래와 같다.
▶ try 블럭 내에서 예외가 발생한 경우
1. 발생한 예외와 일치하는 catch블럭이 있는지 확인한다.
2. 일치하는 catch블럭을 찾게 되면, 그 catch 블럭 내의 문장들을 수행하고 전체 try-catch문을 빠져나가서 그 다음 문장을 계속해서 수행한다. 만일 일치하는 catch블럭을 찾지 못하면 예외는 처리되지 못한다.
▶ try 블럭 내에서 예외가 발생하지 않은 경우
1. catch 블럭을 거치지 않고 전체 try-catch문을 빠져나가서 수행을 계속한다.
코드로 이해하기
class ExceptionEx{
public static void main(Strig args[]) {
System.out.println(1);
System.out.println(2);
try {
System.out.println(3);
System.out.println(0/0); // 0으로 나눠서 ArithmeticException 발생
System.out.println(4); // 실행되지 않음.
} catch (ArithmeticException ae) {
if(ae instanceof ArithmeticException)
System.out.println("true");
System.out.println("ArighmeticException");
} catch (Exception e) { // ArighmeticException을 제외한 모든 예외가 처리됨
System.out.println("Exception");
}
System.out.println(6);
}
}
실행결과
1
2
3
true
ArithmeticException
6
printStackTrace()와 getMessage()
예외가 발생했을 때 생성되는 예외 클래스의 인스턴스에는 발생한 예외에 대한 정보가 담겨져 있으며, getMessage()와 printStackTrace()를 통해서 이 정보들을 얻을 수 있다. catch 블럭의 괄호()에 선언된 참조변수를 통해 이 인스턴스에 접근할 수 있다. 이 참조변수는 선언된 catch블럭 내에서만 사용 가능하며, 자주 사용된다.
printStackTrace() | 예외 발생 당시의 호출스택(Call Stack)에 있었던 메서드의 정보와 예외 메시지를 화면에 출력한다. |
getMessage() | 발생한 예외 클래스의 인스턴스에 저장된 메시지를 얻을 수 있다. |
finally블럭
finally 블럭은 try-catch문과 함께 예외의 발생여부에 상관없이 실행되어야 할 코드를 포함시킬 목적으로 사용되며 try-catch-finally 순서로 구성된다.
자동 자원 반환 ( try-with-resources문)
주로 입출력에 사용되는 클래스 중에서는 사용한 후에 꼭 닫아 줘얗 하는 것들이 있다. 그래야 사용했던 자원(resources)이 반환되기 때문이다.
코드 예)
try(fis = new FileInputStream("score.dat");
dis = new DataInputStream(fis)) {
while(true){
score = dis.readInt();
System.out.println(score);
sum += score;
}catch(EOFException e){
System.out.println("점수 총합"+sum);
}catch(IOException ie){
ie.printStackTrace();
}
try-with-resources문의 괄호() 안에 객체를 생성하는 문장을 넣으면, 이 객체는 따로 close()를 호출하지 않아도 try블럭을 벗어나는 순간 자동적으로 close()가 호출된다. 이처럼 try-with-resources문에 의해 자동으로 객체의 close()가 호출될 수 있으려면, 클래스가 AutoCloseable이라는 인터페이스를 구현한 것이어야만 한다.
예외 되던지기(Exception re-throwing)
한 개 이상의 예외에 대해서 try-catch문을 통해 메서드 내에서 자체적으로 처리하고, 그 나머지는 선언부에 지정하여 호출한 메서드에서 처리하도록 함으로써 양쪽에서 나눠서 처리되도록 할 수 있다. 예외를 처리한 후에 인위적으로 다시 발생시키는 방법으로 할 수 있는데, 이것을 '예외 던지기' 라고 한다.
public class ExceptionEx {
public static void main(String[] args) {
try {
method1();
}catch (Exception e){
System.out.println("main 메서드에서 예외가 처리되었습니다.");
}
}
static void method1() throws Exception {
try{
throw new Exception();
}catch (Exception e){
System.out.println("method1메서드에서 예외가 처리되었습니다.");
throw e; // 다시 예외를 발생시킨다.
}
}
}
// 출력 결과
// method1메서드에서 예외가 처리되었습니다.
// main 메서드에서 예외가 처리되었습니다.
[반환값이 있는 return문의 경우]
static int method1(){
try{
System.out.println("method1()이 호출되었습니다.");
return 0;
}catch(Exception e){
e.printStackTrace();
return 1; // catch 블럭에도 return문이 필요하다.
}finally {
System.out.println("method1()의 finally 블럭이 실행되었습니다.");
}
}
catch 블럭에도 return문이 있어야 한다. 예외가 발생했을 경우에도 값을 반환해야하기 때문이다. 또는, catch블럭에서 예외 되던지기를 할 경우, return문이 없어도 된다.
// ...
}catch(Exception e) {
e.printStackTrace();
//return 1;
throw new Exception(); // return 문 대신 예외를 호출한 메서드로 전달
} //...
연결된 예외(chained exception)
한 예외가 다른 예외를 발생시킬 수도 있다. 예를 들어 예외 A가 예외 B를 발생시켰다면, A를 B의 '원인 예외'라고 한다.
- SpaceException을 원인 예외로 하는 InstallException을 발생시키는 방법
void install() throws InstallException {
try{
startInstall();
copyFiles();
}catch (SpaceException e) {
InstallException ie = new InstallException("설치중 예외발생"); // 예외 생성
ie.initCause(e); // InstallException의 원인 예외를 SpaceException으로 지정
throw ie; // InstallException을 발생시킨다.
}catch(MemoryException me) {
// ...
}
}
먼저, InstallEsception을 생성한 후에, initCause()로 SpaceException을 InstallException의 원인 예외로 등록한다. 그리고 'throw'로 이 예외를 던진다.
*원인 예외로 등록해서 다시 예를 발생시키는 이유
① 여러가지 예외를 하나의 큰 분류의 예외로 묶어서 다루기 위해서이다.
② checked예외(Exception 자손 필수처리)를 unchecked예외(RuntimeException 자손 선택처리)로 바꿀 수 있도록 하기 위해서이다.
[참고]
Java의 정석 (남궁성 저) 8장 예외처리
'공부 > 자바' 카테고리의 다른 글
제네릭스(Generics) (0) | 2024.06.12 |
---|---|
컬렉션 프레임워크(Collections Framework) (0) | 2024.06.10 |
내부 클래스(inner class) (0) | 2024.06.01 |
인터페이스(interface) (0) | 2024.05.31 |
추상 클래스(abstract class) (0) | 2024.05.31 |