Error and Exception

  • 프로그램이 실행되는 도중에 오류(runtime error)가 발생할 수 있는데, Java는 이를 오류(Error)와 예외(exception)으로 나눴다.

  • 일반적인 OS의 fault랑 exception과 동치는 아니고, 발생시 수습이 안 가능하냐 / 가능하냐로 나눈 것이라고 생각하면 된다.

  • Java의 error과 exception은 ErrorException이라는 class로 정의되어 있다.

RuntimeException

  • Exception class 하위의 여러 class들은 RuntimeException이라는 하위 class에 속하는지, 아닌지로 크게 분류가 가능하다.

  • RuntimeException은 오류를 일으킨 프로그램을 작성한 프로그래머가 원인이 되는 오류들이 해당된다. (ArrayIndexOUtOfBoundsException, ClassCastException, NullPointerException, ArithmeticException 등)

  • RuntimeException에 해당 안되는 Exception class들은 오류를 일으킨 프로그램을 사용한 사용자가 원인이 되는 오류들이 해당된다.
    (FileNotFoundException, ClassNotFoundException, DataFormatException)

try-catch

  • 모든 Exception class는 프로그래머가 try-catch 구문을 통해 처리(handle)하는게 가능하다. 이 코드 자체를 handler이라고도 보통 부른다.

  • 이는 일반적인 OS에서 exception을 handle하는 코드를 exception handler이라고 보통 부르는 것에 유래되었다.

  • 원래 exception은 각 OS가 알아서 처리를 한다. 다만 Java는 JVM에서 작동을 하기 때문에 Linux와 비교했을 때 좀 동작 방식이 다른데, 일단 JVM에도 exception 발생시 기본으로 처리를 해주는 UncaughtExceptionHandler이라는 것이 존재한다. 저 handler이 작동하면 프로그램은 비정상종료가 된다.

  • try-catch 구문을 사용하면 특정 코드에서 발생한 Exception들 중 특정 Exception들을 처리하는게 가능해진다. syntax는 C++의 try-catch랑 매우 유사하다.

try {
	//code
} catch (Exception1 e1) {
	//code for handling Exception1
}
  • 지정된 exception만 handle이 되며, 지정되지 않은 녀석들은 여전히 UncaughtExceptionHandler에서 처리한다.

  • 하나의 코드에 대해 여러개의 catch를 각 Exception에 대해 만드는것도 가능하며, 이 경우 exception 발생시 그 exception의 class에 대응되는 handler 하나만 실행된다.

try {
	//code
} catch (Exception e) {
	try {
    	//another code
    } catch (Exception e) { //error
    	//another code2
    }
}

위 코드는 오류를 일으킨다. 이유는 catch 영역 안의 try-catch문에서 외부 catch가 사용한 exception 참조용 변수랑 같은 이름의 exception 참조 변수를 선언했기 때문이다. 오류를 안일으킬거면 하나는 e1, 다른 하나는 e2와 같은 형식으로 두어야 한다.

  • 만일 모든 부류의 Exception을 처리하는 catch 구문을 만들고 싶으면 catch에서 받는 exception의 class를 Exception으로 설정하면 되며, 특정 부류를 받고 싶으면 해당 부류의 class 이름으로 설정하면 된다.

  • 만일 특정 부류의 Exception을 처리하는 catch 구문들을 몇개 만들고 나머지는 공통된 특정 handler에서 처리하게 만들고 싶으면 다음과 같이 코드를 짜자.

try {
	//code
} catch (Exception1 e) {
 	//handle Exception1
} catch (Exception2 e) {
	//handle Exception2
} catch (Exception e) {
	//handle exceptions that are not Exception1 and Exception2
}

try-catch progress

  • 먼저 try 구문을 수행한다. 이 때 exception이 발생하지 않으면 try-catch문 전체를 빠져나가고 계속 진행

  • try 구문 내 실행 도중 exception 발생시 나머지 try 내 구문을 실행하지 않고 즉시 해당 exception과 일치하는 catch 블럭을 찾는다. 만약 존재하지 않으면 UncaughtExceptionHandler이 작동한다.

  • 만약 존재시 해당 catch 블럭 내 구문을 수행한다. 완료되면 try-catch문 전체를 빠져나가고 계속 진행

  • catch 구문 내에도 try-catch가 존재하는게 가능하며, 이 경우 재귀적으로 처리가 된다.

printStackTrace(), getMessage()

  • exception instance에는 exception에 대한 정보가 들어가 있으며, 위 두 함수를 통해 관련 정보를 얻는게 가능하다.

  • printStackTrace()에는 exception 발생시 call stack에 있던 method 정보와 exception message, exception 자체를 출력한다.

  • getMessage()는 exception message를 따로 얻는게 가능하다.

multi-catch block

  • JDK 1.7 이상만 가능하며, 특정 handler이 여러개의 특정 exception을 처리할 수 있도록 지정하는 구문
try {
	//code
} catch (Exception1 | Exception2 e) {
	//handler
}
  • 주의해야하는게 몇가지 있는데, 먼저 연결된 exception class가 서로 hierarchy 관계이면 컴파일 오류가 발생한다. 저렇게 쓸 이유가 없기 때문.

  • 또 해당 catch문 안에서는 저 오류가 무슨 오류인지 구체적으로 파악하는게 불가능하다. 위의 경우에는 eException1인지 Exception2인지 파악을 못한다는 것이다. 둘 다 가능성이 있기 때문. 이 때문에 기본적으로 e에는 저 둘의 common ancestor class에 해당하는 method만 사용이 가능하다.

  • e가 어떤 Exception class에 해당하는지 파악 후에 해당 class의 method를 사용하는건 가능하다. 어지간하면 그렇게 복잡하게 짤 일이 없다만.

