ksouth9

예외처리(exception handling)(3) 본문

Java

예외처리(exception handling)(3)

ksouth9 2022. 3. 18. 02:38

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


try에 리소스(자원)을 선언하고, try 블럭이 끝나면(리소스를 다 사용하고 나면) 자동으로 반납(close)(리소스를 종료)해주는 기능이다.

try(...)문에서 선언된 객체들에 대해서 try가 종료될 때 자동으로 자원을 해제해주는 기능.

주로 입출력에 사용되는 클래스 중에서 사용한 후에 꼭 닫아 줘야 하는 것들이 있다. 그래야 사용했던 리소스가 반환되기 때문이다.

//괄호()안에 두 문장 이상 넣을 경우 ';'로 구분한다.
try (FileInputStream fis = new FileInputStream("score.dat");
     DataInputStream dis = new DataInputStream(fis)) {
     while (true) {
     	score = dis.readInt();
     	System.out.println(scroe);
     	sum += score;
     }
} catch (EOFEXception e) {
	System.out.println("점수의 총합은 " + sum +"입니다.");
} catch (IOException ie) {
	ie.printStackTrace();
}

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

※try블럭의 괄호()안에 변수를 선언하는 것도 가능하며, 선언된 변수는 try블럭 내에서만 사용할 수 있다.

 

AutoCloseable은 try에 선언된 객체가 AutoCloseable을 구현했더라면 Java는 try 구문이 종료될 때 객체의 close() 메서드를 호출해준다. AutoCloseable을 구현한 객체만 close()가 호출된다.

public interface AutoCloseable {
	void close() throws Exception;
}

AutoCloseable 예시

public class AutoCloseableExample {

	public static void main(String[] args) {
		try (CustomResource customResource = new CustomResource();){
            customResource.doSomething();
        } catch (Exception e){
            e.printStackTrace();
        }
    }
}

	class CustomResource implements AutoCloseable {

    public void doSomething(){
        System.out.println("Do something ...");
    }

    @Override 
    public void close() throws Exception {
        System.out.println("CustomResource Closed!");
    }
}//실행결과
Do something ...
CustomResource Closed!

사용자정의 예외 만들기


기존의 정의된 예외 클래스 외에 필요에 따라 프로그래머가 새로운 예외 클래스를 정의하여 사용할 수 있다. 직접 정의하여 사용하는 예외

보통 Exception클래스 또는 RuntimeExcpetion클래스로부터 상속받아 클래스를 만들지만, 필요에 따라서 알맞은 예외 클래스를 선택할 수 있다. 

일반 예외로 선언할 경우 - Exception

실행 예외로 선언할 경우 - RuntimeException

class MyException extends Exception {
	MyException(String msg) {	//문자열을 매개변수로 받는 생성자
    	super(msg);			//조상인 Exception클래스의 생성자를 호출한다.
    }
}

Exception클래스로부터 상속받아서 MyException클래스를 만들었다. 필요하다면, 멤버변수나 메서드를 추가할 수 있다. Exception클래스는 생성 시에 String값을 받아서 메시지로 저장할 수 있다. 사용자정의 예외 클래스에도 메시지를 저장하려면, String을 매개변수로 받는 생성자를 추가해주어야 한다.

public 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;		//이 메서드는 주로 getMessage()와 함께 사용될 것이다.
	}

메시지만 저장하는 것이 아니라 에러코드 값도 저장할 수 있도록 ERR_CODE와 getErrCode()를 MyException클래스의 멤버로 추가했다. 이렇게 함으로써 MyException이 발생했을 때, catch블럭에서 getMessage()와 getErrCode()를 사용해서 에러코드와 메시지를 모두 얻을 수 있을 것이다.

예외던지기(exception re-throwing)


예외를 처리한 후에 인위적으로 다시 발생시키는 방법 '예외 되던지기(exception re-throwing)'

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

그리고 심지어는 단 하나의 예외에 대해서도 예외가 발생한 메서드와 호출한 메서드, 양쪽에서 처리하도록 할 수 있다.

 

먼저 예외가 발생할 가능성이 있는 메서드에서 try-catch문을 사용해서 예외를 처리해주고 catch문에서 필요한 작업을 행한 후에 throw문을 사용해서 예외를 다시 발생시킨다. 다시 발생한 예외는 이 메서드를 호출한 메서드에게 전달되고 호출한 메서드의 try-catch문에서 예외를 또다시 처리한다. 이 방법은 하나의 예외에 대해서 예외가 발생한 메서드와 이를 호출한 메서드 양쪽 모두에서 처리해주어야 할 작업이 있을 때 사용된다. 

주의할 점은 예외가 발생할 메서드에서는 try-catch문을 사용해서 예외처리를 해줌과 동시에 메서드의 선언부에 발생할 예외를 'throws'에 지정해줘야 한다.

public class ExceptionEx17 {

	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메서드 양쪽의 catch블럭이 모두 수행되었다. method1()의 catch블럭에서 예외를 처리하고도 throw문을 통해 다시 예외를 발생 시켰다. 그리고 이 예외를 main메서드에서 한 번 더 처리하였다.

반환값이 있는 return문의 경우, catch블럭에도 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문이 없어도 된다.

static int method1() throws Exception {
	try {
		System.out.println("method1()이 호출되었습니다.");
		return 0;
	} catch(Exception e) {
		e.printStackTrace();
//		return 1;		//catch블럭 내에도 return문이 필요하다.
		throw new Exception();	//return문 대신 예외를 호출한 메서드로 전달.
	} finally {
		System.out.println("method1()의 finally블럭이 실행되었습니다.");
	}
}

연결된 예외(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()로 SpaceException을 InstallException의 원인 예외로 등록한다. 그리고 'throw'로 이 예외를 던진다.

 

initCause()는 Exception클래스의 조상인 Throwable클래스에 정의되어 있기 때문에 모든 예외에서 사용가능하다.

initCause (Throwable cause) - 지정한 예외를 원인 예외로 등록(두 예외를 연결)

getCause() - 원인 예외를 반환

 

원인 예외 사용 이유

  • 발생한 예외를 원인 예외로 등록해서 다시 예외를 발생시키는 이유는 여러가지 예외를 하나의 큰 분류의 예외로 묶어서 다루기 위함이다.
  • checked예외를 unchecked예외로 바꿀 때 사용한다.

'Java' 카테고리의 다른 글

java.lang 패키지(2)  (0) 2022.03.27
java.lang 패키지(1)  (0) 2022.03.20
예외처리(exception handling)(2)  (0) 2022.03.18
예외처리(exception handling)(1)  (0) 2022.03.17
추상클래스와 인터페이스 차이점  (0) 2022.03.17