자바/기본적인 개념

예외처리(2)

백_곰 2021. 12. 17. 14:46

1. 예외 처리의 두 가지 방법

: main과 method()가 호출 스택에 있다고 가정하자.

 

(1) main에서 호출한 method()에서 try-catch문으로 예외 처리하기.

 

(2) main에서 호출한 method()에서 예외가 발생되어, main으로 넘겨서 try-cathc문으로 처리하기.

 

 

 

2. finally 블럭

- finally 블럭은 try-catch문과 함께 예외의 발생여부에 상관없이 실행되어야 할 코드를 포함시킬

목적으로 사용된다.

 

- 순서는 다음과 같다

(1) try -> catch -> finally

(2) try -> finally

 

- finally는 try에서도 꼭 실행되어야 되는 구문과 catch에서도 꼭 실행되어야 되는 구문을 넣어야 할 때

활용된다.

 

 

 

3. 이해를 돕기 위한 예제(1)

: 2번 내용을 이해하기 위한 예제이다.

 

 

( 중간에 try 도중 return; 사용하여 다시 main으로 돌아가는데 finally가 먼저 실행이 되었다. )

 

 

 

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

- 이 구문은 입출력(I/O)과 관련된 클래스를 사용할 때 유용하다.

 

- 입출력(I/O) 때 FileInputStream 클래스 등 많이 사용할 것인데, 대부분 사용 후

꼭 close() 메서드를 사용하여 자원을 반납을 위해 닫아줘야 한다.

 

- 그러나 close() 메서드에서도 예외가 발생할 수 있으므로, try-catch문으로 써 주면

아래의 코드처럼 생성될 것이다.

( 이렇게 되면 너무 복잡하고, 별로 좋지 않다. )

 

( 그래서 등장하게 된 것이 try-with-resources이다. )

( 이 구문을 사용한다면, 에외가 발생하더라도 자원을 자동으로 반납할 수 있다. )

( 아래의 코드를 보자. )

 

( try의 괄호 안에 넣어서 객체를 생성하게 되면, try을 벗어나는 순간 자동으로 close()를 수행하게 해준다. )

( 이렇게 해줄려면, AutoCloseable 인터페이스 구현되어야 한다. )

 

 

( 그러나 이렇게 하더라도 close에서 예외가 발생할 수 있다. )

( 아래의 예제를 통해 확인해보자. )

 

 

 

5. 이해를 돕기 위한 예제(2)

: 4번 내용을 이해하기 위한 예제이다.

 

( 첫 번째 try-catch 문에서는 close()만 호출했다. )

 

( 두 번째 try-catch 문에서는 exception()과 close()를 호출했는데, 먼저 WorkException에 대한 예외가

나오고, 그 다음으로 CloseException에 대한 예외가 나오는데 억제된(Suppressed)이 머리말과 함께

출력되었다.)

( 억제된 이유는 동시에 예외가 발생할 수는 없기 때문이다. )

( 그래서 억제된 예외에 대한 정보는 WorkException에 저장된다. )

 

 

 

6. 사용자 정의 예외 만들기

- 보통은 아래의 코드처럼 만든다.

 

 

- 또한 아래의 코드처럼 멤버를 추가해서 다양하게 메시지를 전달할 수 있다.

 

 

 

7. 이해를 돕기 위한 예제(3)

: 6번 내용을 응용한 예제이다.

 

 

( 하나의 catch가 잡힌다면, 뒤에 catch문은 무시하고 정상적으로 프로그램을 종료시킨다. )

 

 

 

 

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

- 호출한 메서드에서 try-catch문과 호출된 메서드에서 try-catch문을 양 쪽 모두에서

실행되도록 하기 위해서 예외 되던지기를 한다.

 

 

- 방법은 간단하다. 아래의 코드를 보자.

 

- 또한 아래의 코드처럼 return문이 있는 경우도 사용된다.

( 만약 return문이 catch 안에 없다면, 오류가 난다. )

( 또한 catch는 return문 대신 예외 던지기도 가능하다. )

 

( 만약 fianlly에 return100이 활성화 된다면, 출력문은 100이 나온다. )

 

 

 

9. 연결된 예외(chained exception)

