CS/Java (CS)

[Java] 예외 처리(Exception Handling)에 관하여

ummchicken 2023. 2. 10. 00:57

이제야 정리하는...

 

 

글로만 읽어선 머릿속에 안 들어온다.

시간이 오래 걸리지만, 그래도 글로 써야겠다는 생각이 든다.

 

 

 

✔️ 예외 처리(Exception Handling)

예외처리란, 프로그램 실행 흐름 상 오류가 발생했을 때 그 오류를 대처하는 방법이다.

 

💡 프로그램 오류

프로그램 실행 중 어떤 원인에 의해 프로그램이 해당 상황에 대처하지 못할 경우, 
비정상적으로 종료되거나 에러 팝업창이 뜨는 경우이다.

 

 

 


 

✔️ 발생 시점에 따른 에러 분류

💡 Compile Error

컴파일 시점에서 발생하는 에러로, 소스코드를 컴파일러가 컴파일 하는 시점에서
소스의 오타나 잘못된 구문, 자료형 체크 등 검사를 수행하는데 발생하는 에러이다.

이 시점에서 발생하는 문제들은 
수정 후 컴파일을 성공적으로 마칠 경우 클래스 파일(*.class)이 생성된다.

※ Compile시에 발생할 수 있는 예외는 빨간줄로 예외를 처리하라고 나옴.
@Test
void compileError() {
    STring helloStr = "hello";

    System.out.println("helloStr = " + helloStr);
}

→ String을 STring이라 오타를 낸 코드이다.

 

 


 

💡 Runtime Error

프로그램 실행 시점에서 발생하는 에러로,
컴파일러는 컴파일 시점에서 문법 오류나 오타같은 예측 가능한 오류는 잡아줄 수 있지만, 
실행 중 발생할 수 있는 잠재적인 에러까지는 잡을 수 없다.
(컴파일시 발견 X)

그래서 컴파일은 문제없이 완료되어 프로그램이 실행되어도, 
실행 도중 의도치 않은 동작에 대처하지 못해 에러가 발생할 수 있다.

따라서 컴파일 시 발견하지 못하는 런타임 에러를 프로그래머가 예측하여 처리해주어야 한다.
→ 예외 처리.
void stringToInt(String str) {
    int i = Integer.parseInt(str);
    System.out.println("str parsing result = " + i);
}

→ 문자가 숫자 형태가 아닌 문자 타입인 경우, parseInt() 메서드에서 NumberFormatException이 발생할 것이다.

 

 

 

🚨 런타임 시점에서 발생하는 오류는 에러(Error)예외(Exception)으로 나뉜다.

  • 에러(Error) : 프로그램이 코드로 복구될 수 없는 오류이다.
    • 메모리 부족(OutOfMemoryError)
    • 스택오버플로우(StackOverflowError) 등

 

  • 예외(Exception) : 프로그래머가 직접 예측하여 막을 수 있는 처리가능한 오류이다.
    • 인자값 Null 에러(NullPointException) 등

 

 


 

✔️ 예외 처리 상속 트리

Exception의 상속 트리

💡 Java에서 모든 예외는 Exception이라는 클래스를 상속받는다.
  • 자바 최상위 객체인 Object를 필두로 에러 객체와 예외 객체가 있다.
  • 에러 최상위 객체인 Throwable을 상속받는 Error와 Exception이 있다.
  • Exception 하위 예외 클래스 중 RuntimeException과 그 하위 예외를, 선택적 예외로 개발자가 상황에 맞춰 대응해줘야 한다.
  • 그외 나머지 예외 클래스와 그 하위 객체들을 필수(checked) 에외라하여 반드시 체크해줘야하는 예외라 한다. (CheckedException은 실행하기 전에 예측 가능한 예외를 말하고, 반드시 예외 처리를 해야 한다.)
  • RuntimeException 클래스들은 주로 프로그래머의 실수에 의해 발생될 수 있는 예외들이다.
    • Ex) 배열의 범위 벗어남, 값이 null인 참조변수의 멤버를 호출하려는 경우 등

 

  • Exception 클래스들은 주로 외부의 영항으로 발생할 수 있는 것들로, 대표적으로 I/O 입출력에 의해 발생하는 경우가 많다.
    • Ex) 클래스의 이름을 잘못 적음, 데이터 형식 잘못됨, 사용자가 존재하지 않는 파일명을 입력한 경우 등

 

 

 

 


 

✔️ 예외 처리 방법

예외처리(Exception Handling)
- 정의 : 프로그램 실행 시 발생할 수 있는 예외 발생에 대비한 코드를 작성하는 것.
- 목적 : 프로그램의 비정상 종료를 막고, 정상적인 실행 상태를 유지하는 것.

 

 

