예외처리(Exception handling)

프로그램 오류?

프로그램이 실행 중 어떠한 원인으로 인하여 오작동하거나 비정상 종료 될 시 이와같은 결과를 초래하게 만드는 원인을 프로그램 에러 또는 오류라 한다.
컴파일 에러 : 컴파일 시점에서 발생하는 에러
런타임 에러 : 프로그램이  실행하는 중 발생하는 에러
논리적 에러 : 정상실행은 되지만, 예상과 다르게 동작하는 에러

 

에러 , 예외 ?

에러 : 프로그램 코드 레벨에서 수습할 수 없는 심각한 오류
예외 : 프로그램 코드 레벨에서 수습할 수 있는 경미한 오류  

 

예외 클래스 계층구조도 ?

 

다음과 같이 여러가지 예외처리를 위한 예외클래스 계층구조를 확인해 볼 수 있다. 이때, 우리 코드 상에서 잡을 수 있는 오류인 예외는 Exception(=checked예외)을 최상위 부모로하여 RuntimeExcetion(=unchecked예외)을 상속받는 클래스들은 런타임 시 발생하는 예외를 이외에는 컴파일 시 발생할 수 있는 예외들로 구분되어 있다.

 

이때, 컴파일 시 발생하는 예외들은 반드시 사전에 예외처리 관련 코드를 추가하지 않을 경우 컴파일 조차 할 수 없기에 반드시 추가해야한다면 런타임 시 발생하는 예외의 경우는 반드시 예외처리를 해주지 않아도 컴파일 시 문제가 발생하지 않고 정상 작동한다. 그러나 예외처리를 하지 않았기에 예외가 발생시 프로그램은 비정상 종료된다.

 

예외처리하기 ? 

예외처리란, 프로그램 실행 중 발생할 수 있는 예기치 못한 예외 발생에 대비하여 코드를 작성하여 예외로 인한 프로그램 비정상 종료를 막고, 정상적인 실행상태를 유지시키기 위함이다.

예외처리를 하지 않아 발생한 예외로 프로그램이 비정상 종료될 시 처리되지 못한 예외는 JVM의 '예외처리기(UncaughtExceptionHandler)'가 받아서 예외 원인을 화면에 출력한다.

1) try - catch 문

try{
	// 예외가 발생할 수 있는 코드를 넣는다.
}catch(Exception1 e1){
	// Exception1타입 에러 발생 시 처리할 문자
}catch(Exception2 e2){
	// Exception2타입 에러 발생 시 처리할 문자
}catch(Exception3 e3){
	// Exception3타입 에러 발생 시 처리할 문자
}			
            .
            .

   발생가능한 예외들 추가

 

흐름 ? 

👉 try블럭 내에서 예외가 발생할 경우

    1. 발생 원인에 대한 예외 클래스의 인스턴스가 생성된다.

    2. 가장 위 catch문부터 시작하여 참조변수 타입과 생성된 인스턴스 사이에 instance of 참조타입 비교를 수행한다. 

        👉 만약 일치하는 catch문이 없을 시 처리되지  못한 예외에 의해서 프로그램이 비정상 종료된다.

    3. 일치하는 catch문이 있을 시 내부의 작업을 수행한다.

    4. 작업을 완료할 시 try - catch문을  빠져나온다

 

👉 try블럭 내에서 예외없이 완료할 시

    1. try - catch문을  빠져나온다.

    2. try - catch문 다음 작업을 계속 수행한다.

 

printStackTrace() ,getMessage() ? 

printStackTrace() : 예외발생 당시의 호출스택에 있었던 메서드의 정보와 예외 메세지를 화면에 출력
getMessage()      :  발생한 예외클래스의 인스턴스에 저장된 메세지를 얻을 수 있다.

2) 멀티 catch블럭

JDK1.7부터 catch블럭을 '|'기호를 이용해서 여러개의 catch블럭을  하나로 합칠 수 있다.

이때, 주의할 사항은 함께  묶이는 예외클래스 사이에는 상속관계인 클래스는 묶을 수 없으며, 함께 묶이는
클래스들 중 어떠한 에러가 발생하는지는 알 수 없기에 조상 예외 클래스에 선언된 멤버만 사용 가능하다.

만약 해당 예외클래스 멤버를 사용하고 싶을 경우 내부적으로 형변환을 하여 사용할 수는 있다.
try{
  // 예외발생 가능성 있는 구현 코드
}catch(ExceptionA | ExceptionB e){
	e.methodeExceptionAType() // ExceptionA클래스에만 있는 메서드 사용 할 수 x
}

다음을 사용하는 경우는 여러개의 예외처리에 있어서 공통으로 묶어서 동일하게 처리하고 싶을 경우에 활용할 수 있다.

