프로그래밍 언어/JAVA

[JAVA] 자바의 예외처리 Exception

Rateye 2021. 1. 19. 15:07
728x90
반응형

예외 Exception

  • 오류Error 는 시스템의 치명적인 문제로 인해 발생하는 문제이며 개발자가 코드 상에서 해결이 불가능한 문제를 말하지만 예외Exception는 코드 상에서 실수 등으로 인해 발생하는 문제이며 개발자가 코드 상에서 해결이 가능한 문제
  • 예외가 발생할 경우 발생 시점에서 프로그램은 비정상적으로 종료됨
  • 예외가 발생했을 때 프로그램이 비정상적으로 종료되지 않도록 추가적인 작업을 수행하여 정상적으로 종료되도록 하는 것을 예외 처리Exception Handling 라고 한다.
  • 예외가 발생되면 해당 예외에 대한 처리를 담당하는 클래스의 객체를 생성한 뒤 예외 메세지를 포함하여 예외를 던짐(throw) 따라서, 해당 객체를 전달받아 예외를 처리해야함 try ~ catch 문을 사용하여 기본적으로 예외 처리가 가능
  • 예외가 발견되는 시점에 따라 Compile Checked Exception 계열과 Compile ex) RuntimeException 계열(ArithmeticException 등) 계열로 나뉨
    1. Compile Checked Exception : 컴파일 시점(코드 작성 시점)에서 예외 처리 여부를 판단하여 예외가 처리되지 않으면 예외 발생 알림 ex) IOException, SQLException, FileNotFoundException
    2. Compile Unchecked Exception : 컴파일 시점에서는 예외 처리 여부를 검사하지 않고, 실행 시점에서 예외가 발생하면 예외 발생 알림 ex) RuntimeException 계열(ArithmeticException 등)

RuntimeException 계열(Compile Unchecked Exception 계열)

  1. ArithmeticException
    • 수치 데이터를 0으로 나눌 때 발생하는 예외 등
  2. ArrayIndexOutOfBoundsException
    • 배열 인덱스가 잘못 되었을 경우 발생
  3. NullPointerException
    • 참조 변수에 null 값이 저장된 상태에서 참조할 경우 발생 null 이 저장된 참조 변수의 .XXX() 형태로 메서드 호출 시
  4. ClassCastException
    • 객체의 강제 형변환 시 잘못 되었을 경우 발생
public class Ex4 {

	public static void main(String[] args) {

		System.out.println("프로그램 시작!");
		
		int num = 3;
		//		System.out.println(num / 0); // ArithmeticException 예외 발생
		// 예외 발생 시점부터 나머지 모든 코드의 실행이 불가능하며
		// 프로그램이 비정상적으로 종료됨
		
		int[] arr = new int[3];
		//		arr[3] = 10; // ArrayIndexOutOfBoundsException 예외 발생
		
		String str = null;
		//		System.out.println(str.length()); // NullPointerException 예외 발생
		
		Animal ani = new Dog();
		Cat cat = (Cat)ani; // ClassCastException 예외 발생
		
		System.out.println("프로그램 종료!");
		
	}

}

class Animal {}

class Dog extends Animal {}

class Cat extends Animal {}

예외 처리 Exception Handling

  • 예외가 발생할 경우 예외가 발생하지 않도록 예외 발생 코드를 수정하는 것이 아니라 예외가 발생하더라도 별도의 작업을 통해 작업이 끝까지 수행되어 프로그램이 정상적으로 종료될 수 있도록 처리하는 것
  • try~catch 블록을 사용하여 기본적인 예외 처리 try 블록 내에 예외가 발생할 것으로 예상되는 코드를 기술하고 catch 블록에서 예외를 전달받을 클래스를 선언하여 예외 감지 후 해당 예외를 처리할 코드를 기술

기본 문법

try {
 		// 예외 발생 예상 코드들...
 } catch(예외클래스명 변수명) {
 		// 해당 예외 발생 시 수행할 코드들...
 }
