📚 Study/JAVA

[JAVA] 예외처리(try-catch문)

0_ch4n 2022. 5. 2. 19:17
반응형

📌 컴파일

  • 소스코드(.java) → 컴파일 → 클래스 파일(.class)
  • 구문체크, 번역, 최적화, 생략된 코드 추가

 

📌 프로그램 오류

  • 컴파일 에러(compile-time error) : 구문 등의 오류로 실행 전 컴파일 단계에서 잡는 에러
  • 런타임 에러(runtime error) : 컴파일은 정상적으로 됐지만 실행 중 JVM에서 발생하는 에러
    • 에러(error) : 프로그램 코드에 의해서 수습될 수 있는 심각한 오류
    • 예외(exception) : 프로그램 코드에 의해서 수습될 수 있는 다소 미약한 오류
  • 논리적 에러(logical error) : 작성 의도와 다르게 동작하는 에러

오류 클래스의 계층구조

  • Exception 클래스들(checked 예외) : 사용자의 실수와 같은 외적인 요인에 의해 발생하는 예외
  • RuntimeException 클래스들(unchecked 예외) : 프로그래머의 실수로 발생하는 예외

 

📌 예외 처리(exception handling)

  • 정의 : 프로그램 실행 시 발생할 수 있는 예외에 대비한 코드를 작성하는 것
  • 목적 : 프로그램의 비정상적인 종료를 막고 정상적인 실행상태를 유지하는 것
  • 예외처리기(UncaughtExceptionHandler) : 처리하지 못한 예외를 JVM이 받아서 예외의 원인을 화면에 출력하는 것

 

📌 try-catch문

  • try 블럭 내에서 예외가 발생한 경우
    1. 발생한 예외와 일치하는 catch 블럭을 instanceof 연산자로 확인한다 (일치하는 catch 블럭이 없으면 예외는 처리되지 못한다)
    2. 일치하는 catch 블럭 내의 문장과 finally 블럭을 수행 후 전체 try-catch문을 빠져나가 다음 문장을 계속해서 수행한다
    3. 이 때 예외 발생 부분 이후의 문장은 실행되지 않는다
  • try 블럭 내에서 예외가 발생하지 않은 경우
    1. catch 블럭을 거치지 않고 finally 블럭을 거친 후 전체 try-catch 문을 빠져나가서 수행을 계속한다
try {
    //예외가 발생할 가능성이 있는 문장들을 넣는다
} catch (예외클래스 참조변수명) {
    //예외클래스에 해당하는 예외가 발생했을 경우, 이를 처리하기 위한 문장을 적는다
} catch (예외클래스 | 예외클래스 참조변수명) {
    //멀티 catch 블럭으로 jdk1.7부터 생겼다
    //'|' 기호를 이용해서 갯수 제한 없이 중복 코드를 줄일 수 있지만 조상-자손 관계는 불가능하다
    //메서드는 instanceof를 통해 형변환이 가능한지 확인 후 사용한다 (공통 메서드는 상관없다)
} catch (Exception e) {
    //Exception 클래스는 모든 예외 클래스의 조상으로 모든 예외 클래스를 처리 가능
} finally {
    //예외의 발생여부에 관계없이 항상 수행되어야하는 문장들을 넣는다
    //finally 블럭은 try-catch문의 맨 마지막에 위치해야 한다
}

//if문과 달리 try 블럭이나 catch 블럭 내에 포함된 문장이 하나뿐이어도 괄호{}를 생략 불가능
//catch문 내에서는 참조변수 e는 중복 불가능
  • 예외 직접처리 : 메서드 내에서 try-catch문을 통한 예외 처리
  • 예외 떠넘기기 : 호출된 메서드에 throws로 예외 선언하고 호출한 메서드에서 try-catch문을 통한 예외 처리

 

📌 예외 인스턴스의 메서드

  • printStackTrace() : 예외발생 당시의 호출스택(Call Stack)에 있었던 메서드의 정보와 예외 메시지를 화면에 출력
  • getMessage() : 발생한 예외클래스의 인스턴스에 저장된 메시지나 생성자로 저장된 메시지를 얻을 수 있다
e.printStackTrace() 실행결과

java.lang.ArithmeticException: / by zero
    at ExceptionEx8.main(ExceptionEx8.java:7)
    
e.getMessage() 실행결과

/ by Zero

 

📌 예외 발생시키기(throw)

  • checked 예외는 예외 처리가 필수고 unchecked 예외는 예외 처리가 필수가 아니다
