Exception Handling
프로그램이 실행되는 동안 발생하는 예외적인 상황(오류)을 관리하고 처리하는 메커니즘이다.
- 컴파일 에러: 소스 코드가 자바 문법에 맞지 않아 컴파일 자체가 실패하는 경우
- 런타임 에러: 컴파일은 성공했으나, 프로그램 실행 중에 발생하는 오류
- 논리적 에러: 컴파일과 실행은 모두 되지만, 의도와 다르게 동작하는 경우
Java의 예외 클래스는 계층 구조를 통해 다양한 종류의 예외를 체계적으로 관리할 수 있으며, 개발자는 필요한 예외를 선택하여 적절히 처리할 수 있다.
예외 클래스 계층 구조
Section titled “예외 클래스 계층 구조”자바의 모든 예외 클래스는 java.lang.Throwable 클래스를 상속받고, Throwable은 다시 Error와 Exception으로 나뉜다.
Error- 시스템 레벨에서 발생하는 심각한 오류(예:
OutOfMemoryError,StackOverflowError) - 프로그램 코드로 수습할 수 없는 경우가 대부분
- 개발자가
try-catch로 처리하는 것을 고려하지 않음
- 시스템 레벨에서 발생하는 심각한 오류(예:
Exception- 프로그램 코드에서 수습(처리)할 수 있는 비교적 미약한 오류
- 개발자가
try-catch등을 이용해 적극적으로 처리해야 하는 대상
Exception은 다시 두 가지로 나뉜다.
- Checked Exception
Exception을 상속하지만RuntimeException을 상속하지 않는 클래스(예:IOException,SQLException)- 컴파일러가 예외 처리를 강제
try-catch로 감싸거나,throws키워드로 호출한 메서드에 처리를 위임 필요
- Unchecked Exception
RuntimeException클래스와 그 자손들을 의미(예:NullPointerException,ArrayIndexOutOfBoundsException)- 컴파일러가 예외 처리를 강제하지 않음
- 주로 프로그래머의 논리적 실수로 인해 발생하며, 예외를 잡기보다는 원인 코드를 수정하는 것이 권장

try-catch-finally
Section titled “try-catch-finally”예외 처리를 위한 구문으로, 각 블록은 다음과 같은 역할을 한다.
try: 예외가 발생할 수 있는 코드catch: 예외가 발생했을 때 수행할 코드- 프로그램의 비정상 종료를 막고, 정상적인 실행상태를 유지
- 여러 개의
catch블록 작성 가능 - 여러 예외를 하나의
catch블록에서 처리 가능 catch블록의 순서 중요 (하위 타입 예외를 먼저 명시 후 상위 타입 예외 명시)
finally: 예외 발생 여부와 상관없이 항상 수행되어야 하는 코드- 주로 자원 해제 코드 작성
try블록에서return문을 만나도finally블록은 실행된 후 메서드가 종료System.exit()가 호출되거나 JVM 자체가 다운되는 등 극히 예외적인 상황을 제외하고는 항상 실행 보장
class Example {
public static void main(String[] args) { try { // 예외가 발생할 수 있는 코드 } catch (Exception1 e1) { // Exception1 예외가 발생했을 때 처리하는 코드 } catch (Exception2 e2) { // Exception2 예외가 발생했을 때 처리하는 코드 } catch (Exception3 e3) { // Exception3 예외가 발생했을 때 처리하는 코드 } catch (ExceptionA | ExceptionB e5) { // ExceptionA, ExceptionB 예외가 발생했을 때 처리하는 코드 } finally { // 예외 발생 여부와 상관없이 항상 수행되어야 하는 코드 } // 발생한 예외와 일치하는 catch 블록이 없으면 예외는 처리되지 않는다. }}try-with-resources
Section titled “try-with-resources”finally 블록에서 자원을 해제하는 것 대신, 자바 7부터 try-with-resources 구문이 도입되어 자원 해제를 자동으로 처리할 수 있게 되었다.
AutoCloseable인터페이스를 구현한 클래스만 사용 가능try괄호()안에 자원 객체를 선언하면,try블록이 종료될 때 자동으로close()메서드가 호출
public static void main(String[] args) { try (FileInputStream fis = new FileInputStream("file.txt")) { // 파일 읽기 로직 } catch (IOException e) { // 예외 처리 } // fis.close()가 자동으로 호출됨}예외 발생 정보
Section titled “예외 발생 정보”catch 블록에서 잡은 예외 객체 e는 예외에 대한 정보를 담고 있다.
e.printStackTrace(): 예외 발생 당시의 호출 스택(Call Stack)과 예외 메시지를 화면(System.err)에 출력- 개발 및 디버깅 시에는 유용
- 대신 로깅 프레임워크(SLF4J, Logback 등)를 사용하여
logger.error("에러 메시지", e)형태로 로그를 남기는 것을 권장
e.getMessage(): 발생한 예외 객체에 저장된 메시지를 문자열로 반환
class Example {
public static void main(String[] args) { try { int result = 3 / 0; System.out.println(result); } catch (ArithmeticException e) { e.printStackTrace(); // java.lang.ArithmeticException: / by zero, at Example.main(Example.java:5) System.out.println(e.getMessage()); // / by zero } }}메서드에 예외 선언
Section titled “메서드에 예외 선언”메서드 선언부에 throws 키워드를 사용하여, 해당 메서드가 호출 도중 발생시킬 수 있는 Checked Exception을 명시한다.
- 이 메서드를 호출하는 쪽(Caller)에게 예외 처리를 강제
- 호출자는 해당 예외를
try-catch로 처리하거나, 다시throws로 상위 메서드에 처리를 위임 필요
class Exception {
public static void main(String[] args) { try { // 호출자는 try-catch로 처리 File f = createFile(args[0]); System.out.println(f.getName() + "파일이 성공적으로 생성되었습니다."); } catch (Exception e) { System.out.println(e.getMessage() + " 다시 입력해 주시기 바랍니다."); } }
// createFile 메서드는 Exception(Checked Exception)을 발생시킬 수 있음을 선언 static File createFile(String fileName) throws Exception { if (fileName == null || fileName.equals("")) { throw new Exception("파일이름이 유효하지 않습니다."); } File f = new File(fileName); f.createNewFile(); return f; }}사용 자정의 예외
Section titled “사용 자정의 예외”기존에 정의된 예외 클래스 외에, 애플리케이션의 특정 비즈니스 로직에 맞는 예외를 직접 정의할 수 있다.
Exception또는RuntimeException을 상속받아생성Checked Exception으로 만들어 호출자에게 처리를 강제하려면Exception상속Unchecked Exception으로 만들어 처리를 강제하지 않으려면RuntimeException상속- 예외 상황에 대한 구체적인 정보(예: 에러 코드, 관련 데이터)를 멤버 변수 추가 가능
class MyException extends Exception { // Exception 또는 RuntimeException 상속
private final int ERR_CODE;
MyException(String msg, int errCode) { super(msg); ERR_CODE = errCode; }
MyException(String msg) { this(msg, 100); }
public int getErrorCode() { return ERR_CODE; }}예외 되던지기
Section titled “예외 되던지기”catch 블록에서 예외를 처리한 후, 의도적으로 다시 예외를 발생시켜 상위 호출 메서드로 예외를 전달하는 기법이다.
- 예외 로깅 및 재전파
- 현재 위치에서 예외를 로그로 기록하되, 처리는 상위 호출자가 하도록 예외를 다시 전달
- 로직 수행 및 전파
- 예외가 발생했을 때, 수행해야하는 로직 처리
- 후처리 완료 후, 예외가 발생했음을 상위 호출자에게도 알리기 위해 예외를 다시 전달
- 예외 변환 (Exception Translation)
SQLException과 같은 저수준(low-level)의 특정 구현 예외를,DataAccessException과 같은 추상화된 사용자 정의 예외로 변환- 이때
throw new DataAccessException(e)처럼 생성자에 원본 예외(e)를 넘겨주어야(예외 체이닝), 원본 스택 트레이스가 소실되지 않음
class ExceptionEx3 {
public static void main(String[] args) { try { method1(); } catch (Exception e) { System.out.println("main 메서드에서 최종 처리"); // e.printStackTrace(); // 원인 예외(method1의 Exception) 확인 가능 } }
static void method1() throws Exception { try { // 고유한 예외 발생 throw new Exception("최초 발생 예외"); } catch (Exception e) { System.out.println("method1에서 예외 로깅"); // 1. 예외를 그대로 다시 던지기 // throw e; // 2. 예외를 감싸서(wrapping) 던지기 (예외 변환) // 원인(cause) 예외 e를 포함시켜 예외 체이닝(Exception Chaining)을 수행한다. throw new RuntimeException("method1 처리 중 오류", e); } }}