System.out.println("프로그램 시작!");
		
		int num = 3, num2 = 0;
		// 0으로 나눗셈 수행 시 예외가 발생하는데 
		// 0으로 나누지 않도록 데이터를 수정하는 것 등은 예외처리가 아님
		try {
			System.out.println(num / num2); // 예외 발생 예상 코드
			// try 문 내에서 예외 발생 지점부터 아래쪽의 모든 문장들은
			// 실행되지 못하고 바로 catch 블록으로 이동함
			System.out.println("try 문 종료"); // 예외 발생 시 실행되지 못함
		} catch(ArithmeticException e) {
			// ArithmeticException 예외 발생 시에 자동으로 실행되는 부분
			// => ArithmeticException 예외가 발생하지 않으면 실행되지 못함
			System.out.println("0으로 나눌 수 없습니다!");
		}
		
		// 예외 발생 여부와 관계없이 실행되는 코드들...
		System.out.println("프로그램 종료!");

두 가지 이상의 예외가 발생할 가능성이 있는 경우

두 가지 이상의 예외를 각각의 catch 블록에서 처리

  1. 두 개 이상의 catch 블럭을 작성하여 각 예외를 별도로 처리 단, 예외 발생 시 윗쪽 catch 블럭부터 아래쪽으로 순서대로 탐색
	System.out.println("프로그램 시작!");
		
		try {
			num2 = 0;
//			System.out.println(num / num2); // 예외 발생 예상 코드
			
			String str = null;
			System.out.println(str.length()); // 예외 발생 예상 코드
		} catch (ArithmeticException e) {
			System.out.println("ArithmeticException 만 처리!");
		} catch (NullPointerException e) {
			System.out.println("NullPointerException 만 처리!");
		} 
		
		System.out.println("프로그램 종료!");

두 가지 이상의 예외를 하나의 catch 블록에서 처리

1. 하나의 catch 블록에 2개 이상의 예외클래스를 모두 지정할 경우

OR 기호 | 를 사용하여 예외 클래스끼리 묶어줌

 

		System.out.println("프로그램 시작!");
		
		try {
			num2 = 0;
			System.out.println(num / num2); // 예외 발생 예상 코드
			
			String str = null;
			System.out.println(str.length()); // 예외 발생 예상 코드
		} catch (ArithmeticException | NullPointerException e) {
			System.out.println("ArithmeticException 과 NullPointerException 만 처리!");
		}
		
		System.out.println("프로그램 종료!");

 

2. 하나의 catch 블록에 상위 예외클래스를 사용하여 처리

		System.out.println("프로그램 시작!");
		
		try {
			num2 = 0;
//			System.out.println(num / num2); // 예외 발생 예상 코드
			
			String str = null;
			System.out.println(str.length()); // 예외 발생 예상 코드
//		} catch (RuntimeException e) {
//			System.out.println("RuntimeException 계열만 처리!");
		} catch (Exception e) {
			System.out.println("모든 예외 처리!");
//		} catch (RuntimeException e) {
//			System.out.println("RuntimeException 계열만 처리!");
		}
		
		// => 주의!! 여러개의 예외 클래스를 사용할 경우
		//    예외클래스간의 상속관계에서 하위클래스 타입부터
		//    역순으로 처리해야된다!
		
		
		System.out.println("프로그램 종료!");

 

반응형

 

try ~ catch ~ finally 블록

  • try 블록에서 예외 발생 여부와 관계 없이 무조건 실행해야하는 문장들은 finally 블록 내에 기술함
  • 예외가 발생하거나 발생하지 않아도 무조건 실행되며 심지어 return 문을 만나 메서드 실행이 종료될 때도 finally 블록 내의 코드를 실행한 후 종료
  • 주로, 자원 반환을 위한 코드를 기술 ex. DB 접속 종료, 입출력 스트림 종료 등

기본 문법

try {
 		// 예외 발생 예상 코드들...
 } catch(예외클래스명 변수명) {
 		// 해당 예외 발생 시 수행할 코드들...
 } finally {
 		// 예외 발생 여부와 관계없이 수행할 코드들..
 }
