JAVA/JAVA 정리

[JAVA] 예외 처리

웹코린이 2023. 5. 31. 08:52
728x90

에러와 예외

 

에러란 ?

 에러는 시스템에 비정상적인 상황이 생겼을 때 발생한다. 외부 요인일 수도 있고, 프로그램 구동 중에 발생하는 치명적 오류일 수도 있다. 이런 에러들은 개발자가 처리할 수 없는 영역이다. 

 

에러의 종류

에러의 종류 상황
OutOfMemoryError 프로그램 실행 중 메모리 부족
IOError 입출력 에러
StackOverFlowError 가용 메모리 부족 현상, 재귀 호출 문제 시 발생

 

예외란 ?

 대체로 프로그램 구동 중에 나타나는 오류들을 말한다. 문법적으로는 문제없어 보이지만 실제 운영 중에 생기는 문제들이다. 

 

예외 클래스

 

NullPointerException

 JAVA에서 가장 빈번하게 발생하는 실행 예외로, 객체가 제대로 생성되지 않은 상태에서 사용할 경우 발생한다. 우리가 객체를 선언하면 인스턴스는 객체의 주소를 가지게 되고, 그것을 통해 객체에 접근해 값을 가져온다. 그 때 객체는 정의되었는데 실제 메모리에 생성되지 않았을 경우, 아래와 같이 NullPointerException 예외가 발생한다.

public class Test01 {
	public static void main(String[] args) {
    	
        // 배열을 변수를 만들기만 하고 선언하지 않은 상황
        String[] strArray = null;
        
        // 생성되지 않은 배열을 출력하려고 하면
        System.out.println("strArray[0] = " + strArray[0]);
    }
}

// [ 출력 결과 ]
// NullPointerException 발생

 

NumberFormatException

 이 예외는 잘못된 문자열을 숫자로 형 변환할 때 발생한다. 숫자 형태의 문자열은 정수 타입으로 변경할 수 있으나, 실수 형태의 문자열은 변환할 수 없다. 

public class Test02 {
	public static void main(String[] args) {
    	
        String str01 = "11";
        String str02 = "11.11";
        
        // 정수
        int num01 = Integer.parseInt(str01);
        System.out.println("String to int : " + num01);
        
        // 실수
        int num02 = Integer.parseInt(str02);  // 예외 발생
        System.out.println("String to int : " + num02);
    }
}

이럴 때 발생하는 예외를 NumberFormatException 이라고 한다. 정수는 실수를 포함하지 않기 때문에 소수점을 문자로 인식하기 때문에 발생하는 예외이다.

 

ArrayIndexOutOfBoundsException

 다음은 배열에서 인덱스 범위를 초과해 사용할 때 발생한다.

public class Test03 {
	public static void main(String[] args) {
    	int[] arr = {1, 6, 7, 9, 10};
        System.out.println(arr[6]); // 예외 발생
    } 
}

6번 인덱스는 존재하지 않기 때문에 발생하는 예외이다.

 

예외 처리 과정

  1. 코드 진행 중 예외가 발생하면 JVM에게 알린다.
  2. JVM은 발생한 예외를 분석하여 알맞은 예외 클래스를 생성한다.
  3. 생성된 예외 객체를 발생한 지점으로 보낸다.
  4. 예외가 발생한 지점에서 처리하지 않으면 프로그램은 비정상 종료된다.

4번과 같이 넘어온 예외를 처리해 프로그램을 비정상으로 종료되지 않고, 구동할 수 있도록 해야 한다.

 

예외처리 방법

try - catch 구문

 

try - catch 기본 구조

try {
	// 예외가 발생할 가능성이 있는 코드
} 
catch(예외 클래스명 e) { 
	// 예외 처리 코드
}

예외가 발생할 가능성이 있는 코드를 try안에 넣고 예외가 발생했을 시 catch로 넘어온 예외 클래스를 받아서 처리한다. 

 

예제

public class Test04 {
	public static void main(String[] args) {
    	int result = 0;
        
        try {
        	result = 10 / 0;
            System.out.println("나누기 결과 : " + result);
        }
        catch(ArithmeticException e) {
        	System.out.println("0으로 나누기 할 수 없습니다.");
        }
        System.out.println("프로그램 종료.");
    }
}

// [출력 결과]
// 0으로 나누기 할 수 없습니다.
// 프로그램 종료.

 

다중 catch 사용법

 프로그램을 구동할 때 하나의 예외만 발생한다면 처리하기 어렵지 않지만, try 구문 안에서 예외는 다양하게 발생할 수 있다. 따라서 이럴 때 다중 catch문을 사용한다.

public class Test05 {
	public static void main(String[] args) {
    	Scanner sc = new Scanner(System.in);
        
        try {
        	int[] cards = {4, 5, 1, 2, 7, 8};
            System.out.print("몇번쨰 카드를 뽑으시겠습니까? ");
            int cardIndex = sc.nextInt();
            System.out.println("뽑은 카드 번호는 : " + cards[cardIndex]);
        } 
        catch(InputMismachException e) { // 다른 타입의 입력값을 넣었을 경우
        	System.out.println("잘못 입력하셨습니다. 숫자만 가능합니다.");
        }
        
        System.out.println("프로그램 종료");
    }
}

