예외처리

생성 일시: 2023년 7월 26일 오후 6:57

  • 예외처리

예외처리 (Exception Handling)


프로그램의 오류

  • 어떤 원인에 의해 오동작하거나 비정상적으로 종료되는 경우
  • 발생 시점에 따라 구분 가능
    • 컴파일 에러 (Compile-time error)
    • 런타임 에러 (Runtime error) 문법적으로 오류 X 눈에 잘 안 보임
    • 논리적 에러 (Logical error) 의도와 다르게 나오는 것, 묵시적 형변환이 되지 않는 경우

에러와 예외

Error

발생하면 복구할 수 없는 심각한 오류

  • An Error is a subclass of Throwable that indicates serious problems that a reasonable application should not try to catch.
  • 메모리 부족
  • 스택 오버 플로우: 재귀 호출 할 때 많이 발생함

Exception

프로그래머가 적절한 코드를 통해 대비할 수 있는 오류

  • An exceptino is an event, which occurs during the execution of a program, that disrupts the normal flow of the program’s instructions.
  • 클래스 형변환 실패
  • 파일 읽기 실패

예외처리 Exception Handling

  • 예외 발생 시 프로그램의 비정상 종료를 막고 정상적인 실행 상태를 유지하는 것
  • 예외의 감지 및 예외 발생 시 동작할 코드 작성 필요

예외 클래스의 계층

예외처리

예외의 계열

  • RuntimeException 클래스들
    • 프로그래머의 실수로 발생하는 계열
    • unchecked exception
    • 대처 코드가 없어도 컴파일 문제 x
  • Exception 클래스들
    • 사용자의 실수 등 외적인 요인에 의해 발생하는 계열
    • checked exception
    • 대체 코드가 없으면 컴파일 진행 x

예외의 발생 시

public static void main(String[] args) {
	int[] nums = { 10 };
	System.out.println(nums[2]);
}

Exception in thread "main" ~~
// 선언된 배열의 크기보다 더 큰 인덱스를 참조해서 발생하는 오류

예외 처리 키워드

  1. 직접 처리
    1. try
    2. catch
    3. finally
  2. 간접 처리
    1. throws
  3. 사용자 정의 예외 발생시킬 때
    1. throw

try-catch 구문

  • 프로그램 실행 시 발생할 수 있는 예외에 대한 대비코드 작성
  • 비정상적인 종료를 막고, 정상적인 상태를 유지함
  • 예외 발생 시 JVM의 예외처리기가 닫아서 처리
    try {
    	// 실행했을 때 예외가 발생할 수 있는 코드
    } catch (Exception e) {
    	// 예외가 발생했을 때 처리할 코드
    }
    
  • 중괄호 (블록) 생략 불가능
  • 블럭 안에 또다른 try-catch 구문이 올 수 있음
    try {
    	// (1) 예외가 발생할 수 있는 코드
    	// (2) 정상 코드
    } catch (Exception e) {
    	// (3) 예외가 발생했을 때 처리할 코드
    }
    // (4) 일반 코드
    

    실행 순서

    1. 예외가 발생할 경우 1 → 3 → 4
    2. 예외가 발생하지 않을 경우: 1 → 2 → 4
    3. 예외를 처리하지 못한 경우: 1 (중단)

다중 예외처리

  • try 블록에서 여러 종류의 예외가 발생할 경우
  • 하나의 try 블록에 여러 개의 catch 블록 추가 가능
    try {
    	//exception이 발생할 만한 코드
    } catch (XXException e) {
    	//XXException 발생 시 처리 코드
    } catch (YYException e) {
    	//YYException 발생 시 처리 코드
    }
    
    try {
    	//exception이 발생할 만한 코드
    } catch (XXException | YYException e) {
    	//필요시 instanceof를 통해 나누어 작성
    	//XXException 발생 시 처리 코드
    	//YYException 발생 시 처리 코드
    }
    

다중 예외처리 시 유의사항

  • JVM이 던진 예외는 catch 문장을 찾을 때는 다형성이 적용됨
  • 상위 타입의 예외가 먼저 선언되는 경우 뒤에 등장하는 catch 블록은 동작할 기회가 없음
  • 상속 관계가 없는 경우는 무관
  • 상속 관계에서는 작은 범위(자식)에서 큰 범위(조상)순으로 정의
    try {
    	// exception이 발생할 만한 코드
    } catch (XXException e) {
    	//XXException 발생 시 코드
    } catch (YYException e) {
    	//YYException 발생 시 처리 코드
    } catch (Exception e) {
    	// Exception 발생 시 처리 코드
    }
    
    try {
    	// exception이 발생할 만한 코드
    } catch (Exception e) {
    	// Exception 발생 시 처리 코드
    } catch (YYException e) {
    	// YYException 발생 시 처리 코드
    } catch (XXException e) {
    	// XXException 발생 시 처리 코드
    }
    

