기록하자..
예외처리 | 백기선님 LIVE-STUDY 본문
예외 처리
학습할 것
- 자바에서 예외 처리 방법 (try, catch, throw, throws, finally)
- 자바가 제공하는 예외 계층 구조
- Exception과 Error의 차이는?
- RuntimeException과 RE가 아닌 것의 차이는?
- 커스텀한 예외 만드는 방법
Exception과 Error의 차이는?
Exception과 Error 모두 Throwable 클래스의 하위 클래스이다.
에러는 JVM 실행에 문제가 생겼다는 것이다. 보통 하드웨어의 오동작 또는 고장으로 인해 응용프로그램 실행 오류가 발생하는 것을 말한다. 개발자는 이런 에러에 대처할 방법이 없다.
예외는 예외가 발생하면 프로그램이 종료되는 것은 에러와 동일하지만, 개발자가 예외 처리를 통해 프로그램을 종료하지 않고 정상 실행 상태를 유지할 수 있다는 것에 차이가 있다.
자바가 제공하는 예외 계층 구조
자바에서 예외란 사용자의 잘못된 조작 또는 잘못된 코딩으로 인해 발생하는 프로그램 오류를 말한다. 자바에서 예외가 발생하면 프로그램이 종료되지만, 예외 처리를 통해 프로그램을 종료하지 않고 정상 실행 상태가 되도록 유지할 수 있다.
예외에는 두 가지 종류가 있다.
- 일반 예외(Exception)
- 실행 예외(Runtime Exception)
일반 예외는 컴파일러 체크 예외라고도 한다. 체크 예외은 자바 소스를 컴파일하는 과정에서 예외 처리 코드가 필요한지 검사한다. 만약 예외 처리 코드가 없다면 컴파일 오류가 발생한다.
반면, 실행 예외는 컴파일하는 과정에서 예외 처리 코드를 검사하지 않는 코드를 말한다.
두 가지 예외 모두 컴파일 시 예외 처리를 확인하는 차이일 뿐, 모두 예외 처리가 필요하다. 자바에서 제공하는 예외는 클래스로 관리되는데, JVM은 실행도중 예외가 발생하면 해당 예외 클래스로 객체를 생성한다. 자바의 예외 계층 구조는 다음과 같다.

