본문 바로가기

프로그래밍/JAVA

[JAVA 이론] 예외처리

예외처리

프로그램이 실행 중 어떤 원인에 의해서 오작동을 하거나 비정상적으로 종료되는 경우가 있다. 이러한 결과를 초래하는 원인을 프로그램 에러 또는 오류라고 한다.

컴파일 에러: 컴파일 시에 발생하는 에러
런타임 에러: 실행 시에 발생하는 에러
논리적 에러: 실행은 되지만, 의도와 다르게 동작하는 것

에러: 프로그램 코드에 의해서 수습될 수 없는 심각한 오류
예외: 프로그램 코드에 의해서 수습될 수 있는 다소 미약한 오류

 

예외 클래스의 계층구조

- 예외 클래스 계층도

 

- Exception클래스와 RutimeException클래스 중심의 상속 계층도

Exception클래스들 //사용자의 실수와 같은 외적인 요인에 의해 발생하는 예외
RuntimeException클래스들 //프로그래머의 실수로 발생하는 예외

 

try-catch문

예외처리의 정의

- 프로그램 실행 시 발생할 수 있는 예외에 대비한 코드를 작성하는 것

 

예외처리의 목적

- 프로그램의 비정상 종료를 막고, 정상적인 실행상태를 유지하는 것

 

try-catch문의 형태

- 하나의 try블럭 다음에는 여러 종류의 예외를 처리핳 수 있도록 하나 이상의 catch블럭이 올 수 있으며, 이 중 발생한 예외의 종류와 일치하는 단 한 개의 catch블럭만 수행된다.

try {
	//예외가 발생할 가능성이 있는 문장들을 넣는다.
}catch(Exception1 e1) {
	//Exception1이 발생했을 경우, 이를 처리하기 위한 문장을 적는다.
}catch(Exception2 e2) {
	//Exception2이 발생했을 경우, 이를 처리하기 위한 문장을 적는다.
}

 

try-catch문에서의 흐름

- try블럭 내에서 예외가 발생한 경우

1) 발생한 예외와 일치하는 catch블럭이 있는지 확인한다.

2) 일치하는 catch블럭을 찾게 되면, 그 catch블럭 내의 문장들을 수행하고 전체 try-catch문을 빠져나가서 그 다음 문장을 계속해서 수행한다. 만일 일치하는 catch블럭을 찾지 못하면, 예외는 처리되지 못한다.

 

- try블럭 내에서 예외가 발생하지 않은 경우

1) catch블럭을 거치지 않고 전체 try-catch문을 빠져나가서 수행을 계속한다.

 

- 모든 예외 클래스는 Exception클래스의 자손이므로, catch블럭의 괄호()에 Exception클래스 타입의 참조변수를 선언해 놓으면 어떤 종류의 예외가 발생하더라도 이 catch블럭에 의해서 처리된다.

 

printStackTrace()와 getMessage()

예외가 발생했을 때 생성되는 예외 클래스의 인스턴스에는 발생한 예외에 대한 정보가 담겨 있으며, 해당 함수 등을 이용해 정보를 얻을 수 있다. catch블럭 내에서만 사용 가능.

- printStackTrace() 

예외발생 당시의 호출스택(Call Stack)에 있었던 메서드의 정보와 예외 메시지를 화면에 출력한다.

ex) e.printStackTrace();

 

- getMessage()

발생한 예외클래스의 인스턴스에 저장된 메시지를 얻을 수 있다.

ex) ae.getMessage();

 

멀티 catch블럭

JDK1.7부터 여러 catch블럭을 '|'기호를 이용해서, 하나의 catch블럭으로 합칠 수 있게 되었으며, 이를 '멀티 catch블럭'이라 한다. 만일 멀티 catch블럭의 '|'기호로 연결된 예외 클래스가 조상과 자손의 관계에 있다면 컴파일 에러가 발생 한다.

try{ //일반 catch블럭
	...
} catch (ExceptionA e){
	e.printStackTrace();
} catch (ExceptionB e2){
	e2.printStackTrace();
}