Exception 인스턴스의 주요 메서드

  • getMessage(): 발생된 예외에 대한 구체적인 메시지를 반환
  • printStackTrace(): 예외 발생 당시의 호출 스택(Call Stack)을 출력

메서드에서 예외 던지기

  • 메서드 선언부에 throws 키워드를 사용하여 예외 작성
  • 예외가 여러 개일 경우 ,를 사용하여 나열
  • 메서드가 예외를 처리하는 것이 아닌 전달
  • 조상 타입의 예외로 처리 가능

checked exception과 throws

→ checekd exception: 빨간 줄이 뜸!

public static void main(String[] args) {
	CheckedThrowsTest et = new CheckedThrowsTest();
			try {
		et.method1();
	} catch (ClassNotFoundException e) {
		System.out.printf("exceptino handling: %s%n", e.getMessage());
	}
	System.out.println("프로그램 종료");
}
public void method1() throws ClassNotFoundExceoption {
	method2();
}
public void method2() throws ClassNotFoundException {
	Class.forName("Some Class");
	// Class 라는 class가 있음...
	// "" 안에 있는 클래스 이름이 없다면 예외가 생길 수도 있따~ 처리해
}

runtime exception과 throws

메서드 재정의와 throws

  • 메서드 재정의 시 조상클래스 메서드가 던지는 예외보다 부모 예외를 던질 수 없다

finally 구문

  • finally는 예외 발생 여부와 상관없이 언제나 실행
    try {
    	// 예외가 발생할 가능성이 있는 코드
    } catch(Exception e) {
    	// 예외 처리 코드
    } finally {
    	// 예외 발생과 상관없이 항상 수행해야 하는 코드
    }
    
  • 예외 발생시 → try-catch-finally 순으로 코드 실행
  • 예외 미발생시 → try-finally 순으로 코드 실행
  • 중간에 return이 있어도 finally 블록 수행 후 반환

    // 예외 처리 코드
    // finally: 예외 상관없이 수행해야 하는 코드
    // 1. 정상적으로 실행한다면 1 2 4 5
    // 2. 예외 발생 시 1 3 4 5
    // 3. 예외 미처리시 1 4 (중단)
    public static void main (String[] args) {
    	int num = 0;
    	int[] nums = {1};
    	try {
    		System.out.println("1");
    		nums[2] = 10;
    		System.out.println("2");
    	} catch (ArithmeticException e) {
    		System.out.println("3");
    	} finally {
    		System.out.println("4");
    	}
    	System.out.println("5");
    	}
    

자동 자원 반납 구문 (try with resources)

  • 자원 등을 반납할 때 finally에서 close()를 통해 반납을 주로 함
  • 코드가 지저분해지고 다른 예외 상황을 발생시킬 수 있음
    FileInputStram fis = null;
    try {
    	fis = new FileInputStram("test.txt");
    } catch (IOException e) {
    	e.printStackTrace();
    } finally {
    	try {
    		if (fis != null) fis.close();
    	} catch (IOException e) {
    		e.printStackTrace();
    	}
    }
    
  • try() → 괄호 안에 객체를 생성하는 코드를 작성하면, 해당 객체는 close()를 호출하지 않아도 블록을 벗어나는 순간 close()가 호출됨
  • 해당 객체의 클래스가 AutoCloseable이라는 인터페이스를 구현한 클래스여야만 함
    try (FileInputStram fis = new FileInputStream("test.txt)) {
    	// 코드 생략
    } catch (IOException e) {
    	e.printStackTrace();
    }
    

사용자 정의 예외

  • 기존에 정의된 예외 이외에 사용자가 직접 정의 예외를 작성할 수 있음
  • 대부분 Exception 또는 RuntimeException 클래스를 상속받아 작성
    • checked exception 활용: 명시적 예외 처리 또는 throws 필요 (코드는 복잡해지지만 처리, 누락 등 오류 발생 가능성은 down)
    • runtime exception 활용: 묵시적 예외 처리 가능 (코드가 간결해지지만 예외 처리, 누락 가능성 발생)

→ 요약하자면, 예외 처리란 발생할 수 있는 종류별 Exception 클래스를 활용하여 처리하는 것이고, Override로 사용자 정의 예외 처리 또한 할 수 있는 느낌인 것 같다.