예외 처리 (Exception Handling)

김찬희·2023년 2월 23일
0

KH정보교육원

목록 보기
10/27

▶ 프로그램 오류

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

▶ 예외 확인

√ Exception 확인하기
Java API Document에서 해당 클래스에 대한 생성자나 메서드를 검색하면 그 메서드가 어떤 Exception을 발생시킬 가능성이 있는지 확인 가능.
→ 발생하는 예외를 미리 확인하여 상황에 따른 예외 처리 코드를 작성할 수 있음
Java 프로그램 실행 중
Exception 상황이 발생하면 해당 상황과 일치하는 예외 클래스를 찾고 객체로 만들어서 문제가 발생한 위치로 던짐.
√ 예시

▶ 예외 클래스 계층 구조

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

▶ Unchecked Exception

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

  • ArithmeticException
    0으로 나누는 경우 발생
    if문으로 나누는 수가 0인지 검사
  • NullPointerException
    Null인 참조 변수로 객체 멤버 참조 시도 시 발생
    객체 사용 전에 참조 변수가 null인지 확인
  • NegativeArraySizeException
    배열 크기를 음수로 지정한 경우 발생
    배열 크기를 0보다 크게 지정해야 함
  • ArrayIndexOutOfBoundsException
    배열의 index범위를 넘어서 참조하는 경우 배열명.length를 사용하여 배열의 범위 확인
  • ClassCastException
    Cast연산자 사용 시 타입 오류
    instanceof 연산자로 객체타입 확인 후 cast연산
  • InputMismatchException
    Scanner를 사용하여 데이터 입력 시 입력 받는 자료형이 불일치할 경우 발생

▶ 사용자 정의 예외

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

public class UserException extends Exception{
	public UserException() {}
    public UserException(String msg) {
    	super(msg);
    }
}
public class UserExceptionController {
	public void method() throws UserException{
    	throw new UserException("사용자정의 예외발생");
    }
}
public class Run {
	public static void main(String[] args) {
    	UserExceptionController uc = new UserExceptionController();
    	try {
    		uc.method();
    	} catch(UserException e) {
    		System.out.println(e.getMessage());
    	}
	}
}

▶ 예외 처리 방법

  1. Exception이 발생한 곳에서 직접 처리
    try~catch문을 이용하여 예외 처리
  • try : Exception 발생할 가능성이 있는 코드를 안에 기술
  • catch : try 구문에서 Exception 발생 시 해당하는 Exception에 대한 처리 기술
    여러 개의 Exception 처리가 가능하나 Exception간의 상속 관계 고려해야 함
  • finally : Exception 발생 여부와 관계없이 꼭 처리해야 하는 로직 기술
    중간에 return문을 만나도 finally 구문은 실행되지만 System.exit();를 만나면 무조건 프로그램 종료
    주로 java.io나 java.sql 패키지의 메서드 처리 시 이용
    √ try ~ catch로 예외 처리
public void method() {
	BufferedReader br = null;
    try {
    	br = new BufferedReader(new InputStreamReader(System.in));
        System.out.print("입력 : ");
        String str = br.readLine();
        System.out.println("입력된 문자열 : " + str);
    } catch (IOException e) {
    	e.printStackTrace();
    }
}

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

public void method() {
	BufferedReader br = null;
    try {
    	br = new BufferedReader(new InputStreamReader(System.in));
        System.out.print("입력 : ");
        String str = br.readLine();
        System.out.println("입력된 문자열 : " + str);
    } catch (IOException e) {
    	e.printStackTrace();
    } finally {
    	try {
        	System.out.println("BufferedReader 반환");
            br.close();
        } catch (IOException e)
        	e.printStackTrace();
        }
    }
}
  1. Exception 처리를 호출한 메서드에게 위임
  • 메서드 선언 시 throws Exception명을 추가하여 호출한 상위 메서드에게 처리 위임
  • 계속 위임하면 main() 메서드까지 위임하게 되고 main() 메서드에서도 처리되지 않는 경우 프로그램이 비정상 종료됨.
    √ throws로 예외 던지기

Exception과 오버라이딩

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

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

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

import edu.kh.exception.user.exception.ScoreInputException;

public class ExceptionService {

