예외 처리(Exception Handling)

oYJo·2024년 12월 24일

Java

목록 보기
22/25
post-thumbnail

예외 처리(Exception Handling)

프로그램 오류

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

  • 오류의 종류
  1. 컴파일 에러 : 프로그램의 실행을 막는 소스 코드상의 문법 에러. 소스 코드 수정으로 해결
  2. 런타임 에러 : 프로그램 실행 중 발생하는 에러. 주로 if문 사용으로 에러 처리
    (ex. 배열의 인덱스 범위를 벗어났거나, 계산식의 오류)
  3. 시스템 에러 : 컴퓨터 오작동으로 인한 에러, 소스 코드 수정으로 해결 불가
  • 오류 해결 방법
    소스 코드 수정으로 해결 가능한 에러를 예외(Exception)라고 하는데 이러한 예외 상황(예측 가능한 에러) 구문을 처리 하는 방법인 예외 처리를 통해 해결

예외 확인

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

  • 예시

IOException이란 예외가 생길 수 있다

프로그램 수행 → 예외 상황 발생 → JVM이 해당 예외 상황에 맞는 Exception 클래스를 찾아 객체로 만듦
→ 객체로 만들어 졌을 때 빨간 글씨가 나오면서 어디서 예외가 발생했는지 안내

예외 클래스 계층 구조

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

시스템 에러 부분은 내가 할 수 있는 부분이 아님
그러기에 Exception, 런타임 부분만 확인

Checked Exception : 확인해야 한다, 예외처리를 필수로 해야한다
Uncecked Exception : 예외처리 선택이다
NullPointerException : Null 부분
IndexOutOfBoundsException : 범위를 넘길 때

Unchecked Exception

  • RuntimeException 클래스
    Unchecked Exception으로 주로 프로그래머의 부주의로 인한 오류인 경우가 많기 때문에
    예외 처리보다는 코드를 수정해야 하는 경우가 많음
  • RuntimeException 후손 클래스
  1. ArithmeticException
    0으로 나누는 경우 발생
    if문으로 나누는 수가 0인지 검사

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

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

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

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

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

사용자 정의 예외

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

예외 처리 방법 : Exception이 발생한 곳에서 직접 처리

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

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

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

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

예외 처리 방법 : Exception 처리를 호출한 메소드에게 위임

  • 메소드 선언 시 throws Exception명을 추가하여 호출한 상위 메소드에게 처리 위임
  • 계속 위임하면 main() 메소드까지 위임하게 되고 main() 메소드에서도 처리되지 않는 경우 프로그램이 비정상 종료됨
  • throws로 예외 던지기

try : 예외가 발생할 것 같은 코드 수행을 시도 → 수행 중 예외 발생 시 예외 개체가 던져짐(throw)
catch : try에서 던져진 예외를 잡아서 처리

Exception과 오버라이딩

오버라이딩 시 throws하는 Exception의 개수와 상관없이 처리 범위가 같거나 후손 이여아 함

  • Exception 클래스는 상속이 될 수록 상위 클래스 보다 예외의 내용이 더 상세하게 기술됨.

class ExceptionTest

package edu.kh.exception.test;

import java.util.Scanner;

public class ExceptionTest {
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);

		while(true) {
			System.out.println("정수 입력(0 입력 시 종료) : ");
			int input = sc.nextInt();
			
			// int a = 99.9; // 자료형이 맞지 않아 연산을 못해서 "컴파일 에러"(코드 틀림)
				// 코드로 수정 가능
				// 1) (int)99.9 : 강제 형변환
				// 2) double a = 99.9; : 변수 자료형을 double로 변경
				// 3) 99.9 -> 99 또는 100으로 변경
			
			if(input == 0) {
				break;
			}
		}
		
		// 런타임 에러 예제
		// 런타임 에러 : 프로그램 수행 중 발생하는 에러
		// 			  주로 if문으로 처리 가능
		
		int[] arr = new int [3]; //  인덱스는 2까지
		
		arr[0] = 0;
		arr[1] = 10;
		arr[2] = 20;
		// arr[3] = 30;
		// java.lang.ArrayIndexOutOfBoundsException : 배열 범위 초과 예외
		
		if(arr.length >= 3) { // 배열의 인덱스 범위 초과시
			System.out.println("배열의 범위를 초과했습니다.");
			
		} else {
			arr[3] = 30;
			
		}
	}
}

