Exception
Java Exception
Checked Exception은 예외처리를 컴파일러가 강제하기때문에 코드가 복잡해져 코드의 가독성이 떨어질 수 있다. 그래서 만약 상속해서 쓰게 된다면 RuntimeException
을 상속하는 것이 좋다.
Exception 예외 처리
신뢰성 있는 프로그램은 예외를 잡고 처리할 수 있어야 한다.
예외 처리는 간단히 말해서 ‘프로그램을 위한 보험 처리’입니다.
Error와 Exception는 다릅니다.
- Error는 JVM의 동작이상 같이 개발자가 어떻게 조치할 수 없는 수준
- 복구 불가능
- XXXError
- ex. stackoverflow
- Exception은 JVM은 정상적으로 동작하고 있어 다른 방식으로 처리하는 것.
- 복구 가능
- XXXException, RuntimeException
- Ex. 와이파이를 못 찾을 때 자동으로 다시 시도한다
- 우리가 코드로 짤 수 있다.
Exception에는 두 가지가 있습니다.
- Checked Exception : Java 내부에서 예외 처리를 하지 않으면 컴파일을 허락하지 않음. sw적으로 복구 가능
- Unchecked Exception : 컴파일 시에 체크되지 않고, Runtime에 발생하는 예외. 예를 들어, 분모가 0인 경우.
- 가장 흔하게 보는 예외들
NullPointerException
: 인스턴스의 리모컨을 누르지만, 실제 인스턴스는 없을 떄ArrayIndexOutOfBoundsException
: 배열의 범위를 벗어날 때NumberFormatException
: 숫자로 바꿀 수 없는 데이터를 숫자로 바꿀 때
- 예외는 상속 구조로 처리됩니다.
- 가장 상위에 있는 것은 Throwable이라는 클래스이고 하위 자식으로 Exception과 Error가 있습니다.
- 부모 클래스인 java.lang.Exception 클래스만 알아도 괜찮습니다.
- 그래서 이런 형식으로 처리할 수 있습니다.
Exception e = new NullPointerException();
- Exception Class에서 알아야 하는 메소드는?
- printStackTrace()
- 발생한 Exception의 출처를 메모리 상에서 추적하면서 결과를 알려줍니다. 즉, 예외가 발생한 위치를 정확히 화면에 출력해줍니다. 다만 리턴 타입이 void라서 보이는 메세지를 다른 방식으로 응용할 수 없습니다.
- getMessage()
- printStackTrace()가 상세한 내용이라면 getMessage()는 한 줄로 요약된 요약 메세지를 String으로 반환해줄 수 있습니다.
- getStackTrace()
- StatTraceElement라는 인스턴스들의 배열로 printStackTrace()의 결과를 인스턴스화 시켜줍니다. 즉, 별도로 문자열로 만들 때 씁니다.
- printStackTrace()
Try~catch
- 예외를 직접 처리하는 구문입니다.
try{ }catch(Exception e){ }
- 예외 처리의 순서
- 예외 처리를 하고 싶은 코드의 부분을 찾는데 라인 단위가 아니고 로직 단위입니다.
- Try 블록 안에 정상적으로 실행될 코드를 넣는데 try 블록 안에서 선언한 변수는 블록 바깥쪽에서 사용될 수 없으니 주의하세요.
- Catch에는 대안 흐름을 넣습니다.
- 예외가 발생했을 때 어느 부분에서 문제가 생겼는지 (e.printStackTrace())
- 잘못된 결과를 대신해서 나머지 코드에 영향이 없게끔 대안 결과를 제시하는 경우
- 잘못된 상황에서 다른 메소드를 호출하거나 결과를 기록하는 경우
- 중요한 건 try 블록을 크게 했을 경우에는 중간에 예외가 생겼을 때 블록 안에서의 밑에 부분은 실행하지 않고 바로 catch로 넘어가버립니다.
- Catch(Exception e)는 만병통치약입니다. 왜냐면 프로그램 실행시에 예외가 Exception e의 하위 클래스일 가능성이 매우 높기 때문입니다.
- 발생하는 예외마다 다르게 대처해야 한다면 하나의 try에 catch문을 여러 개 써서 처리하면 됩니다. 또한 try catch가 연속적으로 nesting 될 수 있다.
- 마지막으로
finally
는 어떤 상황에서도 반드시 실행된다는 의미입니다.- 보통 연결을 종료할 때 finally를 많이 씁니다. 예외가 발생하든, 하지 않든, 외부 리소스들과 연결을 종료해줘야 하기 때문입니다.
- 무조건적으로 실행된다.
- catch에서 throw를 던져도 실행하고 던지고, return으로 종료하더라도 먼저 실행하고 return이 됨.
// 상위타입이 위에 있기 때문에 아래 코드는 실행되지 않는 데드코드가 된다.
catch(Exception e) {}
catch(FileNotFoundException e){}
내가 만든 커스텀 예외는 클래스를 만든 후, 상위 타입을 extends해서 만든다.
Throws
- 예외 처리를 현재 메소드가 직접 처리하지 않고 호출한 곳에다가 예외의 발생 여부를 알려주는 것입니다. 즉, 책임을 남에게 미루는 것이죠.
Public void doA() throws NullPointerException{…}
- 이 메소드를 실행하다가 NullPointerException이 발생하면 처리하지 않고 책임을 미루겠다는 선언입니다.
Public void doA() throws Exception{…}
- 이렇게 하게 되면 모든 예외 발생 시 처리하지 않고 책임을 전가하는 것입니다.
- 예외는 JVM까지 예외를 던질 수 있습니다.
- 모든 곳에서 예외를 던지면 그냥 예외 처리를 내버려 둡니다.
- 현재 메소드에서 예외가 일어났다면 throws 하여 현재 메소드를 호출한 메소드로 가게 됩니다.
- 예외 처리를 하지 않으면 컴파일러가 컴파일을 허락하지 않습니다. 예외 처리는 반드시 처리하든가, 던지든가 해야합니다.
- 그렇다면 어느 경우에 처리하고 어느 경우에 던져야 할까요?
- 해당 메소드가 다른 메소드와 연결이 되어서 작업되는 메소드라면 호출한 쪽에 던져서 예외의 발생 여부를 알리는 게 좋습니다.
- 만약에 단독으로 실행되는 메소드라면 try~catch를 고려합니다.
- 예외의 용어들
- Throws
- 메소드의 선언부에서 어떤 종류의 예외들이 던져질 것인지를 명시할 때 사용한다.
- Throw
- 직접 예외 인스턴스를 발생시켜서 코드 바깥으로 예외를 던질 때 사용합니다.
- Throws
- Throw new~를 이용해서 예외 인스턴스를 만들어서 바로 던질 수 있습니다.
Public void doJob() throws JobException{
try{
init();
System.out.println("실제 작업 내용입니다.");
finish();
}catch(Exception e){
e.printStackTrace();
Throw new JobException(e.getMessage());
}
}
- 즉, try 부분에서 만약 예외 상황이 발생하면 printStackTrace()를 실행한 후에 새로운 JobException 인스턴스를 만들어서 throw 키워드를 이용해서 코드 바깥쪽으로 던집니다. 이렇게 되면 외부에서는 doJob()은 호출했을 때 실제로 어떤 예외가 발생했는지 알 필요가 없습니다.