try{ //멀티 catch블럭
	...
} catch (ExceptionA | ExceptionB e){
	e.printStackTrace();
}

 

예외 발생시키기

키워드 throw를 사용해서 프로그래머가 고의로 예외를 발생시킬 수 있으며, 방법은 아래의 순서를 따르면 된다.

1. 먼저, 연산자 new를 이용해서 발생시키려는 예외 클래스의 객체를 만든 다음 
   Exception e = new Exception("문자열");

2. 키워드 throw를 이용해서 예외를 발생시킨다.
   throw e;

//형태1
Exception e = new Exception("문자열");
throw e;

//형태 2
throw new Exception("문자열");

 

RuntimeException클래스에 해당하는 예외는 프로그래머에 의해 실수로 발생하는 것들이기 때문에 예외처리를 강제하지 않는다. 하지만 Exception의 경우 강제 됨

 

RuntimeException 클래스 - unckecked예외

Exception 클래스 - ckecked예외

 

메서드에 예외 선언하기

void method() throws Exception1, Exception2, ... ExceptionN{
	//메서드의 내용
}

 

자동 자원 반환 - try-with-resources문

JDK1.7부터 try-with-resources문이라는 try-catch문의 변형이 새로 추가되었다. try-with-resources문의 괄호 ()안에 객체를 생성하는 문장을 넣으면, 이 객체는 따로 close()를 호출하지 않아도 try블럭을 벗어나는 순간 자동적으로 close()가 호출된다. 그 다음에 catch블럭 또는 finally블럭이 수행된다.

public class TryWithResourceEx {
	public static void main(String[] args) {
		try(CloseableResource cr = new CloseableResource()){
			cr.exceptionWork(false);
		}catch(WorkException e){
			e.printStackTrace();
		}catch(CloseException e){
			e.printStackTrace();
		}
	}
}

class CloseableResource implements AutoCloseable{
	public void exceptionWork(boolean exception) throws WorkException{
		System.out.println("exception()");
		
	}
	
	public void close() throws CloseException{
		System.out.println("close()");
	}
}

 

사용자정의 예외 만들기

기존의 정의된 예외 클래스 외에 필요에 따라 프로그래머가 새로운 예외 클래스를 정의하여 사용할 수 있다. 보통 Exception클래스 또는 RuntimeException클래스로부터 상속받아 클래스를 만들지만, 필요에 따라서 알맞은 예외 클래스를 선택할 수 있다.

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;
    }
}

 

예외 되던지기(exception re-throwing)

한 메서드에서 발생할 수 있는 예외가 여럿인 경우, 몇 개는 try-catch문을 통해서 메서드 내에서 자체적으로 처리하고, 그 나머지는 선언부에 지정하여 호출한 메서드에서 처리하도록 함으로써, 양쪽에서 나눠서 처리되도록 할 수 있다.

public class ExceptionEx{

	public static void main(String[] args) {
		try {
			method();
		} catch (Exception e) {
			System.out.println("main메서드에서 예외 처리");
		}
	}

	static void method() throws Exception{
		try {
			throw new Exception();
		}catch(Exception e){
			System.out.println("method메서드에서 예외처리");
			throw e;
		}
	}
}

//method메서드에서 예외처리
//main메서드에서 예외 처리

 

연결된 예외(chained exception)

한 예외가 다른 예외를 발생시킬 수도 있다. 예를 들어 예외 A가 예외 B를 발생시켰다면, A를 B의 '원인 예외(cause exception)'라고 한다.

ie.initCause(e) //ie의 원인예외를 e로 지정

Throwable initCause(Throwable cause) //지정한 예외를 원인 예외로 등록
Throwable getCause() //원인 예외를 반환

 

- 원인 예외로 등록해서 다시 예외를 발생 시키는 이유

1) 여러가지 예외를 하나의 큰 분류의 예외로 묶어서 다루기 위해서이다.

2) checked예외를 unchecked예외로 바꿀 수 있도록 하기 위함