예외의 종류와 try - catch 예외 처리 알아보기

class Child

package edu.kh.exception.model.vo;

import java.io.FileNotFoundException;
import java.io.IOException;

public class Child extends Parent{

	@Override
	public void method() throws FileNotFoundException {
		System.out.println("오버라이딩 된 자식 메소드");
		
		// 오버라이딩 시 예외는 같거나 더 좁은 범위
		// * 좁은 범위 == 구체적인 예외
		
		// FileNotFoundException은
		// IOException의 자식 예외이므로 오버라이딩 가능!
		
		// Exception(모든 예외의 부모)은 IOException의 부모 예외이므로 오버라이딩 불가
		
	}
}

class Parent

package edu.kh.exception.model.vo;

import java.io.IOException;

public class Parent {
	public void method() throws IOException {
		// 호출한 곳으로 IOException을 던짐
		// == 해당 메소드는 IOException을 발생시킬 가능성이 있으므로
		// 호춯하는 곳에서 예외 처리를 해야한다
		System.out.println("부모 메소드");
	}
}

class ExceptionRun

package edu.kh.exception.run;

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

public class ExceptionRun {
	public static void main(String[] args) {
		ExceptionService service = new ExceptionService();
		
//		service.ex1();
//		service.ex2();
//		service.ex3();
//		service.ex4();
		service.ex5();
	}
}

