자바 13일차

김재현·2022년 9월 4일
0

Java

목록 보기
14/15

finally 블럭

  • finally 블럭은 예외의 발생여부에 상관 없이 실행되어야할 코드를 포함시킬 목적으로 사용됨.
  • try-catch 문 끝에 선택적으로 덧붙여 사용할 수 있으며,
    try-catch-finally의 순서로 구성됨.
try { 
	// 예외가 발생할 가능성이 있는 문장 삽입.
} catch (Exception1 e1) {
	// 예외처리를 위한 문장 삽입.
} finally { 
 	// 예외의 발생여부에 관계 없이 항상 수행되야하는 문장들을 넣음. 
    // finally 블럭은 try-catch 문의 맨 마지막에 위치해야 함.
}
  • 예외 발생시에는 try → catch → finally 순으로 실행되고, 예외가 발생하지 않은 경우 try → finally의 순으로 실행된다.
  • 아래의 예시와 같이 프로그램을 설치하는 코드가 있을 때, 설치를 정상적으로 마쳐도 임시파일을 삭제해야하고 중간에 예외가 발생해오 임시파일을 삭제해야한다.
try {
	startInstall();			// 프로그램 설치에 필요한 준비를 함.
    copyFiles();			// 파일들을 복사.
    deleteTempFiles();		// 프로그램 설치에 사용된 임시 파일 삭제.
} catch (Exception e) {	
	e.printStackTrace();
    deleteTempFiles();		// 프로그램 설치에 사용된 임시 파일 삭제
} 							// try-catch 끝
  • 이 경우 try 블럭과 catch 블럭에 같은 코드를 넣기보다, 아래와 같이 finally 블럭에 넣는 것이 낫다.
try {
	startInstall();			// 프로그램 설치에 필요한 준비를 함.
    copyFiles();			// 파일들을 복사.
} catch (Exception e) {
	e.printStackTrace();
} finally { 
	deleteTempFiles();		// 프로그램 설치에 사용된 임시 파일 삭제
} 							// try-catch 끝

사용자 정의 예외 생성

  • 기존에 정의된 예외 클래스 외에 필요에 따라 새로운 예외 클래스를 정의하여 사용할 수 있다. 보통 Exception 클래스 또는 RuntimeException 클래스로부터 상속받는 클래스를 만들지만, 필요에 따라 알맞은 예외 클래스를 선택 가능하다.
class MyException extends Exception {
	MyException(String msg) {	// 문자열을 매개변수로 받는 생성자
    	super(msg);  			// 조상인 Exception 클래스의 생성자 호출.
    }
}
  • Exception 클래스로부터 상속받아서 MyException 클래스를 만들었다. 멤버변수나 메서드를 추가할 수도 있다.
  • Exception클래스는 생성 시 String 값을 받아 메시지로 저장할 수 있다. 사용자 정의 예외 클래스도 메시지를 저장하려면, 위와 같이 String을 매개변수로 받는 생성자를 추가해주어야 한다.
  • 기존 예외 클래스는 주로 Exception을 상속받아 check예외로 작성하는 경우가 많았지만, 최근에는 예외처리를 선택적으로 할 수 있도록 RuntimeException을 상속받아서 작성하는 쪽으로 바뀌어가고 있다.
    check 예외는 반드시 예외처리를 해주어야 하기 때문에 예외처리가 불필요한 경우에도 try-catch문을 넣어 코드가 복잡해지기 때문이다.

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

  • 한 메서드에서 발생할 수 있는 예외가 여럿인 경우, 몇 개는 try-catch문을 통해 메서드 내에서 자체적으로 처리하고, 나머지는 선언부에 지정하여 호출한 메서드에서 처리하도록 함으로써 양쪽에서 나눠 처리하도록 할 수 있다.
    심지어 단 하나의 예외에 대해서도 예외가 발생한 메서드와 호출한 메서드, 양쪽에서 처리하도록 할 수 있다.
  • 이것은 예외를 처리한 후 인위적으로 다시 발생시키는 방법을 통해 가능한데, 이것을 예외 되던지기, exception re-throwing라고 한다.
  • 먼저 예외가 발생할 가능성이 있는 메서드에서 try-catch문을 사용, 예외를 처리해주고 →
    catch문에서 필요한 작업을 행한 후 throw문을 사용해서 예외를 다시 발생시킨다. →
    다시 발생한 예외는 이 메서드를 호출한 메서드에게 전달되고 →
    호출한 메서드의 try-catch문에서 예외를 또다시 처리한다.
  • 이 방법은 하나의 예외에 대해 예외가 발생한 메서드와 이를 호출한 메서드 양쪽 모두에서 처리해줘야 할 작업이 있을 때 사용된다.
    주의할 점은 예외가 발생할 메서드에서는 try-catch문을 사용해서 예외처리를 해줌과 동시에 메서드의 선언부에 발생할 예외를 throws에 지정해줘야 한다는 것이다.
  • 반환 값이 있는 return문의 경우, catch 블럭에도 return문이 있어야한다. 예외가 발생했을 때도 값을 반환해야하기 때문이다.