이렇게 코드를 짰을 경우 인덱스 번호에 없는 값을 넣었을 때 ArrayIndexOutOfBoundsException 이 발생한다. 따라서 다중 catch를 사용하여 아래와 같이 수정하여야 한다.

public class Test05 {
	public static void main(String[] args) {
    	Scanner sc = new Scanner(System.in);
        
        try {
        	int[] cards = {4, 5, 1, 2, 7, 8};
            System.out.print("몇번쨰 카드를 뽑으시겠습니까? ");
            int cardIndex = sc.nextInt();
            System.out.println("뽑은 카드 번호는 : " + cards[cardIndex]);
        } 
        catch(InputMismachException e) { // 다른 타입의 입력값을 넣었을 경우
        	System.out.println("잘못 입력하셨습니다. 숫자만 가능합니다.");
        }
        catch(ArrayIndexOutOfBoundsException e) { // 배열에 없는 인덱스 번호를 입력했을 경우
        	System.out.println("해당 번호의 카드는 없습니다."); 
        }
        
        System.out.println("프로그램 종료");
    }
}

 

finally

 finally란 예외 발생 유무와 상관없이 실행되는 구문이며 생략할 수 있다. 예외 처리를 할 때, 예외와 상관없이 반드시 처리해야 하는 구문들을 작성할 때 사용되며, 보통 외부 연동이나 예외가 발생해도 정상 종료되어야 할 구문들에서 사용한다.

public class Test01 {
	public static void main(String[] args) {
    	Scanner sc = new Scanner(System.in);
        
        try {
        	System.out.println("점수를 입력하세요 ");
            int score = sc.nextInt();
            
            if(score >= 60) {
            	Systme.out.println("합격입니다.");
            } else {
            	System.out.println("아쉽네요. 불합격입니다.");
            }
        }
		catch(InputMismatchException e) {
        	System.out.println("키보드 입력이 올바르지 않습니다.");
        }
        finally {
        	System.out.println("프로그램 종료");
        }
    }
}

// [출력 결과]
// 점수를 입력하세요
// 85
// 합격입니다.
// 프로그램 종료 --> finally이므로 예외 유무와 상관없이 실행 된다.

 

throws

 메소드 내부에서 예외를 처리하지 않고 미룬 후, 해당 메소드를 호출한 쪽에서 예외를 처리하도록 하는 방법을 뜻하고, '예외 던지기' or '예외의 전가'라고 한다.

 try-catch 구문을 사용하지 않고 throws를 사용하는 이유는 메소드에서 예외를 각각 처리하면 메소드 자체의 코드가 길어지거나, 유지 보수 측면에서 효율이 떨어질 수 있다. 따라서 메소드를 호출하는 쪽에서 예외를 처리해주면 좀 더 수월하게 처리할 수 있기 때문에 사용한다.

 

public class Test06 {
	public static void checkYourSelf(Scanner sc) throws InputMismatchException { // 예외 발생 시 호출한 대상으로 떠넘김.
    	System.out.println("1. 사람과 어울리는 것이 좋다. 2. 혼자 있는 것이 좋다.");
        System.out.print("선택) ");
        int check = sc.nextInt();
        
        if(check == 1) {
        	System.out.println("당신은 ENFP");
        } else {
        	System.out.println("당신은 ISFP");
        }
    }

	public static void main(String[] args) {
    	Scanner sc = new Scanner(System.in);
        
        try {
        	System.out.println("=== 성격 유형 검사를 시작합니다. ===");
            ThrowsExceptionExample.checkYourSelf(sc); // 메소드 호출
        }
        catch(InputMismatchException e) {
        	System.out.println("키보드 입력이 잘못되었습니다. ");
        } 
        finally {
        	System.out.println("프로그램 종료");
        }
    }
}

 

임의의 예외 처리

 프로그램의 규칙에 위배되어 예외를 발생해야 할 경우, 임의로 예외를 발생 시킬 수 있다.

 

임의의 예외 처리 특징

정의 예외 발생 상황이 아니더라도, 필요에 의해 강제로 예외를 발생시키는 기능
발생 방법 throw new 예외 객체(메시지);
발생 위치  try-catch 내부 또는 메소드에 예외 던지기가 있는 경우
용도 개발자가 예외를 의도하는 위치

 

예제

pubilc class Test07 {
	public static void main(String[] args) {
    	Scanner sc = new Scanner(System.in);
        
        int val = 0;
        while(true) {
        try {
            	System.out.println("숫자를 입력하세요(0 ~ 50) : ");
                val = sc.nextInt();
                
                if(val == -1) {
                	break;
                }
                if(val < 0 || val > 50) {
                	throw new Exception("숫자의 허용범위가 아닙니다.");
                }
            }
            catch(Exception e) {
				sc.nextLine();
                System.out.println("에러 메시지 : " + e.getMessage());
            }
        }
        System.out.println("프로그램 종료");
    }
}

 

728x90