예외처리

2025. 2. 25. 15:23Java/Java 문법

1. 프로그램 오류 

    • 프로그램이 오작동하거나 비정상적으로 종료되게 하는 원인들
    • 프로그래밍에서는 오류를 발생 시점에 따라 크게 3가지로 나눈다.
      • 컴파일 에러(compile-time error) : 컴파일시에 발생하는 에러 
      • 런타임 에러(runtime error) : 실행시에 발생하는 에러 
      • 논리적 에러(logical error) : 실행은 되지만 의도와 다르게 동작하는것
    • 자바 프로그래밍에서는 실행 시(runtime) 발생할 수 있는 오류를 '에러(error)'와 '예외(exception)' 두가지로 구분 하였다.

 

2. 에러(Error)와 예외(Exception) 

2-1. 에러(error) 

  • 프로그램 코드에 의해서 수습될 수 없는 심각한 오류 
  • 시스템 상에서 프로그램에 심각한 문제가 발생하여 실행중인 프로그램이 종료되는 것
  • 개발자가 미리 예측하거나 코드로 처리하는 것이 불가능한 경우
  • ex: JVM 에러(메모리 부족(OutOfMemoryError), 스택오버플로우(StackOverflowError) 등..), 정전, 컴퓨터 자체 하드웨어적인 문제 등

2-2. 예외(exception) 

  • 프로그램 코드에 의해서 수습될 수 있는 다소 미약한 오류
  • 발생할 수 있는 상황을 개발자가 미리 예측하고 처리할 수 있는 미약한 오류
  • 예외 상황의 경우는 개발자가 적절히 처리하여 코드의 흐름을 컨트롤할 수 있다. 

2-3. 예외 처리 (exception handling)

  • 예외 처리: 프로그램 실행 시 발생할 수 있는 예기치 못한 예외의 발생에 대비한 코드를 작성하는 행위를 말한다.
  • 예외 처리의 목적: 예외의 발생으로 인한 실행중인 프로그램의 갑작스런 비정상 종료를 막고, 정상적인 실행상태를 유지하는 것이다.
  • 또한 개발 시에도 디버깅을 용이하게 해서 예외가 발생한 원인과 위치도 쉽게 파악할 수 있어 용이하다.
  • 예외처리는 자기 자신이 아니라 호출한 쪽에서 처리하도록 유도한다. (호출한 쪽에서 try~catch로 예외 처리)

 

3. 예외 클래스 

3-1. 예외 클래스의 종류 

    • 오류와 예외는 모두 Throwable을 상속 받는다.
    • 예외의 최상위 클래스는 Exception 클래스이다.

https://inpa.tistory.com/entry/JAVA-%E2%98%95-%EC%97%90%EB%9F%ACError-%EC%99%80-%EC%98%88%EC%99%B8-%ED%81%B4%EB%9E%98%EC%8A%A4Exception-%F0%9F%92%AF-%EC%B4%9D%EC%A0%95%EB%A6%AC

 

3-2. 예외 종류 

    • Unchecked Exception (언체크 예외, 런타임 예외)
      • 실행 중에 발생하는 예외
      • 기본적 이미 처리되어 있어 개발자가 명시적으로 예외처리 할 필요없음
      • 해당 예외가 발생하지 않도록 프로그램을 잘 작성하는 것이 중요함.
      • 실행 중인 프로그램이 종료되도록 작성되어 있다.
    • Chekced Exception (체크 예외) 
      • 컴파일 단계에서 반드시 처리해야하는 예외
      • 예외 처리를 하지 않으면 컴파일 에러가 발생한다.

3-3. RuntimeException

  • RuntimeException타입의 예외들은 런타임 시점에 해당 예외 클래스 타입의 Exception이 발생한다.
  • RuntimeExcpetion 후손 클래스 몇 가지
// 1. ArithmeticException 
//0으로 나누는 경우 발생
int dividend = 3;
System.out.println(dividend / 0); //ArithmeticException

// 2. ArrayIndexOutOfBoundsException 
//배열의 index범위를 넘어서 참조하는 경우 발생
int[] intArr = new int[0];
System.out.println(intArr[1]);

// 3. NullPointerException 
//인스턴스가 참조되지 않은 상태(Null)로 인스턴스에 접근하는 경우 발생
int[] intArr = null;
System.out.println(intArr[0]);

// 4. ClassCastException 
//형변환(Cast연산자 사용) 시 자료형에 문제가 있을 때 발생
Object obj = new String("hello");
int num = (Integer)obj;

// 5. NegativeArraySizeException 
//배열 크기를 음수로 지정한 경우 발생
int[] intArr = new int[-1];

 

 

4. 예외 처리 방법 

4-1. try-catch(-finally)로 처리 

