예외 처리

김설영·2022년 4월 3일
0

프로그램 오류

  • 컴파일 에러(compile-time error) : 컴파일 시 발생하는 에러

  • 런타임 에러(runtime error) : 실행 시 발생하는 에러 (프로그램 종료)

    • 에러(error) : 프로그램 코드에 의해서 수습될 수 없는 심각한 오류
      ex > OOME, Out Of Memory Error (메모리 부족으로 인한 종료)
    • 예외(exception) : 프로그램 코드에 의해서 수습될 수 있는 다소 미약한 오류
  • 논리적 에러(logical error) : 작성 의도와 다르게 동작


예외 처리(Exception Handling)

  • runtime error 에서, error는 어쩔 수 없지만, exception은 처리하자 => 예외 처리

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

  • 목적 : 프로그램의 비정상 종료를 막고, 정상적인 실행 상태를 유지.

  • 상속 계층도

             Object		// 최고 조상
               |
            Trowable	// 클래스. 모든 오류의 조상
               |
          ------------
          |           |
	Exception       Error	// 둘다 Runtime Error
    	|             |
RuntimeException,   OOME(Out Of Memory Error)...
IOException...
  • Exception class + 자손들 : 사용자의 실수와 같은 외적인 요인에 의해 발생하는 예외
    • IOException : 입출력 예외

    • ClassNotFoundException : 클래스가 존재하지 않음 (*.class)

    • ...

    • RuntimeException + 자손들 : 프로그래머의 실수로 발생하는 예외

      • ArithmeticException : 산술 계산 예외 (zero divisison error)
      • ClassCastException : 형변환 예외
      • NullPointerException : 널 포인터 (String str = null;인데 메서드를 호출하려 할 때 발생)
      • ...
      • IndexOutOfBoundsException : 배열 범위 벗어남

try-catch

try {
	// 예외가 발생할 가능성이 있는 문장들
}
catch (Exception1 e1) {
	// Exception1이 발생했을 경우, 이를 처리하기 위한 문장 작성
}
catch (Exception2 e2) {
	// Exception2가 발생했을 경우, 이를 처리하기 위한 문장 작성
}
catch (ExceptionN eN) {
	// ExceptionN이 발생했을 경우, 이를 처리하기 위한 문장 작성
}
  • {} 중괄호 절대 생략 불가

printStackTrace() & getMessage()

  • try 블럭에서 프로그램을 수행하다가, 예외 발생 시 특정 타입의 "예외 객체"가 생성됨 -> 발생한 예외에 대한 정보가 들어있음 (메서드를 갖고 있음) -> 메서드를 통해 정보를 가져올 수 있음

  • printStackTrace() : 예외 발생 당시의 호출 스택(call stack)에 있었던 메서드의 정보와 예외 메세지를 화면에 출력

  • getMessage() : 발생한 예외 클래스의 인스턴스에 저장된 메세지를 얻을 수 있음

멀티 catch 블럭

  • 내용이 같은 catch 블럭을 하나로 합친 것
// ex 1
try {
	// ...
}
catch (ExceptionA | ExceptionB e) {	// ExceptionA와 B가 부모자식관계일 경우, 
									// 멀티 catch 블럭을 사용 할 필요가 없음.
									// instanceof 연산자로 체크하기 때문에, 부모 클래스 하나만 보면 됨
	e.printStackTrace();
}


// ex 2
try {
	//...
}
catch (ExceptionA | ExceptionB e) {
	e.methodA();  // ExceptionA에만 있는 method는 사용하면 안됨
    			  // ExceptionA와 B의 공통 멤버만 사용 가능
    
    if (e instanceof ExceptionA) {
    	ExceptionA e1 = (ExceptionA) e;	// 형변환 필요
        e1.methodA();  // ExceptionA에 선언된 메서드 호출 가능
    }
    else {
    	// if (e instanceof ExceptionB)
    }
}

예외 발생시키기

  • 예외 발생시키기
    1. 연산자 new를 이용해서 발생시키려는 예외 클래스의 객체를 만든다.
    -> Exception e = new Exception("고의로 발생시켰음");
    2. 키워드 throw를 이용해서 예외를 발생시킨다.
    -> throw e;

checked 예외, unchecked 예외

  • checked 예외 : 컴파일러가 예외 처리 여부를 체크 (예외 처리 필수)
    ex) Exception과 그 자손 -> 컴파일 자체가 안됨
  • unchecked 예외 : 컴파일러가 예외 처리 여부를 체크하지 않음 (예외 처리 선택)
    ex) RuntimeException과 그 자손 -> 컴파일 가능. try-catch 선택

메서드에 예외 선언하기

  • 예외 처리 방법
    : try-catch문(직접 처리), 예외 선언하기(예외 떠넘기기, 알리기), 은폐

  • 예외 선언
    : 메서드가 호출시 발생가능한 예외를 호출하는 쪽에 알리는 것

  • 오버라이딩
    : 선언부 일치, 접근 제어자 좁게 X, 조상보다 많은 예외 선언 X

// 예외 발생 : throw
// 예외 선언 : throws

// 메서드에 예외 선언 : 메서드 호출 시, 해당 예외들이 발생할 수 있다는 걸 알려줌
void method() throws Exception1, Exception2, ... , ExceptionN {
	// 메서드 내용
}

// method() 내에서 Exception과 그 자손에 대한 예외가 발생 가능함을 알림
void method() throws Exception {	// 모든 예외의 최고조상
	// 메서드 내용
}