💡 자바의 예외 처리는

  • 예외가 발생한 메소드 내에서 직접 처리하는 방법
  • 예외가 발생한 메소드를 호출한 곳으로 예외 객체를 넘겨주는 방법
  • 사용자 정의 예외를 생성하여 처리하는 방법이 있다.

 

 

이제 각 방법을 살펴보자.

 

 


 

1.  try - catch - finally

예외가 발생한 메소드 내에서 직접 처리
try{

	// 예외가 발생될만한 코드
    
} catch(FileNotFoundException e){	

	// FileNotFoundException이 발생했다면

} catch(IOException e){ 

	// IOException이 발생했다면

} catch(Exception e){	

	// Exception이 발생했다면

} finally{

	// 어떤 예외가 발생하던 말던 무조건 실행
    
}
  • try 블록은 예외가 발생할 가능성이 있는 범위를 지정하는 블록이다.
  • try 블록은 최소한 하나의 catch 블록이 있어야 한다.
  • catch 블록은 try 블록 다음에 위치한다.
  • catch 블록의 매개변수는 예외 객체가 발생했을 때 참조하는 변수명으로 java.lang.Throwable 클래스의 하위 클래스 타입으로 선언되어야 한다.
  • 지정된 타입의 예외 객체가 발생하면 try 블록의 나머지 문장들은 수행되지 않고, 자바 가상 머신은 발생한 예외 객체를 발생시키며 발생한 예외 객체 타입이 동일한 catch 블록을 수행한다.
  • finally 블록은 필수는 아니다.
  • finally 블록이 사용되면, finally 블록의 내용은 예외 발생 유무나 예외 catch 유무와 상관 없이 무조건 수행된다.
    • 따라서, 데이터베이스나 파일을 사용한 후 닫는 기능과 같이 항상 수행해야 할 필요가 있는 경우에 사용한다.

 

 

예를 들어보자.

public class ExceptionTest {
 
    public static void main(String[] args) {
        
        String[] name = new String[2];
        
        try {
            name[0] = "아이유";
            System.out.println("이름 : " + name[0]);
 
            name[1] = "트와이스";
            System.out.println("이름 : " + name[1]);
 
            name[2] = "헤이즈";
            System.out.println("이름 : " + name[2]);
            
        }catch (ArrayIndexOutOfBoundsException e) {
        
            System.out.println("배열 참조 에러 발생");
            
        }catch (Exception e) {
        
            System.out.println(e.getMessage());
            
        }finally {
            System.out.println("시스템 종료");
        }
    }
}
이름 : 아이유
이름 : 트와이스
배열 참조 에러 발생
시스템 종료
 
Process finished with exit code 0
  • String[2] 배열을 선언하고, 3 번째 배열에 값을 대입할 때 예외가 발생한다.
  • ArrayIndexOutOfBoundsException이 발생하여 catch에 잡히고, "배열 참조 에러 발생"이라는 메시지가 출력된다.
  • 최종적으로 finally가 실행되며 "시스템 종료" 메시지가 출력된다.

 

 


 

2. throws

예외가 발생한 메소드를 호출한 곳으로 예외 객체를 넘기는 방법
throws는 어떠한 메소드의 내부 소스코드에서 에러가 발생했을 시 예외처리를 try-catch로 하는 것이 아니라,
이 메소드를 사용하는 곳으로 책임을 전가하는 행위이다.

 

public void method1() {
    try {
    	method2();
    } catch(ClassNotFoundException e) {
    	// 예외 처리 코드
        System.out.println("클래스가 존재하지 않습니다.");
    }
}

// ClassNotFoundException을 호출한 위의 catch문에서 예외 처리를 한다.
public void method2() throws ClassNotFoundException {
	Class clazz = Class.forName("java.lang.String2");
}
  • method1()에서 method2()를 실행시킨다.
  • method2() 메소드에서 "java.lang.String2" 클래스를 가져온다.
  • 해당 클래스를 찾지 못하면 classNotFoundException이 발생한다.
  • method2()에서는 ClassNotFoundException을 호출한 main에 예외를 넘긴다.
  • main에서는 ClassNotFoundException을 받아 catch문에서 잡아서 "클래스가 존재하지 않습니다." 메시지를 출력한다.

 

 


 

3. 사용자 정의 예외 생성 (throw)

기존의 예외 클래스로 예외를 처리할 수 없다면, 사용자가 직접 예외 클래스를 작성해서 발생시킬 수 있다.