try {
    Exception e = new Exception("고의로 발생시킨 예외");
    throw e;
//  throw new Exception("한 줄로 줄여 쓸 수 있음");
} catch (Exception e) {
    System.out.println("에러 메시지 : " + e.getMessage());
    e.printStackTrace();
}

 

📌 메서드에 예외 선언하기 (throws)

  • Java API를 사용하는 경우 설명에 'Throws:'를 확인하여 예외처리를 해주어야 한다
  • throws는 예외를 처리해주는 것이 아니라 호출한 메서드에게 예외를 떠넘기는 것이다
void method() throws Exception1, Exception2, ... ExceptionN {
    //메서드의 내용
}

 

📌 try-with-resources문

  • try-catch문의 변형으로 클래스 사용 후 닫아서 자원을 반환하기 위해 사용한다 (AutoCloseable 인터페이스 구현한 클래스 전용)
  • try 블럭의 괄호() 안에는 객체 생성뿐만 아니라 변수도 선언할 수 있고 이 변수는 try 블럭 내에서만 사용된다
  • 예외가 두 개가 발생하면 실제 발생한 예외에 억제된 예외를 저장해 다룬다
try (FileInputStream fis = new FileInputStream("score.dat");
     DataInputStream dis = new DataInputStream(fis)) {
    //수행할 문장
} catch (EOFException e) {
    //EOF 예외 발생 시 수행할 문장
} catch (IOException ie) {
    //IO 예외 발생 시 수행할 문장
}

 

📌 사용자 정의 예외

  • 조상을 Exception으로 하면 checked 예외이므로 try-catch문을 필수적으로 써야 한다
  • 조상을 RuntimeException으로 하면 unchecked 예외이므로 try-catch문을 선택적으로 써도 된다
class MyException extends Exception 또는 RuntimeException {
    MyException(String msg) { //문자열을 매개변수로 받는 생성자를 꼭 써줘야함
        super(msg); //조상 클래스의 생성자를 호출한다
    }
}

 

📌 예외 되던지기(exception re-throwing)

  • 하나의 예외에 대해서 예외가 발생한 메서드와 이를 호출한 메서드 양쪽 모두에서 처리해줘야 할 때 사용한다
  • 호출한 메서드와 호출된 메서드에 try-catch문으로 예외를 처리하고 호출된 메서드에는 throws로 예외를 선언해야 한다
class ExceptionThrowing {
    public static void main(String[] args) {
        try {
            method1(); //1번
        } catch (Exception e) {
            System.out.println("main메서드에서 예외가 처리되었습니다."); //5번
        }
    }

    static void method1() throws Exception {
        try {
            throw new Exception(); //2번
        } catch (Exception e) {
            System.out.println("method1메서드에서 예외가 처리되었습니다."); //3번
            throw e; //4번
        }
    }
}

 

📌 연결된 예외(chained exception)

  • Throwable initCause (Throwable cause) : 지정한 예외를 원인 예외로 등록
  • Throwable getCause() : 원인 예외를 반환
  • 여러가지 예외를 하나의 큰 분류의 예외로 묶어서 다루기 위해 사용
  • checked 예외를 unchecked 예외로 바꾸어 의미없는 try-catch문을 사용하지 않기 위해 사용

class ChainedException {
    public static void main(String[] args) {
        try {
            install(); //1번
        } catch (InstallException e) {
            e.printStackTrace(); //10번 예외 메시지 출력
        }
    }

    static void install() throws InstallException {
        try {
            startInstall(); //2번
        } catch (SpaceException se) {
            InstallException ie = new InstallException("설치 중 예외 발생"); //6번 InstallException 생성
            ie.initCause(se); //7번 InstallException의 원인 예외로 SpaceException 저장
            throw ie; //8번 InstallException 발생
        }
    }

    static void startInstall() throws SpaceException {
        if (!enoughSpace()) { //3번 true
            throw new SpaceException("설치할 공간이 부족합니다."); //4번
        }
        if (!enoughMemory()) {
            throw new RuntimeException(new MemoryException("메모리가 부족합니다.")); //checked 예외를 unchecked 예외로 변경
        }
    }

    static boolean enoughSpace() {
        return false;
    }

    static boolean enoughMemory() {
        return false;
    }
}

class InstallException extends Exception {
    InstallException(String msg) {
        super(msg); //9번
    }
}

class SpaceException extends Exception {
    SpaceException(String msg) {
        super(msg); //5번
    }
}

class MemoryException extends Exception {
    MemoryException(String msg) {
        super(msg);
    }
}

 

📄 참고

자바의 정석 3rd Edition

반응형