	public void ex1() throws IOException {
					// 해당 메서드 내에서 IOException이 발생할 것을
					// 대비한 에외처리 코드
		
		// 예외(Exception) 확인하기
		
		// 키보드 입력을 효율적으로 읽어오는 객체 
		// (Scanner보다 기능은 부족하지만 속도는 빠름)
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		
		System.out.print("입력 : ");
		String input = br.readLine();
		// readLine() : 한 줄 읽어오기 (엔터까지)
		// 왜 빨간줄이 뜰까??
		// - readLine() 메서드는 IOException 이라고 하는 예외를
		//   발생시킬(던질) 가능성이 있기 때문에 
		//   그 상황에 대한 대비책(예외처리)를 하라고 경고하는 것
		
		System.out.println("입력값 : " + input);
		
		// Checked Exception :
		// 컴파일 단계에서 예외가 발생할 가능성이 있는지
		// 반드시 확인해야하는 예외
		
		// -> 공식 API 문서의 메서드 설명에
		//    throws OOOException 으로 작성 되어있는 메서드가 있으면
		//    해당 코드 사용 시 문제가 발생할 것이다 라고 생각하고
		//    그 상황에 대한 예외 처리 코드를 꼭 작성
		
		
		
		// Unchecked Exception :
		// 컴파일 단계에서 예외가 발생할 가능성이 있는지
		// 확인하지 않는 예외
		
		// -> 개발자의 부주의로 나타나는 예외
		//    -> 대부분 쉽게 해결(if)
		
		// -> 치명적인 문제가 아님
		
		System.out.println(5/0);
		
		int[] arr = new int[4];
		System.out.println(arr[10]);
		
		String s = null;
		System.out.println(s.equals("bbb"));
	}
	
	public void ex2() {
		// 예외(Exception) : 코드 수정으로 해결 가능한 에러
		// 예외 처리 : 예외를 처리할 수 있는 구문
		
		// [예외 처리 1] try ~ catch ~ finally
		
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		
		// try : {} 내부에 예외가 발생할 것 같은 코드를 작성하고 실행 시도
		try {
		
			System.out.print("입력 : ");
			String input = br.readLine();
			// public String readLine() throws IOException
			// -> IOException 이라는 Checked Exception을 발생시키는 메서드
			//    -> Exception이 발생할 경우에 대비해서 예외처리 구문 작성(강제)
			
			/* 예외 강제 발생 */
			// -> 예외 객체를 새로 만들어서 던짐
			System.out.println(input);
			throw new IOException();
			
		} catch(IOException e) {
			// catch : try 구문 내에서 던져진 예외가 있을 경우
			//         해당 예외 객체를 잡아채서 catch 구문을 수행해 처리
			
			// catch의 매개 변수에는
			// 던져진 예외 객체를 저장할 수 있는 참조 변수를 작성
			
			System.out.println("키보드 문제로 입력을 진행할 수 없습니다.");
			
			// 발생된 예외가 처리된 후
			// 프로그램이 종료되지 않고 다음 코드가 수행됨
		}
		
		System.out.println("try-catch가 수행되도 프로그램이 종료되지 않음");
	}
	
	public void ex3() {
		
		// 입력 받은 두 정수 나누기
		Scanner sc = new Scanner(System.in);
		
		try {
			System.out.print("입력1 : ");
			int num1 = sc.nextInt();
			System.out.print("입력2 : ");
			int num2 = sc.nextInt();
			
			System.out.printf("%d / %d = %d", num1, num2, num1/num2);
		
		} catch(ArithmeticException e) {
			// 산술적 예외를 잡아 처리
			System.out.println("0으로 나눌 수 없습니다.");
			
		} catch(InputMismatchException e){
			// ** catch문 여러 개 작성 가능 **
			
			// ** 다형성 적용 가능(업캐스팅) **
			// -> 상위 타입 예외를 대개변수로 작성하면
			//    하위 타입의 예외를 모두 처리할 수 있다.
		} catch(Exception e) {
			// !!!!!!!!!! 주의 사항 !!!!!!!!!!
			// - 상위 타입을 처리하는 catch문을
			//   하위 타입을 처리하는 catch문 보다
			//   먼저 작성하면 오류 발생
			// Unreachable catch block for ArithmeticException. 
			// It is already handled by the catch block for Exception
			
			// 해결방법 : 상위 타입 catch를 뒤쪽에 배치해서
			//            하위 타입 catch에 대한 검사가 먼저 진행되게 한다.
			
			// InputMismatchException
			// 스캐너 사용 시 작성법이 잘못되거나 범위를 초과하면 발생하는 예외
			System.out.println("입력이 잘못되었습니다.");
		} finally {
			// finally : 
			// try-catch 구문이 끝난 후 마지막으로 수행
			// ** 예외가 발생 하든 말든 무조건 실행 **
			System.out.println("프로그램 종료");
		}
	}
	
