안녕하세요, 여행벌입니다.

오늘은 자바에서 예외란 무엇이고, 어떻게 처리하는지에 대해서 포스팅해보겠습니다.


예외

 자바에서 예외란 "예외적인 상황"을 의미합니다. 단순한 문법 오류 뿐만 아니라, 실행 중간에 발생하는 "정상적이지 않은 상황"을 모두 뜻하는 표현입니다. 자바 가상머신은 예외가 발생하면 그 내용을 간단히 출력하고 프로그램을 종료합니다.

예외 처리(try ~ catch)

 자바는 예외 상황 별로 그 상황을 알리기 위한 클래스를 정의하고 있습니다. 이러한 클래스를 가리켜 "에외 클래스" 라고 합니다. 자바에서는 예외가 발생하면 해당 예외 클래스의 인스턴스를 생성합니다. 이때, 이 인스턴스를 프로그래머가 처리해준다면, 예외는 처리된 것으로 간주하여 프로그램을 종료하지 않습니다.

 

 예외가 발생했을 때, 예외를 처리하고 프로그램을 정상적으로 실행하기 위해서는 try ~ catch 구문을 이용해야 합니다. 기본 구조는 다음과 같습니다.

try{
	// 관찰영역
}
catch(Exception name){
	// 예외 처리 영역
}
// try ~ catch 다음 영역

 예외가 발생할 수 있는 코드는 try 로 관찰 영역 안에 포함시키고, try 영역 안에서 발생하는 예외를 catch 영역에서 처리해주면 예외가 발생했을 때, 프로그램을 종료하지 않아도 됩니다.

 쉽게 정리하면 try 영역에서 발생하는 예외를 catch 영역에서 해결한다!! 라고 생각하시면 될 것 같습니다.

 

 구체적으로 예외가 발생하면 어떤 일들이 일어나는지 알아보겠습니다.

먼저, try 영역에서 에러가 발생합니다. 그러면 에러에 해당하는 인스턴스가 생성됩니다. 이 인스턴스를 catch 구문에 전달하게 되고, catch 구문 안에서 예외를 처리한 후, try ~ catch 다음 영역부터 다시 프로그램을 실행합니다. 이때, catch 구문 안에서 예외를 어떻게 처리하는지는 가상머신이 신경쓰지 않습니다.

 

 예시를 통해서 익혀보겠습니다.

package Hello;

public class test{
	public static void main(String args[]) {
		System.out.println("프로그램시작");
		System.out.println(5 / 0); // 예외 발생
		System.out.println("예외발생");
		System.out.println("예외가 발생하지만 시스템은 정상적으로 실행");
	}
}

우리는 0으로 나누면 에러가 발생하는 것을 알고 있습니다.

따라서, 위에서 얘기한 대로 예외가 발생한 지점에서 ArithmeticException 이라는 예외 내용을 간단히 출력하고 종료되는 것을 확인할 수 있습니다. 

[Output]
프로그램시작
Exception in thread "main" java.lang.ArithmeticException: / by zero
	at HelloWorld/Hello.test.main(test.java:6)

 이번에는 try ~ catch 구문을 이용해 예외가 발생하는 영역을 try 영역 안에 넣어보았습니다.

package Hello;

public class test{
	public static void main(String args[]) {
		System.out.println("프로그램시작");
		try {
			System.out.println(5 / 0); // 예외 발생
		}
		catch(Exception e) {
			System.out.println("예외발생");
		}
		System.out.println("예외가 발생하지만 시스템은 정상적으로 실행");
	}
}
[Output]
프로그램시작
예외발생
예외가 발생하지만 시스템은 정상적으로 실행

 프로그램이 시작하고 예외가 발생했지만, 프로그램이 정상적으로 끝까지 실행되는 것을 확인할 수 있습니다. 또, catch 구문에서 예외를 처리하지않고 무슨 일을 하던 가상머신은 신경쓰지 않고 try ~ catch 다음 구문부터 프로그램을 시작하는 것을 확인할 수 있습니다.

다중 예외 처리

 예외가 발생하는 종류가 여러가지면 어떻게 해야할까요?? 

 정수 2개를 입력받고 나누기를 하는 프로그램을 생각해보겠습니다.

package Hello;

import java.util.Scanner;

public class test{
	public static void main(String args[]) {
		Scanner kb = new Scanner(System.in);
		System.out.println("첫 번째 정수를 입력해주세요");
		int n1 = kb.nextInt();
		System.out.println("두 번째 정수를 입력해주세요");
		int n2 = kb.nextInt();
		System.out.println(n1 / n2);
	}
}

 1. 정수가 아닌 다른 문자가 입력으로 들어온다면??

 2. 나누는 수가 0이 들어온다면??

 다음과 같은 2가지 상황에서 에러가 발생할 수 있습니다. 그러면 에러가 발생하는 상황을 강제로 만들어서 어떤 에러가 발생하는지 보겠습니다.