class ExceptionService

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 : 선택적으로 예외 처리
	// 2) Checked Exception : 필수적으로 예외 처리

	private Scanner sc = new Scanner(System.in);

	public void ex1() {
		// try - catch 예외 처리

		// try{ } : 괄호 내부에 예외가 발생할 가능성이 있는 코드를 작성한 후 시도

		// catch(예외){ } : try 구문에서 발생한 예외를 잡아서 처리하여
		// 					프로그램이 비정상 종료되지 않도록 함

		System.out.println("두 정수를 입력 받아 나누기한 몫을 출력");

		System.out.print("정수 1 입력 : ");
		int input1 = sc.nextInt();

		System.out.print("정수 2 입력 : ");
		int input2 = sc.nextInt();

		try {
			System.out.println("결과 : " + input1 / input2);
			// java.lang.ArithmeticException : / by zero
			//		산술적 예외 : 0으로 나눌 수 없습니다.
		} catch(ArithmeticException e) {
			// try에서 던져진 예외를 catch문 매개변수로 잡음

			System.out.println("infinity"); // 처리 코드
		} 

		if(input2 != 0) {
			System.out.println("결과 : " + input1 / input2);
		} else {
			System.out.println("infinity");
		}
		// 발생하는 예외 중 일부 예외는 try - catch 구문을 사용하지 않아도
		// 예외 상황을 방지할 수 있다!
		// 여기서 일부 예외는 대부분의 UncheckedException 이다.
	}
	public int inputNumber() {
		int num = 0;

		while(true) {
			try {
				System.out.print("정수 입력 받기 : ");
				num = sc.nextInt(); // Java.util.InputMismatchException : 잘못 입력 예외

				break;

			} catch(InputMismatchException e) {
				System.out.println("잘못 입력하셨습니다. 정수만 입력해주세요");
				sc.nextLine(); // 버퍼에 남아있는 잘못 입력된 문자열을 제거
			}
		}
		return num;
		// num cannot be resolved to a variable
	}
   

	public void ex2() {
		// 정수 3개를 입력 받아 3개의 합계 구하기
		int sum = 0;
		for(int i=0; i<3; i++) {
			sum += inputNumber();
		}
		System.out.println("합계 : " + sum);
	}


	public void ex3() {
		try {
			System.out.print("입력 1 : ");
			int num1 = sc.nextInt(); // InputMismatchException

			System.out.print("입력 2 : ");
			int num2 = sc.nextInt(); // InputMismatchException

			System.out.println("나누기 결과 : " + num1 / num2);
			// ArithmeticException

			// ****************************************

			// 강제로 NullPointerException 발생
			// * NullPointerException : 참조하지 않는 참조변수를 이용해서 코드를 수행할 때 발생
			String str = null;

			System.out.println(str.charAt(0));
			// NullPointerException에 대한 예외처리가 없으면 오류 발생 후 종료


			// ****************************************


			// 관계가 없는 예외는 순서 상관없이 catch문에 작성하면 된다
		} catch(InputMismatchException e) {
			System.out.println("정수가 아닌 문자열이 입력되었습니다");

		} catch(ArithmeticException e) {
			System.out.println("0으로 나눌 수 없습니다");

		} catch(Exception e) {
			// 예외처리 + 다형성
			// Exception 클래스 : 모든 예외의 최상위 부모
			// 다형성 - 업캐스팅 : 부모타입 참조변수로 자식 객체를 참조

			// ** 상위 타입의 예외 클래스를 catch문에 작성하면
			//    다형성 업캐스팅에 의해서 모두 잡아 처리! **
			// == 제일 최상위 부모라서 Exception 클래스가 모두 다 잡아줌
			// 단점 : 구체적으로 어떤 예외에서 문제가 발생하는지는 모름
			// 그래서 맨 밑에다가 써서 좁은 것 부터 큰 걸로 가게 한다


		}
		
		// * catch문 연속 작성 시 Exception 상속 구조 주의!! (다형성)
	}


	public void ex4() {
		
		// 1) try - catch - finally
		// finally : try 구문에서 예외가 발생 하든 말든 무조건 마지막에 수행
		
		try {
			System.out.println( 4 / 0 ); // ArithmeticException
		} catch (ArithmeticException e) {
			System.out.println("예외 처리함");
			
			// 2) catch문 매개변수 활용
			// 매개변수 e : 예외 관련 정보 + 예외 관련 기능
			
			System.out.println(e.getClass()); // 어떤 예외 클래스인가?
			System.out.println(e.getMessage()); // 예외 발생 시 출력된 내용
			
			System.out.println(e); // e.toString();
			// java.lang.ArithmeticException: / by zero
			
			e.printStackTrace(); // 예외가 발생하기까지의 모든 메소드 흐름을 출력
			// 어디서 어떤 에러가 발생했는지 정확히 파악하는 용도
			
		} finally {
			System.out.println("finally는 무조건 수행");
			
		}
	}
	
	// -----------------------------------------------
	
    

	public void ex5() {
		// throws : 호출한 메소드에게 예외를 던짐
		// 			-> 호출한 메소드에게 예외를 처리하라고 위임하는 행위
		
		// throw : 예외 강제 발생 구문(현재 메소드에 예외를 던짐)
		try {
			methodA();
		} catch (Exception e) {
			// Exception : 모든 예외의 최상위 부모
			// -> Exception이 catch 매개변수로 작성되었다 == 예외 종류 상관없이 모두 처리
			System.out.println("예외 처리됨");
			e.printStackTrace();
			// 발생한 예외의 메소드와 위치에 대한 모든 내용을 출력
			// - 예외 발생 지점 추적
		}
	}
	public void methodA() throws IOException{
		methodB();
	}
	
	public void methodB() throws IOException{
		methodC();
	}
	
	public void methodC() throws IOException{
		// methodC() 메소드는 IOException을 발생시킬 가능성이 있으므로
		// 호출하는 곳에서 예외처리를 반드시 해야한다!
		// 단, UnCheckedException은 선택적으로 예외처리할 수 있음
		

		// IOException 예외 강제 발생
		throw new IOException();
		
		// 발생한 예외를 처리하는 방법
		// 1) try - catch로 감싸서 현재 위치에서 처리
		// 2) throws로 호출한 메소드로 예외를 위임하여 처리
		
	}
	
	public void methodD() {
		
		throw new RuntimeException();
		
		// CheckedException : 
		// - RuntimeException과 자식 예외를 제외한 나머지 예외
		// - if문과 같은 단순 코드로 해결이 불가능하며
		// 반드시 예외 처리 구문을 작성해야 하는 예외
		
		// UnCheckedException : 
		// - RuntimeException과 자식 예외를 지칭함
		// - 프로그램 수행 중
		// 개발자의 코딩 실수, 사용자의 잘못된 값 입력으로
		// 흔하게 발생할 수 있는 예외

		// --> 예외처리 구문이 아니라 if문과 같은 단순 코드로 해결 가능
		
		// --> 예외처리 구문을 반드시 작성할 필요없다
		
		
	}
	
}

profile
Hello! My Name is oYJo

0개의 댓글