	public void ex4() {
		
		// throw : 예외 강제 발생
		//         ex) throw new IOException();
		
		// throws : 해당 메서드에서 발생한 예외를
		//          호출한 메서드로 던져버리는 예외 처리 방법
		
		System.out.println("ex4() 실행");
		
		try {
			methodA();
			
		} catch(IOException e) {
//			e.getMessage();
			
			e.printStackTrace();
			// Trace : 추적하다
			// -> 예외가 발생한 지점까지의 Stack 구조를 추적하여 출력
			
			System.out.println("catch문 처리");
			
		}
	}
	
	public void methodA() throws IOException {
		System.out.println("methodA() 실행");
		methodB();
	}
	
	public void methodB() throws IOException{
		System.out.println("methodB() 실행");
		
		methodC();
		// methodC()는 IOException을 던질 수도 있기 때문에
		// 호출 시 예외 처리 구문을 작성해야 한다.
	}
	
	public void methodC() throws IOException{
		System.out.println("methodC() 실행");
		throw new IOException();
	}
	
	public void ex5() throws ScoreInputException{
		// 사용자 정의 예외
		// - Java에서 제공하지 않는 예외 상황이 있을 경우
		//   이를 처리하기 위한 예외 클래스를 사용자가 직접 작성
		
		Scanner sc = new Scanner(System.in);
		
		
		System.out.print("점수 입력(0~100) : ");
		int score = sc.nextInt();
		
		if(score <0 || score > 100) {
//			throw new ScoreInputException();
			throw new ScoreInputException("ex5() 호출 중 0~100 사이 범위 초과");
		}
		System.out.println("입력한 점수는 : " + score);
		
	}
	
	public void startEx5() {
		try {
			ex5();  // ScoreInputException이 던져질 가능성이 있음
			System.out.println("가나다");
		} catch(ScoreInputException e) {
//			e.printStackTrace();
			System.out.println("예외 내용 : " + e.getMessage());
			System.out.println("예외처리를 진행");
		} finally {
			System.out.println("프로그램 종료");
		}
	}
}
package edu.kh.exception.run;

import java.io.IOException;

import edu.kh.exception.service.ExceptionService;

public class ExceptionRun {
	
	public static void main(String[] args) throws IOException {
		
		ExceptionService service = new ExceptionService();
		
//		service.ex1();
//		service.ex2();
//		service.ex3();
//		service.ex4();
		service.startEx5();
	}
}
package edu.kh.exception.user.exception;

// 사용자 정의 예외를 만드는 방법!!
// - 이미 존재하는 Java의 Exception Class 중 하나를 상속 받으면 된다
//   (관련성 있는 예외가 있으면 해당 예외 클래스,
//    없으면 Exception 또는 RuntimeException을 상속)

// Checked Exception을 원할 경우 : Exception 상속
// Unchecked Exception을 원할 경우 : RuntimeException 상속

public class ScoreInputException extends Exception{
	
	// 기본 생성자
	public ScoreInputException() {
		super("입력된 점수가 범위를 초과하였습니다.");
	}
	
	// 매개변수 생성자
	public ScoreInputException(String message) {
		super(message);
	}
}
package edu.kh.exception.dto;

import java.io.IOException;

public class Parent {

	public void test() throws IOException{
		System.out.println("부모 test()");
	}
}
package edu.kh.exception.dto;

import java.io.EOFException;

public class Child extends Parent{

	@Override
	public void test() throws EOFException{
		// 오버라이딩 시 예외처리는 같거나 좁은 범위
		
		System.out.println("자식 test()");
	}
}
profile
김찬희입니다.

0개의 댓글