[Output]
첫 번째 정수를 입력해주세요
a
Exception in thread "main" java.util.InputMismatchException
	at java.base/java.util.Scanner.throwFor(Scanner.java:939)
	at java.base/java.util.Scanner.next(Scanner.java:1594)
	at java.base/java.util.Scanner.nextInt(Scanner.java:2258)
	at java.base/java.util.Scanner.nextInt(Scanner.java:2212)
	at HelloWorld/Hello.test.main(test.java:9)

 첫 번째 정수를 'a' 라는 문자를 입력했더니, InputmismatchException이 발생한 것을 확인 할 수 있습니다.

 두 번째 정수에 0을 입력하면, 0으로 나누는 상황이 되므로 위에서 다뤘던 ArithmeticException이 발생하겠죠?? 그럼 각각 다른 에러 상황에 다른 조치를 취하려면 어떻게 해야될까요?

package Hello;

import java.util.InputMismatchException;
import java.util.Scanner;

public class test{
	public static void main(String args[]) {
		try {
			Scanner kb = new Scanner(System.in);
			System.out.println("첫 번째 정수를 입력해주세요");
			int n1 = kb.nextInt();
			System.out.println("두 번째 정수를 입력해주세요");
			int n2 = kb.nextInt();
			System.out.println(n1 / n2);
		}
		catch(InputMismatchException e) {
			System.out.println("잘못된 입력을 했습니다.");
		}
		catch(ArithmeticException e) {
			System.out.println("잘못된 나누기 연산입니다.");
		}
	}
}

 다음과 같이 catch 구문을 2개 만들고, 각각 발생할 수 있는 에러 인스턴스를 넘겨주면 됩니다.

[Output]
첫 번째 정수를 입력해주세요
a
잘못된 입력을 했습니다.
[Output]
첫 번째 정수를 입력해주세요
5
두 번째 정수를 입력해주세요
0
잘못된 나누기 연산입니다.

 문자열을 입력해도 정상적으로 프로그램이 진행되고, 다음과 같이 0으로 나눠도 프로그램이 잘 진행되는 것을 확인할 수 있습니다.

 

 자바7에서부터는 다음과 같이 한 번에 여러가지 예외를 처리할 수 있도록 묶는 것도 가능합니다.

package Hello;

import java.util.InputMismatchException;
import java.util.Scanner;

public class test{
	public static void main(String args[]) {
		try {
			Scanner kb = new Scanner(System.in);
			System.out.println("첫 번째 정수를 입력해주세요");
			int n1 = kb.nextInt();
			System.out.println("두 번째 정수를 입력해주세요");
			int n2 = kb.nextInt();
			System.out.println(n1 / n2);
		}
		catch(InputMismatchException | ArithmeticException e) {
			System.out.println("잘못된 입력 혹은 잘못된 나누기 연산입니다.");
		}
	}
}

 따라서, 상황 별 예외의 처리 방식이 다르지 않은 경우에는 위와 같이 한 번에 catch 구문 안에서 여러 예외가 처리될 수 있또록 묶는 것도 좋은 방법이 될 수 있습니다.

예외 처리(try ~ catch ~ finally)

 try ~ catch 문을 이용해 예외 처리를 진행하면, 예외가 발생한 시점에 catch 문으로 넘어가고, catch 문이 실행되고 나서 try ~ catch 문 다음부터 다시 시작합니다. 그럼, 다음과 같이 try 문에서 예외가 발생한 시점보다 뒤에 꼭 실행되야하는 코드가 있으면 어떻게 해야될까요?

try{
	// 예외발생
	// 꼭 실행해야되는 코드
}
catch(Exception e){

}

 예외가 발생하면 꼭 실행해야되는 코드가 실행되지 않고 try ~ catch 문이 종료될 것입니다.

 그럼 또 문제가 발생하겠죠?? 이를 막기 위해 finally 가 추가되었습니다.

try{
	// 관찰영역
}
catch(Exception e){
	// 에러처리영역
}
finally{
	// 꼭 실행되야하는 영역
}

 finally 영역은 try 영역이 실행된다면 무조건 실행되는 영역입니다. 따라서, try 문 안에서 예외가 발생해 catch 영역으로 넘어가더라도 마지막에는 finally 영역을 실행하게 됩니다.


 

 

+ Recent posts