예외 처리(Exception Handling)

웃음인·2025년 4월 15일

Java

목록 보기
21/37
post-thumbnail

프로그램 오류

프로그램 수행 시 치명적 상황이 발생하여 비정상 종료 상황이 발생한 것
프로그램 에러라고도 함

🔸 오류의 종류

  1. 컴파일 에러 : 프로그램의 실행을 막는 소스 코드상의 문법 에러. 소스 코드 수정으로 해결.

  2. 런타임 에러 : 프로그램 실행 중 발생하는 에러. 주로 if문 사용으로 에러 처리
                      ex) 배열의 인덱스 범위를 벗어났거나, 계산식의 오류

  3. 시스템 에러 : 컴퓨터 오작동으로 인한 에러, 소스 코드 수정으로 해결 불가


🔸 오류 해결 방법

소스 코드 수정으로 해결 가능한 에러예외(Exception)라고 하는데
이러한 예외 상황(예측 가능한 에러) 구문을 처리 하는 방법인 예외 처리를 통해 해결




Exception 확인하기

Java API Document 에서 해당 클래스에 대한 생성자나 메소드를 검색하면
그 메소드가 어떤 Exception을 발생시킬 가능성이 있는지 확인 가능
→ 발생하는 예외를 미리 확인하여 상황에 따른 예외 처리 코드를 작성할 수 있음

ex) java.io.BufferedReader의 readLine() 메서드


예외 클래스 계층 구조

Exception과 Error 클래스 모두 Object 클래스의 자손이며
모든 예외의 최고 조상은 Exception 클래스
반드시 예외 처리해야 하는 Checked Exception과
해주지 않아도 되는 Unchecked Exception으로 나뉨

Unchecked Exception

🔸 RuntimeException 클래스

Unchecked Exception으로 주로 프로그래머의 부주의로 인한 오류인 경우가 많기 때문에
예외 처리보다는 코드를 수정해야 하는 경우가 많음


  • RuntimeException 후손 클래스

    • ArithmeticException
      0으로 나누는 경우 발생
      if문으로 나누는 수가 0인지 검사

    • NullPointerException
      null인 참조 변수로 객체 멤버 참조 시도 시 발생
      객체 사용 전에 참조 변수가 null인지 확인

    • NegativeArraySizeException
      배열 크기를 음수로 지정한 경우 발생
      배열 크기를 0보다 크게 지정해야 함

    • ArrayIndexOutOfBoundsException
      배열의 index범위를 넘어서 참조하는 경우
      배열명.length를 사용하여 배열의 범위 확인

    • ClassCastException
      Cast연산자 사용 시 타입 오류
      instanceof 연산자로 객체타입 확인 후 cast연산

    • InputMismatchException
      Scanner를 사용하여 데이터 입력 시
      입력 받는 자료형이 불일치할 경우 발생


예외 처리 방법

1. Exception이 발생한 곳에서 직접 처리

try-catch문을 이용하여 예외 처리

  • try : Exception 발생할 가능성이 있는 코드를 안에 기술

  • catch : try 구문에서 Exception 발생 시 해당하는 Exception에 대한 처리 기술
             여러 개의 Exception 처리가 가능하나 Exception 간의 상속 관계 고려해야 함

  • finally : Exception 발생 여부와 관계없이 꼭 처리해야 하는 로직 기술
              중간에 return문을 만나도 finally구문은 실행되지만
              System.exit();를 만나면 무조건 프로그램 종료
              주로 java.iojava.sql 패키지의 메소드 처리 시 이용

  • try-catch로 예외 처리
package edu.kh.exception.model.service;

import java.io.IOException;
import java.util.InputMismatchException;
import java.util.Scanner;

public class ExceptionService {
	
	// 예외(Exception) : 소스 코드의 수정으로 해결 가능한 오류
	
	// 예외는 두 종류로 구분됨
	// 1) Unchecked Exception : 선택적으로 예외 처리 (ex. Runtime Exception ..)
	// 2) Checked Exception :  필수적으로 예외 처리(ex. IOException, 나머지..)
	
	private Scanner sc = new Scanner(System.in);
	
	public void ex1() {
		System.out.println("두 정수를 입력받아 나누기 한 몫을 출력");
		
		System.out.println("정수 1 입력 : ");
		int input1 = sc.nextInt();
		
		System.out.println("정수 2 입력 : ");
		int input2 = sc.nextInt();
		
		
		try {
			// 예외가 발생할 것 같은 코드
			System.out.print("결과 : " + input1 / input2); 
			// 산술적 예외 (0으로 나눌 수 없다)
			
		} catch(ArithmeticException e) {
			// try에서 던져진 예외를 catch 문의 매개변수 e로 잡음
			System.out.print("infinity"); // 처리 코드
		}
		
		if(input2 != 0) {
			System.out.print("결과 : " + input1 / input2); 
		} else {
			System.out.print("infinity");
		}
		
		// 발생하는 에외 중 일부 예외 try = catch 사용 안 해도
		// if - else 예외 상황 방지 가능
		// 일부 예외 == 대부문 Unchecked Exception
	}