public static void exceptionMethod(int num) {
		/*
		 * 예외가 발생할 경우 실행되는 코드 순서
		 * 1 -> 3 -> 4(finally) -> 5 -> 메서드 종료
		 * (return 문을 만나지 않으므로 try 블록 바깥 코드도 실행됨)
		 * 
		 * 예외가 발생하지 않을 경우 실행되는 코드 순서
		 * 1 -> 2 -> return -> 4(finally) -> 메서드 종료
		 * (return 문을 만나더라도 finally 블록을 우선 실행함)
		 */
		
		try {
			System.out.println("Code1 : num = " + num);
			
			int total = 1 / num; // 예외 발생 예상 코드
			
			System.out.println("Code2 : 예외 발생 없음!");
			
			return; // 메서드 실행 도중 return 문을 만나면
			// 메서드를 호출한 곳으로 즉시 되돌아가지만
			// finally 블록 내의 코드가 있을 경우 해당 코드를 실행한 후 
			// 메서드를 호출한 곳으로 돌아감
		} catch (Exception e) {
			System.out.println("Code3 : 예외 발생 후 처리 완료!");
		} finally {
			System.out.println("Code4 : 예외 발생 여부와 관계없이 항상 실행!");
		}
		
		System.out.println("Code5 : try 문 종료 후 실행!");
		
	}
System.out.println("프로그램 시작!");
		
		int num1 = 10, num2 = 0;
		String str = null;

		try {
			// ArrayIndexOutOfBoundsException 발생 가능성이 있는 코드
		//			System.out.println(num1 / num2);
			
			// NullPointerException 발생 가능성이 있는 코드
		//			System.out.println(str.length());
			
			System.out.println("예외 없음!");
		} catch (Exception e) {
			System.out.println("모든 예외 처리!");
		} finally {
			System.out.println("예외 발생 여부와 관계없이 무조건 실행!");
		}
		
		System.out.println("프로그램 종료!");
		
		System.out.println("===========================");
		
		System.out.println("메서드 호출 시작!");
		
		exceptionMethod(0);
		
		System.out.println("메서드 호출 종료!");

예외 처리에 대한 위임(throws 키워드)

  • 예외 처리를 예외가 발생한 곳에서 직접 처리하지 않고 메서드를 호출한 곳으로 발생한 예외를 떠넘길 때 사용
  • 메서드 정의 시 마지막에 throws 키워드를 명시하고 예외가 발생할 것으로 예상하는 클래스명을 지정하면 해당 예외 발생 시 try ~ catch 블록이 없을 경우 메서드를 호출한 곳으로 예외를 던진다. 이 때, 위임받은 곳에서 해당 예외를 처리(try ~ catch)해야함
  • 메서드를 호출하는 외부에서 메서드 호출 시점에 예외 발생 여부를 판별하고, 예외를 처리할 수 있도록 해주며 또한, 메서드를 호출하는 Caller 에서 여러 메서드 내의 예외를 한꺼번에 묶어서 처리할 수 있도록 해준다!
