4 분 소요

📚 JAVA


📚 Exception Handling ( 예외 처리 )

Exception Handling ( 예외 처리 ) 란 무엇일까?

예외 처리

예외처리를 알기 전 에러와 예외의 차이를 알 필요가 있다.

예외 클래스의 계층

모든 예외의 최고 조상은 Exception Class이다.
Checked 계열과 Unchecked 계열로 나뉜다.


예외 클래스 계층

  • Checked Exception 계열 ( = Exception class )
    외부적으로 발생할 수 있는 예외
    예외에 대한 대처 코드가 없으면 컴파일이 진행되지 않음
    FlieNotFoundException : 존재하지 않는 파일의 이름 입력
    ClassNotFoundException : 클래스의 이름을 잘못 입력
    DataFormatException : 데이터 형식이 잘못 입력
    SQLException : SQL Server에서 경고 또는 오류를 반환
    IOException : I/O 오류가 발생

  • Unchecked Exception 계열 ( = RuntimeException class)
    프로그래밍 요소들과 관계가 깊다.
    예외에 대한 대처 코드가 없더라도 컴파일은 진행 된다.
    RuntimeException의 하위 클래스들
    ArithmeticException : 0으로 나누려고 하는 경우
    NullPointerException : 값이 null인 참조변수의 멤버를 호출

Throwable의 주요 메서드

  • public String getMessage()
    발생된 에외에 대한 구체적인 메세지를 반환
  • public Throwable getCause()
    예외의 원인이 되는 Throwable 객체 또는 null을 반환
  • public void printStackTrace()
    예외가 발생된 메서드가 호출되기까지의 메서드 호출 스택을 출력.
    디버깅의 수단으로 주로 이용

try ~ catch 문

다음은 try ~ catch문이다.

try{
    //예외가 발생할 수 있는 코드
}catch(Exception e){    //던진 예외를 받음
    //예외가 발생했을 때 처리할 코드
}

try문에서 예외가 발생 하면

  • JVM이 해당 Exception 클래스의 객체 생성 후 던진다(throw)
  • 던져진 exception 을 처리할 수 있는 catch 블록에서 받은 후 처리한다.
  • 정상적으로 처리되면 try~catch문을 벗어나 다음 문장을 진행한다.

try문에서 예외가 발생하지 않으면

  • catch문을 거치지 않고 try ~ catch 블록의 다음 문장을 진행한다.

다중 Exception Handling

try 블록에서 여러 종류의 에러가 발생할 경우

  • 하나의 try 블록에 여러 개의 catch 블록 추가 가능
  • JVM이 던진 예외는 catch 문장을 찾을 때는 다형성이 적용된다.
  • 상위 타입의 예외가 먼저 선언되는 경우 뒤에 등장하는 catch 블록은 동작할 기회가 없다.
  • 상속 관계에서는 작은 범위( 자식 )에서 큰 범위( 조상 )순으로 정의
  • 상속 관계가 없는 경우 무관
    다중 예외 처리를 이용한 Checked Exception 처리
  • 발생하는 예외들을 하나로 처리 (ex) (Exception e)
  • 가급적이면 예외 상황 별로 처리하는 것을 권장
    심각하지 않은 예외를 굳이 세분화해서 처리하는 것도 닝바
  • ’|’ 를 이용해 catch문에서 상속관계가 없는 여러 개의 exception 처리

try ~ catch ~ finally 구문

finally는 예외 발생 여부와 상관 없이 언제나 실행
중간에 return 문을 만나도 finally 블록을 먼저 수행 후 리턴 실행

try{
    //예외가 발생할 수 있는 코드
}catch(Exception e){    //던진 예외를 받음
    //예외가 발생했을 때 처리할 코드
}finally{
    // try block에서 접근했던 System 자원의 안전한 원상복구
}

try ~ catch ~ finally 의 주요 목적은 try블록에서 사용한 리소스 반납이다.
생성한 시스템 자원을 반납하지 않으면 장래 resource leak 발생 가능
-> close 처리

throw 키워드