발생한 Exception을 직접 처리하는 방식이다.

    • try 블럭: 예외(Exception)가 발생할 가능성이 있는 코드를 포함하여 작성하는 블럭
    • catch블럭: try 블럭에서 예외 발생 시 해당 예외 타입(Exception 클래스 타입)에 대한 처리를 기술하는 블럭
      여러 개의 catch블럭을 이어서 사용할 수 있으며 상위 타입의 예외를 처리하는 catch 블럭이 아래 쪽에 위치해야 한다.
    • finally 블럭: 예외 발생 여부와 상관 없이 꼭 실행되어 처리해야 할 코드가 있으면 작성하는 블럭이다.
      주로 java.io나 java.sql 패키지의 메소드 처리 시 자원 반납을 위해 사용한다.
    • 예외가 발생한 경우 실행 순서: try -> catch -> finally
      예외가 발생 하지 않은 경우 실행 순서: try -> finally

4-2. try-with-resource 구문

  • 자바 7버전에서 추가된 기능으로 입출력에서 사용되는 스트림의 자원 반납 close()를 finally 블럭을 사용하지 않고 용이하게 처리하기 위해 도입된 문법이다.
  • try 블록에 괄호()를 추가하여 파일을 열거나 자원을 할당하는 명령문을 명시하면, 해당 try 블록이 끝나자마자 자동으로 파일을 닫거나 할당된 자원을 해제해 준다.
//finally 사용하여 자원 해제 
FileWriter file = null;
try {
    file = new FileWriter("data.txt");
    file.write("Hello World");
} catch (IOException e) {
    throw new RuntimeException(e);
} finally {
    try {
        file.close(); //// close()에서 발생하는 예외처리도 필수
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
}
        
//finally를 쓰지 않고 try옆 ()에 자원 할당 -> try 블록 끝나면 자동으로 close()
try (FileWriter file = new FileWriter("data.txt")) { // 파일을 열고 모두 사용되면 자동으로 닫아준다
    file.write("Hello World");
} catch (IOException e) {
    e.printStackTrace();
}

 

4-3. throws로 위임

  • 메서드가 예외를 직접 처리하지 않고 호출한 쪽으로 던짐
  • Exception이 발생하는 메소드(또는 생성자)를 호출한 상위 메소드에게 처리를 위임하는 방식이다.
  • 메서드에 예외를 선언하려면 메서드의 선언부에 throws를 사용해서 메서드 내에서 발생할 수 있는 예외를 적어주면 된다. 만일 모든 예외의 최고 조상인 Exception 클래스를 메서드에 선언하면 이 메서드는 모든 종류의 예외가 발생할 가능성이 있다는 뜻이다.

4-4. throw

  • throw를 사용하면 직접 예외를 발생시킬 수 있다. 
  • 연산자 new를 이용하여 발생시키려는 예외 클래스의 객체를 만들고 throw로 해당 객체 던지기 
public void checkAge(int age) {
    if (age < 18) {
        throw new IllegalArgumentException("18세 이상만 가능합니다.");
    }
}

 

5. 사용자 정의 예외

  • 기존의 정의된 예외 크래스 외에 필요에 따라 프로그래머가 새로운 예외 클래스를 정의하여 사용할 수 있다.
  • class MyException extends Exception { ... } -> Exception 클래스 상속받기 
  • 그러나 되도록 기존의 예외클래스를 활용하는 것이 좋음
class MyException extends Exception {
    public MyException(String message) {
        super(message);
    }
}

public class Main {
    public static void main(String[] args) {
        try {
            throw new MyException("사용자 정의 예외 발생");
        } catch (MyException e) {
            System.out.println(e.getMessage());
        }
    }
}

 

6. 오버라이딩 시 예외 발생 가능 범위

상속 시 오버라이딩하는 메소드는 부모 클래스의 원본 메소드보다 더 상위 타입의 예외를 발생 시키면 안된다.

public class SuperClass { //부모 클래스 
	public void method() throws IOException { }
}

public class SubClass extends SuperClass { //자식 클래스 
    //✅ 1. 예외 없이 오버라이딩 가능
    //@Override
    //public void method() {}	//정상

    //✅ 2. 같은 예외를 던져주는 구문으로 오버라이딩 가능
    //@Override
    //public void method() throws IOException {} //정상

    // ❌ 3. 부모의 예외처리 클래스보다 상위의 예외로는 후손클래스에서 오버라이딩 불가 
    //@Override
    //public void method() throws Exception {}		//에러

    //✅ 4. 하지만 부모의 예외처리 클래스보더 더 하위에 있는 예외는 오버라이딩 가능
    @Override
    public void method() throws FileNotFoundException {}  //정상
}