// ex 
// 두 예외가 발생할 수 있음을 알림
static void startInstall() throws SpaceException, MemoryException {
	if (!enoughSpace())
    	throw new SpaceException("설치 공간이 부족합니다.");
    if (!enoughMemory())
    	throw new MemoryException("메모리가 부족합니다.");
}

Finally 블럭

  • 예외 발생 여부와 관계 없이, 수행되어야 하는 코드를 넣는다.

  • try 블럭 안에 return 문이 있어서, try 블럭을 벗어 날 때도, finally 블럭은 반드시 실행된다.

try {
	// 예외 발생 가능성이 있는 문장 삽입
}
catch (Exception1 e1) {
	// 예외 처리
}
finally {
	// 예외 발생 여부와 관계 없이, 항상 수행되어야 하는 문장 삽입
    // try-catch 문의 맨 마지막에 위치해야 함
}

사용자 정의 예외 만들기

  • 직접 예외 클래스 정의

  • 상속을 통해 만들며, 조상은 Exception or RuntimeException 중 선택

    • 사용자가 발생시키는 예외 : Exception (try-catch 필수)
    • 프로그래머의 실수로 발생되는 예외 : RuntimeException (선택 처리)
class MyException extends Exception {
	// 에러 코드 값 저장하기 위한 필드 추가
    private final int ERR_CODE;
	
    // 생성자를 넣어주어야 함!
	MyException(String msg, int errCode) {	// 문자열을 매개변수로 받는 생성자
    	super(msg);							// 조상인 Exception 클래스의 생성자 호출
    	ERR_CODE = errCode;
    }
    
    MyException(String msg) {
    	this(msg, 100);
    }
    
    public int getErrCode() {	// 에러코드를 얻는 메서드
    	return ERR_CODE;		// getMessage()와 함께 사용 될 것
    }
}

예외 되던지기

  • exception re-throwing

  • 예외를 처리한 후에 다시 예외를 발생시키는 것

  • 호출한 메서드와 호출된 메서드 양쪽 모두에서 예외처리 하는 것

연결된 예외

  • 한 예외가 다른 예외를 발생시킬 수 있다.

  • 세부적인 예외들을 포괄적인 예외로 감싼다.

  • 예외 A가 예외 B를 발생시키면, A는 B의 원인 예외(cause exception) 라고 한다.

    • Throwable initCause(Throwable cause) : 지정한 예외를 원인 예외로 등록
    • Throwable getCause() : 원인 예외를 반환
  • 사용하는 이유

    1. 여러 예외를 하나로 묶어서 다루기 위해서 (너무 많으면, catch 블럭이 많아짐)

      • 코드 간단화. (세부적인 예외 처리를 메서드 안으로 감춤)
      • 에러 메세지가 자세해짐.
    2. checked 예외(Exception 자손, 필수처리)를 unchecked 예외(RuntimeException의 자손, 선택처리)로 변경하려 할 때

      • 변경 이유 : 예외 처리가 필수라서, try-catch를 꼭 써야함 -> 이것은 제약이기 때문에 불편함
// Throwable : Exception, Error의 조상클래스

// 하나의 예외(Throwable) 안에, 또다른 예외(원인 예외)를 포함시키는 것
public class Trowable implements Serializable {
	...
    // 원인 예외를 저장하기 위한 iv
    private Throwable cause = this;	// 객체 자신(this)을 원인 예외로 등록
	...
    public synchronized Throwable initCause(Throwable cause) {
    	...
        this.cause = cause;  // cause를 원인 예외로 등록
        return this;
    }
    ...
}


// ex : 이유 1번
void install() throws InstallException {	// InstallException만 던지게 했음
	try {
    	startInstall();		// SpaceException 발생
        copyFiles();
    }
    catch (SpaceException e) {	// 예외 연결!
    	InstallException ie = new InstallException("설치 중 예외 발생");	// 예외 생성
        ie.initCause(e);	// InstallException의 원인 예외를 SpaceException으로 지정
        throw ie;			// InstallException을 발생시킨다
    }
    catch (MemoryException me) {  // 예외 연결!
    	...
    }
}

// 실제로 발생한 것은 SpaceException인데, InstallException을 만들어서 그 안에 넣어준것.


// ex : 이유 2번

// Exception 자손 : 예외 선언. 예외 필수 처리는 예외 선언을 꼭 해줘야 함
static void startInstall() throws SpaceException, MemoryException {	

	if (!enoughSpace())
    	throw new SpaceException("설치할 공간이 부족합니다");
    
    if (!enoughMemory())
    	throw new MemoryException("메모리가 부족합니다");
}

// RuntimeException으로 변경. 
// 클래스 자체를 변경 할 경우, 다른 프로그램에 영향을 줄 수 있으므로 연결 예외를 이용한다.
static void startInstall() throws SpaceException {
	if (!enoughSpace())
    	throw new SpaceException("설치할 공간이 부족합니다");
	
    // 필수 처리에서 선택처리로 변경. 예외 선언 안 해도됨!
    if (!enoughMemory())
    	throw new RuntimeException(new MemoryException("메모리가 부족합니다"));	
        // 생성자를 사용한 원인 예외 지정
        // 생성자가 다음과 같이 지정되어 있음. -> RuntimeException(Throwable cause)
}
profile
블로그 이동하였습니당! -> https://kimsy8979.tistory.com/

0개의 댓글