	public void ex2() {
		// 여러가지 예외에 대한 처리 방법
		
		try {
			System.out.print("정수 1 입력 : "); // InputMismatchException
			int input1 = sc.nextInt(); // int 형 아닌 다른형 넣었을 때
			
			System.out.print("정수 2 입력 : "); // InputMismatchException
			int input2 = sc.nextInt(); // int 형 아닌 다른형 넣었을 때
			
			System.out.println("결과 : " + input1 / input2);  // ArithmeticException
			// input2에 0이 들어왔을 때
			
			String str = null;
			
			System.out.println( str.charAt(0) ); // NullPointerException
			
			
			// InputMismatchException, ArithmeticException 둘 다 RuntimeException 후손
			// RuntimeException 은 Exception 후손
			// Exception
			//   ㄴ RuntimeException
			//		     ㄴ InputMismatchException, ArithmeticException
			
			
		} catch(InputMismatchException e) {
			System.out.println("타입에 맞는 값만 입력하세요");
			
		} catch(ArithmeticException e) {
			System.out.println("0으로 나눌 수 없습니다");
			
		} catch(Exception e) { // 상속관계 잘 생각해서 순서에 맞게 작성하기 !!!
			// Exception 클래스 : 모든 예외의 최상위 부모
			// -> 다형성 - 업캐스팅: 부모타입 참조변수로 자식객체 참조
			
			// ** 상위 타입의 예외 클래스를 catch문에 작성하면
			// 	  다형성 업캐스팅에 의해 모두 잡아서 처리 가능 !! **
			
			System.out.println("뭔지 모르겠으나 예외가 발생해서 처리함..");
			e.printStackTrace();
			// 발생한 예외에 대한 메서드와 위치에 대한 모든 내용을 출력
			// 예외 발생 지점 추적 메서드
		}
	}

  • try-catch-finally로 예외 처리 후 반드시 수행해야 하는 로직 처리
[service]

	public void ex3() {
		// try = catch = finally
		// finally : try 구문에서 예외가 발생하든 안 하든 무조건 마지막에 수행
		
		try {
			System.out.print("정수 1 입력 : "); // InputMismatchException
			int input1 = sc.nextInt(); // int 형 아닌 다른형 넣었을 때
			
			System.out.print("정수 2 입력 : "); // InputMismatchException
			int input2 = sc.nextInt(); // int 형 아닌 다른형 넣었을 때
			
			System.out.println("결과 : " + input1 / input2);  // ArithmeticException
			
		} catch(ArithmeticException e) {
			System.out.println("예외 처리 됨");
			
			// catch문 매개변수 활용
			// 매개변수 e : 예외 관련된 정보 + 예외 관련 기능
			
			 System.out.println( e.getClass() ); // 어떤 예외 클래스인가 ?
			 // class java.lang.ArithmeticException
			 
			 System.out.println( e.getMessage() ); // 예외 발생 시 출력된 내용
			 // / by zero
			 
			 System.out.println(e); // e.toString()
			// java.lang.ArithmeticException: / by zero
			 
			 e.printStackTrace();
			 
		} catch(InputMismatchException e) {
			System.out.println("숫자만 넣어라..");
			
		} finally {
			System.out.println("무조건 수행됨");
			sc.close(); // 스캐너 통로 닫기 : 메모리 누수 방지
			
		}
	}

2. Exception 처리를 호출한 메소드에게 위임

  • 메소드 선언 시 throws Exception명을 추가하여 호출한 상위 메소드에게 처리 위임

  • 계속 위임하면 main() 메소드까지 위임하게 되고
    main() 메소드에서도 처리되지 않는 경우 프로그램이 비정상 종료됨.


  • throws로 예외 던지기

[service]

	public void ex4() {
		// throw / throws
		
		try {
			methodA(); 
			
		} catch(Exception e) {
			// Exception : 모든 예외의 최상위 부모
			// == 예외 종류 상관없이 모두 처리 
			System.out.println("methodC 에서부터 발생한 예외를 ex4에서 잡아 처리함");
		}
	}
	
	public void methodA() throws IOException {
		methodB();
		
	}
	
	public void methodB() throws IOException {
		methodC();
	}
	
	public void methodC() throws IOException { 
					// throws : 호출된 메서드에게 예외를 던짐
					// -> 호출한 메서드에게 예외를 처리하라고 위임하는 행위
		
		// throw : 예외 강제 발생 구문
		throw new IOException();
	}
}
package edu.kh.exception.run;

import edu.kh.exception.model.service.ExceptionService;
import edu.kh.exception.model.service.UserException;

public class ExceptionRun {
	public static void main(String[] args) {
		new ExceptionService().ex4();
		
		
		
		try {
			new ExceptionService().ex5();
			
		} catch (UserException e) {
			System.out.println(e.getMessage());
		}
	}
}



Exception과 오버라이딩

오버라이딩 시 throws하는 Exception의 개수와 상관없이 처리 범위가 같거나 후손 이여아 함
* Exception 클래스는 상속이 될 수록 상위 클래스 보다 예외의 내용이 더 상세하게 기술됨


사용자 정의 예외

Java API에서 제공하는 Exception Class 만으로는 처리할 수 없는 예외가 있을 경우
사용자의 필요에 의해 생성하는 Exception Class.
Exception 발생하는 곳에서 throw new 예외클래스명()으로 발생

package edu.kh.exception.model.service;

public class UserException extends Exception{
	
	public UserException() {}
	
	public UserException(String msg) {
		super(msg);
	}
		 
}
package edu.kh.exception.model.service;

import java.io.IOException;
import java.util.InputMismatchException;
import java.util.Scanner;

public class ExceptionService {
	
	private Scanner sc = new Scanner(System.in);

	public void ex5() throws UserException {
		// 사용자 정의 예외
		throw new UserException("사용자정의 예외 발생");
	}
}
package edu.kh.exception.run;

import edu.kh.exception.model.service.ExceptionService;
import edu.kh.exception.model.service.UserException;

public class ExceptionRun {
	public static void main(String[] args) {
		new ExceptionService().ex4();
		
		
		
		try {
			new ExceptionService().ex5();
			
		} catch (UserException e) {
			System.out.println(e.getMessage());
		}
	}
}

0개의 댓글