throw 키워드를 통한 처리 위임

  • 예외가 없어지는게 아니라 단순히 전달
  • 예외를 전달받은 메서드는 다시 예외 처리의 책임 발생
  • 처리하려는 예외의 조상 타입으로 throws 처리 가능
  • Checked exception은 반드시 try ~ catch 나 throws 필요
  • Unchecked exception은 throws 안해도 전달되지만 결국은 try ~ catch로 처리해야함

try ~ with ~ resources

JDK 1.7 이상에서 리소스의 자동 close 처리
객체들이 AutoCloseable interface가 구현해 있으면 finally 역할을 해준다.

사용자 정의 예외

API에 정의된 예외 이외에 필요에 따라 사용자 정의 예외 클래스 작성
대부분 Exception 또는 RuntimeException 클래스 상속받아 작성

  • Checked exception 활용
    코드는 복잡해지지만 오류 발생 가능성 줄어듦
    명시적 예외 처리 또는 throws 필요
  • Unchecked exception 활용
    묵시적 예외 처리 가능
    코드는 간결해지지만 예외 처리 누락 가능성 발생

사용자 정의 장점
객체의 활용 - 추가정보, 기능 활용 가능
코드의 재사용 - 동일한 상황에서 예외 객체 재사용 가능
throws 메커니즘의 이용 - 중간 호출 단계에서 return 불필요

Exception Handling 를 사용하는 이유?

프로그램의 비정상 종료를 막고 정상적인 실행상태를 유지하는 것 !!
그리고 후에 디버깅 시에도 많은 도움을 얻을 수 있다.
Throwable의 printStackTrace는 메서드 호출 스택 정보 조회까지 가능하여 디버깅 시 활용 가능

Exception Handling 실습 해보즈아 !

try ~ catch문

public class Exception {
	public static void main(String[] args) {

		int[] arr = { 1 };        // 배열 선언 , 크기 = 1

		try {
			System.out.println(arr[1]);    // 2번째 배열 호출 -> 오류
		} catch (ArrayIndexOutOfBoundsException e) {    
			System.out.println("배열 크기 확인 !!");    // 오류시 출력
		}
		System.out.println("프로그램 종료");
	}
}

출력 결과
배열 크기 확인 !!
프로그램 종료


크기가 1인 배열을 선언하고 2번째 배열을 호출 했을 때 배열의 크기보다 큰 인덱스를 호출하였으므로 예외가 발생. 그래서 catch 블록에서 ArrayIndexOutOfBoundsException를 받아 예외 처리를 진행 하였다.


try ~ catch ~ finally

public class Exception_finally {
	public static void main(String[] args) {
		int[] arr = { 1 };

		try {
			System.out.println(arr[1]);
		} catch (ArrayIndexOutOfBoundsException e) {
			System.out.println("배열 크기 확인 !!");
		}finally {      // finally문 무조건 실행
			System.out.println("무조건 실행");
		}
		} 
}

출력 결과
1
무조건 실행


try ~ catch 문에 finally를 붙였다.
예외가 발생하든 안하든 무조건 finally문을 실행한다.


사용자 정의 예외

class FruitNotFoundException extends Throwable {
	public FruitNotFoundException(String name) {
		System.out.println(name + "에 해당하는 과일은 없습니다.");
	}
}

public class Test {
	public static String[] arr = {"사과", "오렌지", "포도"};
	public static void main(String[] args) {
		try {
			String name = "토마토";
			for(int i=0; i<arr.length; i++) {
				if(arr[i].equals(name)) {
					arr[i] = null;
				}
			}
			throw new FruitNotFoundException(name);
		}catch(FruitNotFoundException e) {
			e.getMessage();
		}
	}
}

출력 결과
토마토에 해당하는 과일은 없습니다.


FruitNotFoundException 이라는 사용자 정의 예외 클래스를 만들었다.


Exception Handling 마무리

기존에 try ~ catch 문을 가지고 예외 처리 한다는 것을 알고는 있었지만
더욱 예외 처리의 중요성과 필요성에 대해 알게 되었고, 프로젝트 개발 때는 더 신중하게 예외처리를 해야겠다고 생각하게 되었다.
좋은 프로그램은 예외 처리가 잘 되어있어야 한다 😏





👏 참조
https://rebeccacho.gitbooks.io/java-study-group/content/chapter8.html