예외(Exception)

haribo·2021년 2월 27일
1

Java

목록 보기
10/17

Why?

열심히 짠 코드, 잘 굴러가면 정말 좋겠지만 때론 예상치 못한 에러가 나곤 한다. 개발자의 실수도 있을것이고, 프로그램 자체의 문제일 수도 있을것이고, 예상치 못한 경우도 있을것이다.

상황을 하나 가정해보자. Wrapper클래스를 이용하여 문자열을 숫자로 변환할때, 문자열 안에 담긴 값이 숫자라면 문제가 없겠지만, 안에 들어있는 값이 문자라면 에러가 뜰 것이다. Java 프로그램은 예외상황을 만나면 그 즉시 실행을 중단하고 강제 종료된다.

public class Main01 {
	public static void main(String[] args) {
		
		// 문자열의 숫자 변환에 아무런 문제가 없다.
		String year1 = "1980";
		int age1 =2014 - Integer.parseInt(year1) + 1;
		System.out.println(age1);
		
		// 숫자로 변환할 수 없으므로, 에러가 발생한다.
		String year2 = "뭘까요?";
		int age2 = 2014 - Integer.parseInt(year2) + 1;
		System.out.println(age2);
		
		System.out.println("---- 프로그램을 종료합니다. ----");
	}

}

에러의 종류 (시점에 따라 분류된다.)

  • 컴파일 에러 : 소스코드의 구문 오류로 인하여 컴파일이 불가능한 상태. 이클립스에서 빨간색으로 표시되는 경우에 해당한다. 프로그램을 실행하기 전에 발견되므로 상대적으로 고치기 쉽다. (500원 주고 음료수 사와라는 것이다. 애당초 전제가 잘못되어서 실행될 일이 없다.)
  • 런타임 에러 : 구문상의 에러는 없지만, 프로그램이 실행되는 과정에서 다양한 경우의 수에 대응하지 못하여 발생하는 예외상황. 런타임 에러가 발생하면 프로그램은 강제로 종료된다. 우리가 프로그램의 에러라고 부르는 현상은 대부분 런타임 에러에 해당한다. (1000원주고 삼각김밥 사오래서 출발했는데, 편의점이 폐업했다.)

고전적인 에러 방지 방법

public class Main02 {
	public static void main(String[] args) {
		
		int[] arr = new int[3];
		
		for (int i = 0; i < 5; i++) {
			if (i < arr.length) {
				arr[i] = i; // <-- i가 3일때 에러 발생
				System.out.println(arr[i]);	
			}
		}
	}
}
  • 프로그램이 논리적 모순에 빠지지 않도록 프로그래머가 if문을 사용하여 처리해준다.
  • 단점 : 오래된 코드를 튜닝할때, if문의 필요성을 모르고 지워버린다든지 하는 대참사가 일어날 수 있다.

런타임 에러의 종류

  • 논리 오류 : 논리적 모순이 생기지 않도록 코드를 작성해야한다. (실력부족)
  • 자바 가상 머신 자체 오류 : 우리의 영역밖이다. 지구에 운석이 떨어지는걸 막을 순 없듯..
  • 예외(Exception)
    • 프로그램 실행 중에 발생할 수 있는 예기치 않은 사건으로, 프로그래머의 노력으로 처리할 수 있다. (야호!)
    • JVM은 프로그램 실행중에 예외가 발생하면 관련 예외 클래스로부터 예외 객체를 생성하여 프로그램에서 지정된 예외 처리 구문으로 넘긴다.
    • 예외가 발생하면 프로그램에 지정된 예외 처리 구문이 JVM에 의해 호출되고, 이때 전달되는 예외 객체를 넘겨받아 적절한 처리를 수행한다.

자바의 예외처리

  • try ~ catch ~ finally 구문을 통해 대처한다.
