Java/이론&문법

Java의 Exception

Bubbles 2023. 9. 15. 02:31

예외란?

  • An exception is an event, which occurs during the execution of a program, that disrupts the normal flow of the program's instructions (Oracle Docs) 
  • 예외란 프로그램의 실행동안 발생되는 것으로, 프로그램 명령어들의 정상적인 흐름을 방해하는 것을 의미합니다. 

Try - Catch - Finally

  • 예외가 발생한 경우
    • try 내에서 발생한 이후의 문장들은 실행되지 않음
    • catch 내에 있는 문장은 실행되고, try-catch 문 이후의 내용이 실행 
  • 예외가 발생하지 않은 경우
    • try 문이 쭉 실행되고, catch 내에 있는 문장은 실행되지 않음.
  • finally는 예외 발생 여부와 상관없이 실행되는 문장이다. 
  • try 블록 내에서 선언한 변수는 try 블록 내에서만 사용이 가능하다.  

Catch 블록은 순서를 따진다. 

  • Exception이 예외의 가장 최상위인 만큼, 마지막 catch 블록에 넣어주어야 한다. 
    • 아래 코드는 NullPointerException을 발생시킨다. 
public static void main(String[] args) {
        int[] arr = new int[5];
        try {
            arr = null;
            System.out.println(arr[2]);
        } catch (NullPointerException e) {
            System.out.println("NullPointerException");
        } catch (Exception e) {
            System.out.println("ERROR");
        }
}
  • 만약 아래처럼 순서를 바꾸면 어떨까?
 public static void main(String[] args) {
        int[] arr = new int[5];
        try {
            arr = null;
            System.out.println(arr[2]);
        } catch (Exception e) {
            System.out.println("ArrayIndexOutOfBounds");
        } catch (NullPointerException e) {
            System.out.println("NullPointerException");
        } 
}
  • try문 다음에 오는 catch 블록은 1개 이상 올 수 있으며, 먼저 선언한 catch 블록의 예외 클래스가 다음에 선언된 catch 블록 예외의 부모라면, 자식에 속하는 catch는 실행될 일이 없으므로 컴파일이 되지 않는다.
  • Exception 클래스가 모든 예외의 부모이므로 발생하는 현상.
  • 하나의 try 블록에서 예외가 발생하면 그 예외와 관련이 있는 catch 블록을 찾아서 실행한다. 
  • 발생한 예외와 관련있는 catch 블록이 없다면, 예외가 발생되고 끝나므로 마지막에 Exception 으로 묶어주는 습관을 들일 것. 

 


예외의 종류

 

 

 

Error

  • 자바 프로그램 밖에서 발생한 예외
  • Exception 클래스와의 가장 큰 차이는 프로그램 안에서 발생했는지, 밖에서 발생했는 지의 여부이다. 
  • 또한 프로그램이 멈추어 버리는(프로세스에 영향을 주는) 것이 Error이다. 
    • Exception 클래스는 에러가 아니며, Exception은 스레드에만 영향을 주어 계속 실행이 가능하다. 

 

Runtime Exception ( == Unchecked Exception )

  • 예외가 발생할 것을 미리 감지하지 못했을 때 발생
  • Runtime Exception을 확장한 예외들이다. 
  • 컴파일 시에 체크하지 않기 때문에 unchecked Exception이라고도 부르는 것 

 

Checked Exception

  • Exception 클래스의 하위 클래스 중 Runtime Exception을 제외한 나머지 
  • 컴파일러가 예외 처리를 확인한다. (컴파일 단계에서 잡힌다)
  • 반드시 예외 처리가 되어야 한다.
    • ex) IO의 경우, 무조건 throws IOException 을 main에 달아줘야 하는 등..

 

Throwable 

  • 위 그림에서 보면 알 수 있듯, Exception과 Error의 공통 부모 클래스들은 Throwable이다. 
    • 즉, Exception, Error 모두 상위 클래스인 Throwable로 처리가 가능하다! 
  • Exception, Error의 성격은 다르지만, 동일한 이름의 메소드를 사용하여 처리할 수 있도록 하기 위함이다. 
  • Throwable의 생성자는 아래와 같다. 
public Throwable() {
	fillInStackTrace();
}

public Throwable(String message) {
	fillInStackTrace();
	detailMessage = message;
}

public Throwable(String message, Throwable cause) {
	fillInStackTrace();
	detailMessage = message;
	this.cause = cause;
}

public Throwable(Throwable cause) {
	fillInStackTrace();
	detailMessage = (cause==null ? null : cause.toString());
	this.cause = cause;
}

 

  • Throwable 내부에 작성된 메소드 (이것보다 더 많지만 일부만 발췌)
public String getMessage() // 예외 메시지를 String 형태로 제공받음

public String toString() // getMessage보다 더 자세한 형태의 메시지(예외클래스 이름까지)

public synchronized Throwable fillInStackTrace()
// 코드 내부에서 private native Throwable fillInStackTrace(int dummy) 호출

public void setStackTrace(StackTraceElement[] stackTrace)

public void printStackTrace(PrintWriter s) // 메소드들의 호출관계 출력

 

 


Throws Exception 

 

  • try 문 안에서 throw new Exception ~ 을 통해 예외를 발생시킨다. 
    • 개발자가 예외 클래스의 객체를 생성, try 문 이후는 실행되지 않고 catch 문으로 넘어간다.
    • catch 블록에서 해당하는 예외가 있다면 (동일하거나, 상속 관계에 있는 예외가 있다면) 해당 catch 블록에서 처리 
    • catch 블록에서 해당하는 예외가 없다면 메소드 밖으로 예외를 던진다. (예외가 발생한 메소드를 호출한 메소드로 처리를 위임)
    • 대신, 이 예외가 발생한 메소드가 메소드 밖으로 예외를 던진다면, 걔를 호출한 메소드가 try-catch로 묶어서 처리 해 줘야 한다. 
  • 예시코드
    • throwsException 은 자기를 호출한 main에 예외 처리를 위임
    • main(예외발생 메소드를 호출한 메소드) 메소드는 try-catch로 감싸서 이 예외를 처리한다. (안 그러면 컴파일 안됨)
public static void main(String[] args) {
    try {
    	// ... 
       throwsException(13);
    } catch (Exception e) {
    	// ...
    }
}

public void throwsException(int num) throws Exception {
	if (num > 12) {
    	throw new Exception("~~~");
    }
}

 


Throwable 또는 Exception 상속 받아 예외를 직접 만들수도 있다. 

 

  • 예시 코드 
public class MyException extends Exception {
    public MyException() {
    	super();
    }
    
    public MyException(String Message) {
    	super(message);
    }
}

// 실제 사용한다면
public void throwMyException() throws MyException {
    try {
    	if (num > 12) {
        	throw new MyException("Number is Over");
        }
    } catch (MyException e) {
    	e.printStackTrace();
    }
}

 

  • 추가적인 예외 처리 전략
    • 임의의 예외 클래스를 위처럼 만든다면, try-catch로 묶어줄 필요가 있을 때만 Exception 클래스를 확장한다.
      • Runtime Exception은 해당 메소드를 호출하는 애만 try-catch로 잡고, 메소드 자체는 try-catch 안 사용해도 된다.
    • 일반적으로 실행 시 예외를 처리할 수 있는 경우에는 RuntimeException 클래스를 확장하는 것을 권장한다.
    • catch문 내에는 꼭 로그 처리와 같은 예외 처리를 해줄 것(예외 분석이 어려워지지 않도록)

 

 

기타 참고 자료 

더보기

자바의 신 1권 14장

Oracle Docs