throwing exception

  • OS의 interrupt/trap처럼 exception을 코드에서 만드는 것이 가능하다. 이를 throwing exception이라고 보통 표현한다.

  • 이름이 저런 이유는, exception을 만드는 구문이 throw를 사용하기 때문.

try {
	Exception e = new Exception("LOL");
    throw e;
    throw new Exception("LOL, but never thrown..."); //shortened version
} catch (Exception e) {
	System.out.println("message : " + e.getMessage());
}
  • exception을 throw할 때 처리를 하지 않으면 컴파일 오류를 일으키는 녀석이 있고, 처리를 하지 않아도 컴파일 오류는 일으키지 않지만 runtime error을 일으키는 녀석이 있다. 각각 Exception class와 RuntimeException class에 해당된다.

exception declared on method

void method() throws Exception1 {
	//code
}
  • 이러면 methodException1 Exception1의 자식 class를 throw할 수 있다는 것을 의미한다.

  • 해당 method에서 저 Exception을 처리하기 싫고 상위 함수에게 넘기고 싶을 때 사용된다.

  • 당연히 상위 함수에서도 본인 상위 함수에게 넘길 수도 있다. (...) 이를 재귀적으로 반복하다가 처리가 안된 경우에는 UncaughtExceptionHandler이 결국 발동된다. 이를 방지하려면 어딘가에서 try-catch 구문을 사용해야 한다.

  • exception을 throw하는 method를 사용시 그 exception을 처리하는 try-catch를 꼭 만들거나, 그냥 본인도 그 exception을 throw할 수 있어야 한다. 폭탄 돌리기 강요를 하는 이유는 실수를 방지하기 위해서다.

finally

  • try-catch 구문 뒤에 집어넣는게 가능하다.

  • try가 정상적으로 다 실행되었든, 중간에 중단되어 catch가 실행되어 정상적으로 끝난 경우든 간에 마지막에 실행되는 코드를 집어넣는 공간이다.

try {
	//code
} catch (Exception e) {
	//code when Exception happens in try
} finally {
	//code that alwyas runs after try or catch
}
  • try나 catch에서 return이 존재하고 이에 도달했을시, 만약 finally구문이 있으면 finally내 코드가 실행된 다음에 return을 한다.

try-with-resource

  • JDK 1.7이상에서만 존재

  • try 내에서 만든 특정 resource가, try 내의 Exception이 발생해서 catch로 넘어가게 되었거나 그냥 정상적으로 끝났을 때 자동으로 반환이 될 수 있도록 지원해주는 구문이다.

  • finally를 통해 resource를 반환하는 것이 좋지 않은 이유는 그 안에서 exception 발생시 또 문제가 되어 이게 재귀적으로 문제가 되기 때문이다.

try (FileInputStream fis = new FileInputStream("resource.txt");
	 DataInputSTream dis = new DataInputStream(fis)) {
     //code
} catch (Exception e) {
	//exception code
}
  • 이러면 fis, dis는 try를 벗어나면 자동으로 반환이 된다.

  • 해당 resource 관련 class가 AutoCloseable를 구현했을 때만 가능하다.

그외 잡기술

  • 사용자 정의 Exception을 만드는것도 가능하며, 이 경우 Exception class를 상속받는다. RuntimeException을 상속받아 사용자 정의 RuntimeException을 만드는 것도 가능하다(...)

  • exception rethrowing이라는 것이 있다. 특정 exception에 대한 catch문 내에서 그 exception을 다시 throw하는 것이다. 보통 이러는 경우가 본인과 상위 호출 함수 양측에서 해당 exception에 대해서 처리를 해야 하는 경우에 사용된다. 당연히 이런 경우 Exception 처리를 위한 try-catch와 더불어 해당 method가 그 Exception을 throw한다는 것을 선언부에다가도 지정해야 한다.

  • try-catch가 함수 전체의 마무리 부분을 담당하는데 그 함수가 void가 아닌 값을 반환하는 함수인 경우, try랑 catch 모두 return 값이 필요한것은 자연스럽게 생각할 수 있다. 그러나 해당 함수가 exception을 throw하는게 가능할 경우 catch문에서 return 대신 exception을 throwing하고 마무리 지을 수 있도록 정의하는 것도 가능하다.

  • chained exception이라는 것이 있다. exception이 발생해 catch문으로 넘어갔을 때 그 exception을 다른 exception에 포장하고 또 throw를 하는 것이다. 어떤 공통된 오류를 일으키나 원인이 다른 것을 나타내는 상속관계가 없는 exception들을 상위 exception으로 포장한 다음에 기존 exception을 원인 exception 형태로 포함시킬 때 사용되는 경우가 사례 1. 또 다른 활용법은 check가 강요되는 exception을 check가 강요되지 않는 exception (RuntimeException)으로 바꿀 때 쓸 수 있따.

profile
안 흔하고 싶은 개발자. 관심 분야 : 임베디드/컴퓨터 시스템 및 아키텍처/웹/AI

0개의 댓글

Powered by GraphCDN, the GraphQL CDN