public static void 과장() throws Exception { // 모든 예외를 위임
		// 2개의 예외를 위임받아 과장이 직접 처리할 경우
		// => 각각의 예외를 따로 catch 하거나 | 기호로 다중 catch 묶을 수 있음
		//		try {
		//			대리();
		//		} catch (ClassNotFoundException | SQLException e) {
		//			System.out.println("과장이 ClassNotFoundException & SQLException 예외를 직접 처리!");
		//		}
		
		// 2개의 예외를 위임받아 과장이 직접 처리하지 않고 위임할 경우
		// => 각 예외 클래스 모두를 throws 뒤에 명시하거나
		//    예외 클래스를 통합하여 상위 클래스명을 명시할 수도 있다!
		대리();
	}
	
	public static void 대리() throws SQLException, ClassNotFoundException {
		// 예외를 위임받아 대리가 직접 처리할 경우
//		try {
//			사원();
//			사원2();
//		} catch (ClassNotFoundException e) {
//			System.out.println("대리가 ClassNotFoundException 예외를 위임받아 처리!");
//		} catch (SQLException e) {
//			System.out.println("대리가 SQLException 예외를 위임받아 처리!");
//		}
		
		// 대리가 위임받아 직접 처리하지 않고 과장()에게 위임할 경우
		사원(); // SQLException
		사원2(); // ClassNotFoundException
	}
	
	public static void 사원() throws SQLException {
		// 예외 발생 시 예외가 발생한 곳에서 직접 예외를 처리하는 방법
//		try {
//			Connection con = DriverManager.getConnection(""); // SQLException
//		} catch (SQLException e) {
//			System.out.println("사원이 SQLException 예외를 직접 처리!");
//		} 
		
		// 예외 발생 시 예외가 발생한 곳에서 처리하지 않고
		// 예외가 발생한 메서드를 호출한 곳으로 예외를 떠넘기는 방법
		// => 발생할 것으로 예상되는 예외클래스명을
		//    메서드 선언부 마지막에 throws 키워드 뒤에 명시
		Connection con = DriverManager.getConnection(""); // SQLException
		// => throws SQLException 명시할 경우
		//    현재 사원() 메서드를 호출한 대리() 메서드 내의
		//    사원() 메서드 호출 코드에 예외 처리가 위임됨
	}
	
	public static void 사원2() throws ClassNotFoundException {
		// ClassNotFoundException 예외를 대리에게 위임
		Class.forName("");
	}
		try {
			과장();
		} catch (Exception e) {
			System.out.println("main() 메서드(사장)가 모든 예외 위임받아 처리!");
		}

throw 키워드

  • 개발자가 직접 예외를 발생시킬 때 사용
  • 자바 문법에서 위반되지 않는 코드라도 강제로 예외 발생 가능
  • 예외를 발생시킬 위치에 throw 키워드 뒤에 발생시킬 예외 클래스의 객체를 생성하여 강제로 예외를 발생시켜 던짐
class InvalidScoreException extends Exception {

	// 1. 생성자를 정의하여 예외 메세지를 전달받을 수 있는
	//    String 타입 파라미터 1개를 갖는 생성자 오버라이딩
	public InvalidScoreException(String message) {
		// 전달받은 메세지를 Exception 클래스의 생성자에서 초기화 수행
		super(message);
	}
	
}
public static void score(int score) throws Exception {
		// int형 파라미터에 정수 150 이 전달될 경우 문법적으로는 맞지만
		// 점수 범위(0 ~ 100) 기준으로 논리적으로 맞지 않은 값이 전달됨
		// 따라서, 이 때 개발자가 직접 강제로 예외를 발생시킬 수 있다.
		if(score >= 0 && score <= 100) {
			System.out.println("학점 계산!");
		} else {
//			System.out.println(score + " : 점수 입력 오류!");
			
//			try {
//				// 강제로 예외를 발생시키기 위해 throw 키워드 사용
//				// => throw 키워드 뒤에 예외 발생 클래스 객체 생성
//				//    이 때, 파라미터로 예외 메세지 문자열을 전달 가능
//				throw new Exception(score + " : 점수 입력 예외 발생!");
//				// => try~catch 를 통해 예외 처리 또는 throws 를 통해 예외 위임
//			} catch (Exception e) {
//				// 예외 처리 시 전달받은 객체의 메서드를 통해 예외 정보 확인
//				// 1. e.getMessage() : 예외 객체의 메세지 정보를 문자열로 리턴
////				System.out.println(e.getMessage());
//				
//				// 2. e.printStackTrace() : 예외 클래스명, 메세지, 발생 위치 등
//				//    예외에 대한 상세 정보를 출력
//				e.printStackTrace();
//			}
			
			// 외부로 예외를 떠넘길 경우
			throw new Exception(score + " : 점수 입력 예외 발생!");
			
		}
	}
	
	public static void score2(int score) throws InvalidScoreException {
		// 사용자 정의 예외 클래스를 사용하여 강제로 예외 발생시키기
		if(score < 0 || score > 100) {
			throw new InvalidScoreException(score + " : 점수 입력 예외 발생!");
		}
	}
		try {
			score(150);
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		try {
			score2(150);
		} catch (InvalidScoreException e) {
			e.printStackTrace();
		}
		
		System.out.println("프로그램 종료!");
728x90
반응형