3) 메서드에 예외 선언하기

/* 
다음과 같이 메서드에 발생할 수 있는 예외들을 적을 수 있다.
 실제 예외발생 시 해당 메서드에서 예외를 처리하지 않고 해당 메서드를 호출한 메서드로
 예외를 넘긴다 
 */
void exceptMethod() throws 예외클래스1,예외클래스2,.....{
	//메서드 내용
}

 

다음과 같이 예외발생 시 예외가 발생한 메서드 내부적으로 처리하는 것이 아닌 호출한 쪽으로 예외를 넘길 수 있다. 이때, 발생할 수 있는 예외에 대해서는 보통 컴파일 단계 예외를 기입하며, 런타임 시 발생 할 수 있는 예외는 제외하여도 실행 시 문제가 없다. 물론 런타임 예외가 발생하면 처리해주어야한다.

 

다음과 같이 메서드를 통해서 예외를 떠넘길  경우 반드시 호출스택 상 있는 어딘가에서는 반드시 처리를 해주어야하며 main메서드까지 처리가 안될 시 예외에 의한 비정상 종료가 된다.

4) finally 블럭

finally블럭은 예외의 발생여부와 상관없이 실행되어야하는 코드를 포함시킬목적으로 사용된다.
try{
  // 예외발생 가능성 있는 구현 코드
}catch(Exception  e){
  // 해당 예외 발생 시 처리할 코드
}finally{
 /*
 - 예외의 발생과 상관없이 마지막에 반드시 수행되는 코드
 - try - catch문 마지막에 위치해야한다.
 - try문에서 자원을 사용한 후 예외발생여부와 상관없이 자원은 반납되어야한다. 
   이럴때 해당 위치에서 자원을 반납해주면 예외 상관없이 반납된다.
  */
}

5) try - with - resources 문

JDK1.7부터 새로 추가되었으며, 주로 I/O 관련 클래스 사용 시 유용하게 사용할 수 있다.
/* 
try - with - resources을 통해서 자원을 사용 후 자동반납하기 위해서 해당 클래스는
AutoCloseable 인터페이스에 close()를 구현해야한다.
*/
public interface AutoCloseable{
	void close() throws Exception;
}

// try - with - resources 구문 사용
try ( FileInputStream fis = new FileInputStream("test.txt") ; // 여러 자원 사용 시 ;로 구분
      DataInputStream dis = new DataInputStream(fis)){
      
        // 예외발생 가능한 코드
      }catch(Exception e){
      }catch(Exception2 e){
      }

위에 코드에서 사용한 자원에 대해서 부모클래스로 올라가보면 InputStream 클래스에서 Closeable 인터페이스의 close()를 구현한 것을 확인할 수 있다.

예외  만들기 및 발생시키기 ? 

1) 예외 발생 시키기

throw 키워드를 사용하면 예외를 발생시킬 수 있다.
 public static void main(String[] args) {
        try{
            ArithmeticException e = new ArithmeticException(); // 발생시킬 예외 인스턴스 생성
            throw e; // throw 키워드를 사용해서 예외발생
//            throw new ArithmeticException(); 한 줄로 표현
        }catch (ArithmeticException e){
            System.out.println("getMessage() : "+e.getMessage());
            System.out.println();
            System.out.print("printStackTrace() : ");
            e.printStackTrace();
        }
    }

다음과 같이 예외가 발생하는 것을 볼 수 있다. 그러나 한 가지는 예외메세지가 null인 것을 볼 수 있다. 임의로 만들어서 발생시킬경우 메세지가 없는 것 같다. 이럴때 인스턴스 생성 시 생성자 매개변수로 예외메세지를 String 형태로 지정해줄 수 있다.

2) 사용자정의 예외 만들기

보통 Exception클래스 또는 RuntimeException클래스로부터 상속받아 만들지만, 필요에 맞게 상속할 예외클래스를 선택하여 사용할 수 있다.
//사용자정의 예외 만들기
class MyException extends Exception{
    MyException(String msg){
        super(msg);
    }
}


public class Main {
    public static void main(String[] args) {
        try{
            throw new MyException("사용자 예외 만들기");
        }catch (MyException e){
            System.out.println("getMessage() : "+e.getMessage());
            System.out.println();
            System.out.print("printStackTrace() : ");
            e.printStackTrace();
        }
    }
}

 

'P.L > Java' 카테고리의 다른 글

열거형(enums)  (0) 2023.03.26
유용한 배열 사용 - java.util.Arrays 클래스  (0) 2023.02.08
java.lang.Character 클래스  (0) 2023.01.28
java.util.Arrays 클래스를 활용한 배열복사  (0) 2023.01.19
String 클래스 - repeat()  (0) 2022.12.20