- 예를 들어, 예외 A가 예외 B를 발생시켰다면, A를 B의 '원인 예외"라고 한다.

 

- 아래의 코드는 예외를 catch를 통해 등록하고 있다.

 

( 위 코드를 보면 발생한 예외를 바로 처리하지 않고 예외를 initCause() 메서드를 통해 SpaceException

InstallException의 원인 예외로 등록하여 다시 예외를 발생시키고 있다. )

 

( *initCause()Exception 클래스의 조상인 Throwable 클래스에 정의되어 있기 때문에 모든 예외에서

사용가능하다. )

( 또한 getCause()도 정의되어 있으며, 원인 예외를 반환한다. )

 

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

 

( 아래의 코드를 보자. )

 

( 현재 SpaceException MemoryException 클래스가 InstallException을 상속받고 있다. )

 

( 이렇게 작성하게 되면 실제로 어떤 예외가 발생했는지 알 수 없다는 문제가 발생한다. )

( 또한 상속관계를 변경해야 한다는 것도 부담이 될 수 있다. )

 

( 그래서 생각한 것이 예외가 원인 예외를 포함시킬 수 있게 한 것이다. )

( 그렇게 되면 굳이 상속관계가 될 필요가 없다. )

 

 

 

- 또한 다른 이유로는 연결된 예외는 checked 예외를 unchecked 예외로 바꿀 수 있도록 하기 위해서이다.

 

( 위 아래의 코드를 비교해보자. )

 

( RuntimeException으로 변경해줌으로써, uncheked로 변경되었다. )

( 그러므로, throws에서 MomoryException을 쓸 필요가 없어졌다. )

 

( 이때, RuntimeException(Throwable cause)의 형태로 받아서 위에서 initCause()대신 사용했다. )

 

 

 

 

10. 이해를 돕기 위한 예제(4)

: 예외처리(1) + 예외처리(2)의 통합 예제이다.

 

public class Exercise001 {
	public static void main(String[] args) {
		try {
			install();
		} catch(InstallException e) {
			e.printStackTrace();
		} catch(Exception e) {
			e.printStackTrace();		
		}	
	}
	static void install() throws InstallException {
		try {
			startInstall();		// 프로그램 설치에 필요한 준비를 한다.
			copyFiles();		// 파일들을 복사한다. 
		} catch (SpaceException e)	{
			InstallException ie = new InstallException("설치중 예외발생");
			ie.initCause(e);
			throw ie;
		} catch (MemoryException me) {
			InstallException ie = new InstallException("설치중 예외발생");
			ie.initCause(me);
			throw ie;
		} finally {
			deleteTempFiles();	// 프로그램 설치에 사용된 임시파일들을 삭제한다.
		} // try의 끝
	}

static void startInstall() throws SpaceException, MemoryException { 
	if(!enoughSpace()) { 		// 충분한 설치 공간이 없으면...
		throw new SpaceException("설치할 공간이 부족합니다.");
	}

	if (!enoughMemory()) {		// 충분한 메모리가 없으면...
		throw new MemoryException("메모리가 부족합니다.");
//		throw new RuntimeException(new MemoryException("메모리가 부족합니다."));
	}
} // startInstall메서드의 끝

   static void copyFiles() { /* 파일들을 복사하는 코드를 적는다. */ }
   static void deleteTempFiles() { /* 임시파일들을 삭제하는 코드를 적는다.*/}
   
   static boolean enoughSpace()   {
		// 설치하는데 필요한 공간이 있는지 확인하는 코드를 적는다.
		return false;
   }
   static boolean enoughMemory() {
		// 설치하는데 필요한 메모리공간이 있는지 확인하는 코드를 적는다.
		return true;
   }
} // ExceptionTest클래스의 끝

class InstallException extends Exception {
	InstallException(String msg) {
	   super(msg);	
   }
} 

class SpaceException extends Exception {
	SpaceException(String msg) {
	   super(msg);	
   }
} 

class MemoryException extends Exception {
	MemoryException(String msg) {
	   super(msg);	
   }
}

( initCause()를 사용해서 InstallException의 상속관계를 없애고, 포함시킬 수 있다.)

 

 

( 또한 checked 예외를 unchecked로 변경시키면, 아래의 코드처럼 변경하면 된다. )