자바의 모든 예외는 java.lang.Exception에 저장되어 있다.
자바에서 예외 처리 방법 (try, catch, throw, throws, finally)
프로그램에서 예외가 발생했을 때 갑작스러운 종료를 막고, 정상 실행을 유지할 수 있도록 처리하는 코드를 예외 처리 코드라 한다.
일반 예외는 컴파일러가 소스 코드를 컴파일 할 때 코드를 작성하도록 요구하지만, 실행 예외는 그렇지 않다. 그렇다면 이제 자바의 예외 처리 방법에 대해 알아보자.
try ~ catch ~ finally
try {
System.out.println("예외 발생");
System.out.println(12/0);
} catch (Exception e) {
System.out.println("Arithmetic Exception 발생");
} finally {
System.out.println("이 부분은 항상 실행합니다.");
}
try 블록에는 예외 발생 코드가 위치하고, 예외가 발생한다면 즉시 실행을 멈추고 catch 블록으로 이동해 예외 처리 코드를 실행하게 된다. finally 블록은 예외 발생 여부에 상관없이 항상 실행되게 된다.
try블록에서 예외가 발생한다면 이 예외를 처리할 catch블럭이 있는지를 먼저 찾게 된다. catch 블럭의 예외와 실제 발생한 예외를 instanceof 연산자를 통해 검사를 하고 true라면 catch 블럭을 실행하고 false라면 예외는 처리하지 않게 된다. 그렇기에 하나의 try블럭에 여러개의 catch 블럭을 작성할 수 있게 된다.
다중 catch
try {
System.out.println("예외 발생");
System.out.println(12/0);
} catch (NullPointerException e1) {
System.out.println("Null Pointer Exception 발생");
} catch (ArithmeticException e2) {
System.out.println("Arithmetic Exception 발생");
} catch (Exception e3) {
System.out.println("Exception!");
}
다중 catch문을 사용할 때 주의할 점이 있다. 예외의 상속 구조를 고려해야 한다는 점이다.
위의 다중 catch문에서 Exception은 모든 예외의 상위 클래스이다. 만약 해당 catch문이 다른 catch문보다 먼저 나오게 된다면 ArithmeticException 블록의 예외 처리 코드는 발생하지 않았을 것이다.
(사실 컴파일러 단에서 컴파일조차 되지 않는다.)
multi catch
그래서 JAVA 7부터 여러 개의 Exception을 다루기 위해 multi catch문이 등장했다.
try {
System.out.println("예외 발생");
System.out.println(12/0);
} catch (NullPointerException | ArithmeticException e) {
System.out.println("Null Pointer Exception 또는 Arithmetic Exception 발생");
} catch (Exception e3) {
System.out.println("Exception!");
}
try ~ catch ~ resource
JAVA7 부터 사용할 수 있는 trycatchresource 이다. 예외가 발생했을 떄 resource를 자동으로 close()해준다. 사용 로직을 작성할 때 객체는 AutoCloseable 인터페이스를 구현한 객체여야 한다.
public interface AutoCloseable {
void close() throws Exception;
}
try {
FileInputStream fis = new FileInputStream("없는 파일.txt");
} catch(IOException e) {
e.printStackTrace();
}
이렇게 try 블럭에서 예외발생시 바로 fis.close()를 호출하고 catch로 예외 처리 코드를 동작시키게 된다.try 블럭에서 close()가 필요한 코드가 있으면 바이트코드를 조작하여 close()를 생성하기 때문에 이와 같은 일이 가능하다.
printStackTrace()를 통해 예외가 발생하기 까지의 이력을 출력해준다.
이는 메서드가 실행되면 JVM 스택영역에 Stack Frame이 쌓이게 된다.
Stack 영역은 Stack Frame을 저장하는 Stack이며 JVM은 이 Stack Frame을 push하고 pop하게 된다.
예외가 발생하게 되면 예외의 내용을 보여주는 Stack Trace의 각 라인은 Stack Frame을 표현하는 것이며, Stack 영역에 쌓여 있는 Stack Frame들을 pop 하며 출력하는 것이다.
출처
throw
throw 키워드를 통해 예외를 강제로 발생시킬 수 있다.
throw new XXXException();
throw new XXXExcption("message");
다음과 같이 예외 객체를 생성해 예외를 발생시킬 수 있다.
public static void main(String[] args) {
String username = "Ex";
try {
if (username.equals("Ex")) {
throw new IllegalArgumentException();
}
} catch (IllegalArgumentException e) {
System.out.println(e.getMessage());
}
}
throws
이렇게 발생한 예외를 try~catch 블록으로 처리하지 않고 떠넘길 수 있는데 이때 사용하는 것이 throws 이다. throws 키워드는 메서드 선언부 끝에 작성되어 메서드에서 처리하지 않은 예외를 호출한 곳으로 떠넘기는 역할을 한다.
throws 키워드가 붙어있는 메서드는 반드시 try 블럭내에서 호출되어야 한다. 그렇지 않으면 또 throws 키워드를 이용해 다시 예외를 떠넘겨야 한다.
public static void main(String[] args) {
try {
errorMethod();
} catch (ClassNotFoundException e) {
System.out.println("클래스가 존재하지 않습니다.");
}
}
public static void errorMethod() throws ClassNotFoundException {
Class clazz = Class.forName("java.lang.String2");
}
main 메서드에서도 throws를 통해 예외를 떠넘길 수 있다. 결국 JVM이 최종적으로 예외 처리를 하게 된다.JVM은 예외의 내용을 콘솔에 출력하는 것으로 예외 처리를 한다.
커스텀한 예외 만드는 방법
프로그램을 개발하다보면 자바에서 제공하는 예외만으로는 다양한 예외를 표현할 수 없을 때가 있다. 이럴 때 사용자가 직접 정의해서 예외를 정의할 수 있는데 이를 애플리케이션 예외라고 한다.
사용자 정의 예외는 컴파일러가 체크하는 예외인 일반 예외로도 만들 수 있고, 실행 예외로도 만들 수 있다. 일반 예외의 경우 Exception을 상속해서, 실행 예외의 경우 RuntimeException을 상속해서 정의한다.
public class CustomException extends RuntimeException {
public CustomException() {}
public CustomException(String message) {
super(message);
}
}
이런 커스텀한 예외도 예외 클래스이름은 ~Exception으로 끝나며 네이밍 컨벤션을 지켜주는 것이 좋다.
참고 자료
'자바' 카테고리의 다른 글
| Enum | 백기선님 LIVE-STUDY (0) | 2022.08.06 |
|---|---|
| 멀티스레드 프로그래밍 | 백기선님 LIVE-STUDY (0) | 2022.05.31 |
| 인터페이스 | 백기선님 LIVE-STUDY (0) | 2022.05.31 |
| 패키지 | 백기선님 LIVE-STUDY (0) | 2022.05.31 |
| 상속 | 백기선님 LIVE-STUDY (0) | 2022.05.02 |
