11 예외 처리

winA·2025년 7월 1일

BE/Java

목록 보기
13/16
post-thumbnail

💛 예외와 예외 클래스

에러

복구할 수 없는 것

컴퓨터 하드웨어의 고장으로 인해 응용프로그램 실행 오류가 발생하는 것이다. 개발자가 대처할 수 없다.

예외

복구할 수 있는 것

잘못된 사용 또는 코딩으로 인한 오류이다.

예외가 발생하면 프로그램은 곧바로 종료되지만, 예외 처리를 통해 계속 실행 상태를 유지할 수 있다.

  • 일반 예외: 컴파일러가 예외 처리 코드 여부를 검사하는 예외이다.
  • 실행 예외: 컴파일러가 예외 처리 코드 여부를 검사하지 않는 예외이다.

Java는 예외가 발생하면 예외 클래스로부터 객체를 생성한다. 자바의 모든 에러와 예외 클래스는 Throwable을 상속받아 만들어지고, 추가적으로 예외 클래스는 java.lang.Exception 클래스를 상속받는다. 실행 예외는 RuntimeException과 그 자식 클래스에 해당한다. 이 밖의 예외클래스는 모두 일반 예외이다.

실행 예외는 강제화 되어있지는 않지만

💛 예외 처리 코드

try{
	// 정상 실행 부분
	// 예외 발생 가능 코드
}catch(예외클래스 e){
	// 예외 처리
}finally{
	// 항상 실행
}	
  • try 블록에서 작성한 코드가 예외 없이 정상 실행되면 catch 블록은 실행되지 않고 finally 블록이 실행된다.
  • try 블록에서 예외가 발생하면 catch 블록이 실행되고 finally 블록이 실행된다.

catch 블록에서 발생하는 예외 메시지는 System.err를 사용하여 출력하는 것이 좋다. 정상 출력(System.out)과 오류 출력(System.err)을 구분하면, 로그 파일을 분리해 관리할 수 있다.

finally 블록은 옵션 선택 사항이다.

예외 클래스

  • NullPointerException: 참조 변수가 null인 상태에서 필드나 method에 접근할 경우 발생한다.
  • ClassNotFoundException: ClassPath 위치에서 주어진 클래스를 찾았는데, 찾지 못했을 경우 발생한다.

💛 예외 종류에 따른 처리

다중 catch를 사용해서 발생하는 예외에 따라 예외 처리 코드를 다르게 작성할 수 있다. 해당 타입의 예외가 발생하면 catch 블록이 선택되어 실행된다. catch 블록이 여러개라도 catch블록은 단 한개만 실행된다.

package ch11.sec03_type.exam03;

public class ExceptionHandlingExample {
	
	public static void main(String[] args) {
		String[] array = {"100", "1oo", null, "200"};
		for (int i = 0; i <= array.length; i++) {
			try {
				int value = Integer.parseInt(array[i]);
				System.out.println("array[" + i + "]: " + value);
			} catch (ArrayIndexOutOfBoundsException e) {
				System.out.println("배열 인덱스가 초과됨: " + e.getMessage());
			} catch (NullPointerException | NumberFormatException e) { // 2가지 예외를 동일학 처리
				System.out.println("데이터에 문제가 있음: " + e.getMessage());
			} catch (Exception e) {
				System.out.println("실행에 문제가 있습니다.");
			}
		}
	}
}

처리해야 할 예외 클래스들이 상속 관계에 있을 때는 하위 클래스 catch 블록을 먼저 작성하고 상위 클래스 catch 블록을 나중에 작성해야 한다. 예외가 발생하면 catch 블록은 위에서부터 차례대로 검사 대상이 되는데, 하위 예외도 상위 클래스 타입이므로 상위 클래스 catch 블록이 먼저 검사 대상이 되면 안 된다.

두가지 예외를 같이 작성하여 같은 method가 실행되도록 할 수 있다.

💛 리소스 자동 닫기

리소스는 사용하기 위해 열어야 하며, 사용이 끝난 다음에는 닫아야 한다.

항상 프로그램 실행 과정에서 리소스를 사용하다가 예외가 발생된 경우에도 안전하게 닫는 것이 중요하다. 그렇지 않으면 리소스가 불안정한 상태로 남아있게 된다.

try-with-resources 블록을 사용해서 예외 발생 여부와 관계없이 리소스를 자동으로 닫아준다. 프로그램 도중에 예외가 발생하면 자동으로 리소스의 close() method가 호출된다.

try(FileInputStrema fis = new FileInputStream("file.txt")){
	...
}catch(IOException e){
	...
}

AutoCloseable 인터페이스

AutoCloseable 인터페이스를 구현한 객체만 리소스 자동 닫기가 가능하다.

public class FileInputStream **implements AutoCloseable**{
	@Override
	public void close() throws Exception{}
}

복수개의 리소스 처리하기

try() 괄호 안에 ;로 구분하여 작성한다.

try(
	FileInputStream fis1 = new FileInputStream("file1.txt");
	FileInputStream fis2 = new FileInputStream("file2.txt")
){
	...
}catch(IOException e){
	...
}

💛 예외 떠넘기기

method 내부에서 예외 발생 시 throws 키워드를 이용해서 method를 호출한 곳으로 예외를 떠넘길 수 있게 한다.

a method가 b method를 호출했을 때, 만약 b method 내부에서 예외가 발생해도, a method는 b에서 예외가 발생했는지 직접 알 수 없다. 그래서 a는 b가 정상 실행된 것처럼 착각하고 다음 코드(c)를 실행할 수도 있다. 이렇게 되면 프로그램이 비정상적으로 동작할 위험이 생긴다. 이를 막기 위해, b method는 throws를 이용해 발생한 예외를 a에게 “던져서” 알려준다. a는 전달받은 예외를 보고 직접 처리하거나 다시 상위 method로 던질 수 있다.

떠넘길 예외 클래스를 쉼표로 구분해서 나열한다.

리턴타입 method명(매개변수, ...) throws 예외클래스1, 예외클래스2, ...{
}
public class ThrowsExample1 {
	
	public static void main(String[] args) {
		try {
			findClass();
		} catch (ClassNotFoundException e) {
			System.out.println("예외 처리: " + e.toString());
		}
	}
	
	public static void findClass() **throws ClassNotFoundException** {
		Class.forName("java.lang.String2");
	}
}

나열할 예외 클래스가 많으면 throws Exception 또는 throws Throwable로 모든 예외를 떠넘길 수 있다.

💛 사용자 정의 예외

표준 라이브러리에는 없어 직접 정의하는 예외 클래스

일반 예외는 Exception의 자식 클래스로 선언한다. 실행 예외는 RuntimeException의 자식 클래스로 선언한다.

사용자 정의 예외 만들기

  • 일반 예외를 만들고 싶으면 Exception 클래스를 상속한다.
  • 실행 예외를 만들고 싶으면 RuntimeException 클래스를 상속한다.
public class InsufficientException extends Exception {
    
    public InsufficientException() {
    }
    
    public InsufficientException(String message) {
        super(message); // 부모 생성자 호출 (예외 메시지 저장)
    }
}

부모 생성자를 호출하는 것까지가 개발자의 역할이다. 내가 직접 예외처리를 해주지 않을거라면 예외를 던져서 처리한다.

예외 발생 시키기

throw 키워드와 함께 예외 객체를 제공하면 된다. 예외의 원인에 해당하는 메시지를 제공하고 싶다면 생성자 매개값으로 전달한다.

throw new Exception("예외메시지");
throw new RuntimeException("예외메시지");
throw new InsufficientException("예외메시지");

0개의 댓글