연결된 예외 (chained exception)

  • 한 예외가 다른 예외를 발생시키는 경우.
    예외 A가 예외 B를 발생시킨다면, A를 B의 원인 예외(cause exception)이라고 한다.
try {
	startInstall();			// SpaceException 발생
    copyFiles();			// 파일들을 복사.
} catch (SpaceException e) {
	InstallException ie = new InstallException("설치 중 예외 발생"); // 예외 생성
    ie.initCause(e);		// InstallException의 원인 예외를 SpaceException으로 지정
    throw ie; 				// InstallException을 발생.
} catch (MemoryException me) {
	....
  • InstallException을 생성한 후에, initCause()SpaceExceptionInstallException의 원인 예외로 등록한다. 그리고 throw로 예외를 던진다.
    initCause()는 Exception 클래스의 조상인 Throwable 클래스에 정의되어 있기 때문에 모든 예외에서 사용 가능하다.

    Trowable initCause (Throwable cause) : 지정된 예외를 원인 예외로 등록
    Throwable getCause() : 원인 예외를 반환

  • 발생한 예외를 그냥 처리하지 않고, 왜 원인 예외로 등록해서 다시 예외를 발생시킬까? 여러가지 예외를 하나의 큰 분류의 예외로 묶어서 다루기 위해서이다.
  • 하지만 InstallExceptionSpaceExceptionMemoryException의 조상으로 해 catch블럭을 작성하면, 실제로 발생한 예외가 어떤 것인지 알 수 없고, 상속관계를 변경해야 한다는 것도 부담이다.
try {
	startInstall();			// SpaceException 발생
    copyFiles();
} catch (InstallException e) {  // InstallException은
	e.printStackTrace(); 	    // SpaceException과 MemoryException의 조상.
}
  • 그래서 나온 방법이, 예외가 원인 예외를 포함할 수 있게 한 것이다. 이렇게 하면, 두 예외는 상속관계가 아니어도 상관 없다.
  • 또 다른 이유는 checked 예외를 unchecked 예외로 변경하기 위해서다.
    checked : Exception 자식. 필수처리

    unchecked : RuntimeException 자식. 선택처리
class SpaceException extends Exception { 
	~~
  • SpaceException을 예외 필수 처리인 Exception에서 선택처리인 RuntimeException으로 바꾸고 싶다. 하지만 SpaceException을 여러 곳에서 사용하고 있다면 상속관계를 바꾸는 것이 쉬운 것이 아니다. 이럴 때 연결된 예외를 사용할 수 있다.
    RuntimeException을 만들고 SpaceException을 포함시키는 방식으로 말이다.
static void startInstall() throws SpaceException, MemoryException { 
	if(!enoughSpace())
    	throw new SpaceException("공간 부족");
    
    if(!enoughMemory())
    	throw new MemoryException("메모리 부족");
}

static void startInstall() throws SpaceException {
	if(!enoughSpace())
    	throw new SpaceException("공간 부족");
    
    if(!enoughMemory())
    	throw new RuntimeException(new MemoryException("메모리 부족"));
}
  • RuntimeException을 만들고 MemoryException을 원인예외로 등록함으로써 MemoryException를 예외 선택처리로 바꾸어주었기 때문에 위의 경우와 다르게 선언부에 선언해주지 않아도 된다.
  • MemoryExceptionException의 자식이므로 반드시 예외를 처리해야하는데, 이것을 RuntimeException으로 감싸버렸기 때문에 unchecked 예외가 되었다. 더 이상 startInstall()의 선언부에 MemoryException을 선언하지 않아도 된다.
  • initCause() 를 사용해 원인예외로 등록할 수도 있고, 생성자를 사용해서 원인예외로 등록할 수도 있다.
    위 코드에서는 initCause()대신 RuntimeException의 생성자를 사용했다.
    RuntimeException(Throwable cause) → 원인예외를 등록하는 생성자

연결된 예외 정리

  • 어떤 예외를 다른 예외로 감싸는 것.
  • 세부적인 예외를 포괄적인 예외로 감쌀때 사용.
  • checked 예외를 unchecked 예외로 변경할 때 사용.

0개의 댓글