JAVA_10_예외 처리

송지윤·2024년 2월 19일

Java

목록 보기
16/23

예외 처리 (Exception Handling)

프로그램 오류

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

빨간글씨 비정상종료 == Error
소스코드 수정으로 처리할 수 있는 에러 == Exception

오류의 종류

  1. 컴파일 에러 : 프로그램의 실행을 막는 소스 코드상의 문법 에러. 소스 코드 수정으로 해결. (빨간줄 생기는 거)
  2. 런타임 에러 : 프로그램 실행 중 발생하는 에러. 주로 if문 사용으로 에러 처리
    ex) 배열의 인덱스 범위를 벗어났거나, 계산식의 오류
  3. 시스템 에러 : 컴퓨터 오작동으로 인한 에러, 소스 코드 수정으로 해결 불가

오류 해결 방법

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

예외 확인

Exception 확인하기

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

예시

BufferedReader : 입출력에 관련된 API
java.io.BufferedReader의 readLine() 메소드

이걸 처리해야한다고 나와있음

구글에 java api 17 검색해서 확인할 수 있음

예외 클래스 계층 구조

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(시도하다) : 예외가 발생할 것 같은 코드 수행을 시도
-> 수행 중 예외 발생 시, 예외 객체가 던져짐(throw)

catch(던진 것을 받다) : try에서 던져진 예외를 잡아서 처리
-> 예외 잡아서 처리했기 때문에 프로그램이 종료되지 않음

  • try : Exception 발생할 가능성이 있는 코드를 안에 기술
    ex) arr[5] = 40;
  • catch : try 구문에서 Exception 발생 시 해당하는 Exception에 대한 처리 기술
    여러 개의 Exception 처리가 가능하나 Exception간의 상속 관계 고려해야 함
    ex) System.out.println("배열 범위 초과");
  • finally : Exception 발생 여부와 관계없이 꼭 처리해야 하는 로직 기술
    중간에 return문을 만나도 finally구문은 실행되지만 System.exit();를 만나면 무조건 프로그램 종료
    주로 java.io나 java.sql 패키지의 메소드 처리 시 이용

try 사용 예

	public void ex1() {
		
		System.out.println("두 정수를 입력받아 나누기한 몫을 출력");
		
		System.out.print("정수 1 입력 : ");
		int input1 = sc.nextInt();
		
		System.out.print("정수 2 입력 : ");
		int input2 = sc.nextInt();

예외 처리 전

		System.out.println("결과 : " + input1/input2);

결과

두 정수를 입력받아 나누기한 몫을 출력
정수 1 입력 : 5
정수 2 입력 : 0
java.lang.ArithmeticException: / by zero

산술적 예외 발생 (0으로 나눌 수 없다.)

예외 처리 후

		try {
			System.out.println("결과 : " + input1/input2);
			// 예외 발생할 것 같은 코드 넣어줌
		} catch(ArithmeticException e) {
			// try 에서 던져진 예외를 catch 문의 매개변수 e 로 잡음.
			System.out.println("infinity"); // 처리코드
		}

결과

두 정수를 입력받아 나누기한 몫을 출력
정수 1 입력 : 5
정수 2 입력 : 0
infinity

if문으로 예외 처리
위와 같은 결과 출력

		if(input2 != 0) {
			System.out.println("결과 : " + input1/input2);
		} else {
			System.out.println("infinity");
		}
		
		// 발생하는 예외 중 일부 예외 try~catch 구문 사용 안해도
		// 예외 상황 방지할 수 있다. (if-else 사용)
		// 일부 예외 == 대부분 Unchecked Exception
		
	}

try ~ catch 사용 예

InputMismatchException
숫자 입력 창에 숫자 아닌 거 입력했을 때 발생

ArithmeticException
산술 오류

NullPointerException
null 값에서 무언가를 꺼내려 했을 때 발생

	public void ex2() {

		try {
			System.out.print("입력 1 : ");
			int num1 = sc.nextInt();
			
			System.out.print("입력 2 : ");
			int num2 = sc.nextInt();

			System.out.println("결과 : " + num1/num2);

			String str = null;
			
			System.out.println(str.charAt(0));
			
		} catch(InputMismatchException e) { // import 해줘야함
			System.out.println("타입에 맞는 값만 입력해주세요");
		} catch(ArithmeticException e) {
			System.out.println("0 으로 나눌 수 없습니다.");
		} catch(Exception e) {
			
			System.out.println("뭔지 모르겠으나 예외가 발생해서 처리함.");
			e.printStackTrace();

		}
	}

Exception 클래스 : 모든 예외의 최상위 부모
다형성 - 업캐스팅 : 부모타입 참조변수로 자식객체 참조
(모든 자식 예외들을 다 잡아서 처리 가능)
상위 타입의 예외 클래스를 catch 문에 작성하면 다형성 업캐스팅에 의해 모두 잡아서 처리 가능
발생한 예외에 대한 메서드와 위치에 대한 모든 내용을 출력

e.printStackTrace();
예외 발생 지점 추적
java.lang.NullPointerException: Cannot invoke "String.charAt(int)" because "str" is null at edu.kh.exception.model.service.ExceptionService.ex2(ExceptionService.java:97) at edu.kh.exception.run.ExceptionRun.main(ExceptionRun.java:12)

try ~ catch ~ finally 사용 예

finally : try 구문에서 예외가 발생 하든 말든 무조건 마지막에 수행

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

			System.out.println("결과 : " + num1/num2);
			
		} catch(ArithmeticException e) {
			System.out.println("예외 처리 됨");
			
		} finally {

			System.out.println("무조건 수행 됨");
			sc.close();

		}
	}

Resource leak: 'sc' is never closed
메모리 누수 발생
sc.close;
스캐너 통로 닫기 : 메모리 누수 방지
다 사용했으면 닫아줘야함
통로 닫기 할 때 finally 에 작성해줌

catch 문 매개변수 활용

매개변수 e : 예외 관련된 정보 + 예외 관련 기능

System.out.println(e.getClass()); // 어떤 예외 클래스인가?
java.lang.ArithmeticException

System.out.println(e.getMessage()); // 예외 발생시 출력된 내용
/ by zero

System.out.println(e); // e.toString()
java.lang.ArithmeticException: / by zero

예외 처리 방법

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

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

throws로 예외 던지기

throws : 호출한 메서드에게 예외를 던짐
-> 호출한 메서드에게 예외를 처리하라고 위임하는 행위
throw : 예외를 강제 발생 구문

	public void ex4() {

		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 {
		// 예외 강제 발생
		throw new IOException();
		
//		try {
//			throw new IOException();
//			
//		} catch(IOException e) {
//			e.printStackTrace();
//		}
		// 메서드마다 다 써줘야함
	}

Exception과 오버라이딩

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

왼쪽 IOException 까지는 가능 범위가 같으니까

사용자 정의 예외

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());
      }
   }
}

0개의 댓글