사용자가 예외 클래스를 직접 정의하려면, 
예외 클래스의 최상위 클래스인 java.lang.Exception 클래스를 상속받아 클래스를 정의한다.
class 예외클래스이름 extends Exception { ...

 

 

자바 가상 머신은 프로그램 수행 중에 예외가 발생하면, 

자동으로 해당하는 예외 객체를 발생시킨 다음 catch 블록을 수행한다.

 

 

하지만 예외는 사용자가 강제적으로 발생시킬 수도 있다.

예외를 발생시키기 위해 throw 예약어를 사용한다.

throw new 예외클래스이름(매개변수);

 

 

 

💡 throw를 이용한 예외 발생 시에도 try-catch-finally 구문을 이용한 예외처리를 하거나

public class ExceptionTest {
    public static void main(String[] args) {
    	try {
            throw new Exception();
        } catch(Exception e) {
            System.out.println(e);
        } finally {
            System.out.println("시스템 종료");
        }
    }
}
java.lang.Exception
시스템 종료
 
Process finished with exit code 0

 

 

 

💡 throws를 이용하여 예외가 발생한 메소드를 호출한 다른 메소드로 넘기는 예외 처리 방법을 사용해야 한다.

public class MyException extends Exception {
    
    public MyException() {
        super("dev-coco님이 만든 예외");
    }
}
public class ExceptionTest {
 
    static void callException() throws MyException {
 
        throw new MyException();
    }
 
    public static void main(String[] args) {
 
        try {
            callException();
        }catch (MyException e) {
            System.out.println(e.getMessage());
        }finally {
            System.out.println("시스템 종료");
        }
    }
}
dev-coco님이 만든 예외
시스템 종료
 
Process finished with exit code 0
  • MyException이라는 Exception을 상속받는 예외를 만들었다.
  • MyException은 "dev-coco님이 만든 예외"라는 메시지를 갖는다.
  • ExceptionTest의 main이 실행되면, callException() 메소드를 호출한다.
  • callException() 메소드는 MyException()을 new로 생성한 후 callException()를 호출한 main으로 던진다.
  • MyException을 받은 main은 catch에서 해당 예외를 받아 예외 메시지를 출력한다. "dev-coco님이 만든 예외"
  • 최종적으로 finally가 실행되어 "시스템 종료"가 출력된다.

 

 


 

✔️ Custom Exception 만들기

기존에 정의된 예외 클래스 이외에 필요에 따라 새로운 예외 클래스를 정의하여 사용할 수 있다.
보통 Exception 클래스를 상속받는 클래스를 만들지만, 필요에 따라서 알맞은 예외 클래스를 상속받아서 만든다.

String 타입의 파라미터를 갖는 생성자는 
상위 클래스의 생성자를 호출하여 예외 메시지를 넘겨준다.
try 블록에서 예외가 발생하면, 예외 객체는 catch 블록의 매개변수에서 참조하게 되므로 
매개변수를 이용하면 예외 객체의 정보를 알 수 있다.

모든 예외 객체는 Exception을 상속받기 때문에, 메소드를 예외 객체에서 호출할 수 있다.
그 중에서도 많이 사용되는 getMessage()와 printStackTrace()로 예외에 대한 정보를 알 수 있다.

 

public class AnyCreateException extends Exception {
  public AnyCreateException() {};
  public AnyCreateException(String message) {
     super(message);
    // 사용자 정의 예외의 생성. 생성자는 보통 두 가지를 선언하는데
    // 두 번째의 경우 에외 메시지를 전달하기 위해 String 타입의 매개 변수를 갖는 생성자이다.
 }
}
.....
public class Account{
int balance = 100;
 public void withdraw(int money) throws AnyCreateException {
  if(balance < money){
   throw new AnyCreateException("Excpetion 발생!");
    // AnyCreateException 의 두 번째 생성자를 선택한 것이다.
    // 모든 예외는 Exception이 가지고 있는 메소드들을 호출 할 수 있다.
    // 그 중 getMessage 메소드가 이 예외 메시지를 리턴한다. 사용은 아래에 구현했다.
 }
}
....
public static void main(String[] args){
 Account account = new Account();
 try{
 account.wtihdraw(30000);
}
 catch ( AnyCreateException e ) {
  String message = e.getMessage(); // 메시지를 담은 생성자를 선택했을 때, 그 메시지를 getMessage() 메소드가 리턴한다.
                              
  System.out.println(message);
  e.printStackTrace(); // 예외가 어디에서 발생했는지 출력해준다.
}
  • 사용자 정의 예외 생성 시, 생성자는 보통 두 가지를 선언한다.
    • 두 번째의 경우, 예외 메시지를 전달하기 위해 String 타입의 매개 변수를 갖는 생성자다.

 

 

 


 

 

아 진짜 개어렵네..^^

Custom Exception 도전해보려고 깔짝거렸는데, 

후................................ 왜 새롭냐;;;

 

 

 

 

 


출처