try {
	// 기본적으로 실행되는 부분
} catch (예외클래스이름 객체이름) {
	// try 블록에서 예외가 발생(런타임 에러)한 경우 이 곳으로 제어가 넘어온다.
) finally {
	// 에외 상황의 발생 여부에 상관 없이 무조건 실행되는 부분
	// finally 블록은 생략 가능하다.
}
public class Main03 {
	public static void main(String[] args) {
		
		try {
			
			String year2 = "뭘까요?";
			// 숫자 형식 에러(NumberFormatException) 
			// try블록 안에서 에러를 만나면 catch 블록으로 점프한다.
			int age2 = 2014 - Integer.parseInt(year2) + 1;
			
			System.out.println(age2);
			
		} catch (NumberFormatException e) {
	
			System.out.println("에러가 발생했습니다.");
			System.out.println("원인: " + e.getMessage());
			
		}
		
		System.out.println("---- 프로그램을 종료합니다. ----");

	}

}
  • 에러가 생겨도 프로그램 자체가 다운되진 않는다.
  • 원인을 알려준다.
public class Main04 {
	public static void main(String[] args) {
		int[] arr = new int[3];
		
		try {
			for (int i = 0; i < 5; i++) {
				arr[i] = i; // <-- i가 3일때 에러 발생 
										// (ArrayIndexOutOfBoundsException)
				
				System.out.println(arr[i]);	
			
			}
		} catch (ArrayIndexOutOfBoundsException e) {
			System.out.println("에러 발생");
			System.out.println("원인:" + e.getMessage());
		}
		
		System.out.println("---- 프로그램을 종료합니다. ----");
	}

}
  • 배열의 크기를 벗어나는 경우도 예외 처리로 예방할 수 있다.

catch 블록을 사용한 복합 에러 처리

  • 에러가 예상되는 상황에 복수로 명시하는 것이 가능하다.

Exception Class

  • 자바에서 예외 상황을 의미하는 모든 클래스들의 최상위 클래스
  • 이 클래스의 이름으로 catch 블록을 구성하면, 모든 예외 상황에 일괄적으로 대응할 수 있지만, catch블록이 세분화된 경우와는 달리 상황별 개별적인 처리는 불가능하다. (이도저도 아닌 경우... 경우의 수를 전부다 catch로 명시할 수는 없으니까 떄려넣는 클래스)
  • 대부분 맨 마지막 catch 블록에 명시하여 마지막 알 수 없는 에러를 의미하도록 구성. (에런데요 뭔지 모르겠어요)

에러 객체 e의 기능

  • e.getMessage() : 간략한 에러 메시지를 리턴, e.getLocaliseMessage()도 같은 기능을 한다.
  • e.printStackTrace() : 실제 예외 상황시에 출력되는 메시지를 강제로 출력한다. 개발자가 catch 블록 안에서 예외 상황을 분석하기 위한 용도로 사용한다.

복합적 예외상황에 대응하기

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

		// 사용자의 입력값을 가정
		String[] src = { "가", "2", "aaa", "ccc" };
		// 벌써부터 틀리는 스멜이 난다.

		// src의 내용들을 숫자로 변환해서 저장할 배열
		int[] arr = new int[3];
		
		try {
			for(int i = 0; i < src.length; i++) {
				arr[i] = Integer.parseInt(src[i]);
				System.out.println(arr[i]);
			}
		// 발생할 수 있는 예외의 종류에 따라서 catch 블록 세분화
		} catch (NumberFormatException e) {
			System.out.println("입력값에 오류가 있습니다.");
			// 개발자가 보려는 용도로 출력하는 시스템 에러 메시지
			e.printStackTrace();
		} catch (ArrayIndexOutOfBoundsException e) {
			System.out.println("데이터가 너무 많습니다.");
			e.printStackTrace();
		} catch (Exception e) {
			// 예외 종류를 의미하는 모든 클래스는
			// java.lang.Exception 클래스를 상속받으므로
			// 이 블록은 모든 종류의 예외에 대해 대처할 수 있다.
			System.out.println("알 수 없는 예외가 발생했습니다.");
			e.printStackTrace();
		} finally {
			// 이 블록은 예외의 발생 여부에 상관없이 무조건 실행된다.
			System.out.println("데이터 변환 종료");
		}
		
		System.out.println("---- 프로그램을 종료합니다. ----");

	}

}
이 포스트는 itpaper.co.kr에서 제공되는 강의자료를 바탕으로 작성되었습니다.
profile
그림 그리는 백엔드 개발자

0개의 댓글