반응형

Exception(예외)


프로그램 실행 중 예상치 못한 동작이나
개발자가 의도하지 않은 동작을 예외라고 표현한다.
이러한 예외는 if문으로 분기하도록 할 수 있으나 if문만을 사용하여 예외처리를 하면
코드에서 정상적인 흐름분기(if)와 예외에 대한 분기를 식별하기 어려워진다.
그래서 만들어진 매커니즘이 try~catch를 이용하는 예외처리 기법이다.

자바에서 예외처리를 위해 미리 정의되어 있는 Exception을 상속받은 클래스들이 정의되어 있다.

 

그리고 try, catch, finally 구문을 제공한다.
이를 활용하여 예외 처리 방법을 확인해 본다.

 

try~catch/finally

 

먼저 예외가 무엇인지 이해하기 위해 다음 예제를 실행해 본다.

package exam;

import java.util.Scanner;

public class Exam {
	public static void main(String[] args) {
		Scanner in = new Scanner(System.in);
		int n1 = 0;
		int n2 = 0;
		int ret = 0;
		System.out.print("첫 번째 수 : ");
		n1 = in.nextInt();
		System.out.print("두 번째 수 : ");
		n2 = in.nextInt();
		ret = n1 / n2;
		System.out.println("결과 : " + ret);
	}
}

실행결과


위와 같이 정상적으로 동작결과를 확인할 수 있다.
위 프로그램이 동작할 때 어떤 예외가 발생할 수 있을까?

첫 번째 발생 가능한 예외(문자입력)
ex1)

 

ex2)

 

nextInt()메서드는 정수형태의 문자를 입력 받는데 정수가 아닌 문자가 입력되면 위와 같은 예외가 발생된다.

 

두 번째 발생 가능한 예외(정수/0문제 - 컴퓨터에서는 정수를 0으로 나눌 수 없음)


위와 같이 자바 프로그램 동작 중 예외가 발생되면 즉시 프로그램이 종료된다.
이러한 동작을 처리하는 작업이 바로 예외처리이다.

 

예외처리는 다음 세 가지의 구문블록으로 처리한다.
1. 예외가 발생되는 블록을 지정하는 try 블록
     try내에서 예외가 발생되면 예외객체를 생성하고 
     발생된 예외객체는 throw를 통해 catch로 던져진다.
2. try에서 던져진 예외객체를 받아 처리할 수 있는 catch(예외객체 참조변수) 블록
     throw된 예외객체를 참조하여 개발자가 원하는 대로 처리하도록 코드를 작성할 수 있다.
     일반적으로 메시지를 출력하는 코드로 로그형태를 사용
     또는 printStackTrace()메서드로 호출 스택을 추적하는 내용을 사용
3. try에서 예외가 발생되던 발생되지 않던 무조건 실행할 내용을 작성하는 finally 블록

 

일단 처음 예제에 try와 catch개념을 확인한다.

package exam;

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

public class Exam {
	public static void main(String[] args) {
		Scanner in = new Scanner(System.in);
		int n1 = 0;
		int n2 = 0;
		int ret = 0;
		try {
			System.out.print("첫 번째 수 : ");
			n1 = in.nextInt();
			System.out.print("두 번째 수 : ");
			n2 = in.nextInt();
			ret = n1 / n2;
			System.out.println("결과 : " + ret);
		} catch (InputMismatchException e) {
			System.out.println("예외 메시지 : " + e.getMessage());
			e.printStackTrace();
		}
		System.out.println("프로그램 종료");
	}
}

실행하여 문자를 입력해본다.


위와 같은 결과를 볼 수 있는데 동작을 설명하면 다음과 같다.
     숫자가 아닌 문자가 입력되었을 때 nextInt()메서드에서 
     throw new InputMismatchException()이 발생하고
     발생된 객체는 throw를 통해 catch(InputMismatchException e)로 전달되고
     예외가 발생된 코드 이후의 코드는 실행되지 않는다.
     catch에서는 발생된 예외에 대한 정보를 출력하도록 하였다.
     catch블록 처리 후 계속 진행하여 프로그램은 정상적으로 동작된다.

 

자바는 프로그램에서 예외가 발생되면 프로그램이 종료되는데 이를 적절하게 처리하도록 지원
위와 같이 try~catch를 통해 예외처리가 된다.

 

앞에서 정수를 0으로 나눌 수 없다는 예외가 있었다. 이 예외를 처리하는 코드를 추가한다.

package exam;

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

public class Exam {
	public static void main(String[] args) {
		Scanner in = new Scanner(System.in);
		int n1 = 0;
		int n2 = 0;
		int ret = 0;
		try {
			System.out.print("첫 번째 수 : ");
			n1 = in.nextInt();
			System.out.print("두 번째 수 : ");
			n2 = in.nextInt();
			ret = n1 / n2;
			System.out.println("결과 : " + ret);
		} catch (InputMismatchException e) {
			System.out.println("예외 메시지 : " + e.getMessage());
			e.printStackTrace();
		} catch (ArithmeticException e) {
			System.out.println("예외 메시지 : " + "0으로 나눌 수 없음");
			e.printStackTrace();
		}
		System.out.println("프로그램 종료");
	}
}

실행하여 예외 발생 시켜보기

 

catch()는 여러 개 이어서 작성할 수 있다.
이 때 주의할 점은 상속관계를 보고 상위의 클래스를 참조하는 catch가 먼저 작성되면
이 후의 예외가 동작되지 않을 수 있다.
예를 들어 다음과 같이 Exception 최상위 클래스를 참조하도록 하면 모든 예외가 업캐스팅으로 참조 되어 이후에 나오는 지정된 catch구분으로 처리되지 않는다.

다음과 같은 처리를 주의!

 


다음으로 finally구문을 추가하도록 한다.
finally구문은 예외가 발생되던 발생되지 않던 무조건 실행할 코드를 작성하는 예외처리 블록이다.
보통 입출력 스트림을 닫는 기능이나 DB접속 연결을 해제하는 코드를 작성하는 것이 일반적인 용도이다.
예제확인

package exam;

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

public class Exam {
	public static void main(String[] args) {
		Scanner in = new Scanner(System.in);
		int n1 = 0;
		int n2 = 0;
		int ret = 0;
		try {
			System.out.print("첫 번째 수 : ");
			n1 = in.nextInt();
			System.out.print("두 번째 수 : ");
			n2 = in.nextInt();
			ret = n1 / n2;
			System.out.println("결과 : " + ret);
		} catch (InputMismatchException e) {
			System.out.println("예외 메시지 : " + e.getMessage());
			e.printStackTrace();
		} catch (ArithmeticException e) {
			System.out.println("예외 메시지 : " + "0으로 나눌 수 없음");
			e.printStackTrace();
		} finally {
			System.out.println("예외 발생 여부와 상관없이 try~catch블록이 종료될 때 실행되는 영역");
		}
		System.out.println("프로그램 종료");
	}
}


정상동작

 

InputMismatchException 예외발생


ArithmeticException 예외발생

 

위와 같이 동작하는 부분이 finally가 선언된 블록이다.

 

사용자 정의 예외 클래스
기본적으로 자바에서 정의된 예외 상황에 따른 클래스들 이외에
개발요구에 따른 별도의 예외를 작성할 필요가 있다.
그 때 사용자가 직접 예외클래스를 작성하고 try~catch에서 사용할 수 있다.

이 때 사용되는 구문은 throw구문이다.

throw

예외 객체를 catch로 던지는 기능

 

테스트
값을 입력 받아 출력하는 예제

package exam;

import java.util.Scanner;

public class Exam {
	public static void main(String[] args) {
		Scanner in = new Scanner(System.in);
		int n1 = 0;
		System.out.print("입력 : ");
		n1 = in.nextInt();
		System.out.println("결과 : " + n1);
		System.out.println("프로그램 종료");
	}
}

위 동작에서 1~100사이의 값만 입력 받는 조건을 예외로 처리하고 싶다면 다음과 같이 클래스를 정의할 수 있다.
다형성을 통해 예외를 관리할 수 있도록 Exception을 상속받아 정의한다.(권장)

이 때 예외가 컴파일 타임에 확인을 하도록 해야 한다면 Exception을 활용하고 런타임에 확인하도록 한다면 RuntimeException을 상속(RuntimeException은 try~catch를 사용하지 않아도 실행 가능)

class MyException extends Exception {
	public MyException(String msg) {
		super(msg);
	}
}
class MyException extends RuntimeException {
	public MyException(String msg) {
		super(msg);
	}
}

 

Exception클래스는 예외정보를 담는 String객체 message를 생성자를 통해 설정할 수 있다.
이 후 getMessage()메서드로 출력이 가능하므로 이를 이용하도록 MyException클래스 작성한 것이다.

위 예외 클래스를 사용하는 예제

package exam;

import java.util.Scanner;

class MyException extends Exception {
	public MyException(String msg) {
		super(msg);
	}
}

public class Exam {
	public static void main(String[] args) {
		Scanner in = new Scanner(System.in);
		int n1 = 0;
		System.out.print("입력 : ");
		try {
			n1 = in.nextInt();
			if (n1 < 1 || n1 > 100) {
				MyException e = new MyException("1~100 범위를 벗어남");
				throw e;
			}
			System.out.println("결과 : " + n1);
		} catch (MyException e) {
			System.out.println("예외 : " + e.getMessage());
			e.printStackTrace();
		}
	}
}

위 코드에서 if문으로 예외 상황에 대한 조건을 작성하고
예외가 발생된 경우 MyException객체를 생성하며 생성자를 통해 예외메시지를 지정한다.
생성된 MyException객체를 throw를 통해 catch로 던진다.
던져진 MyException객체는 catch블록을 통해 처리된다.

정상동작


예외발생

 

위와 같은 방법으로 프로그램 작성 중 발생되는 조건에 따라 예외를 만들어 사용이 가능하다.

마지막으로 throws 를 이해하도록 한다.

 

throws
throws는 메서드 내에서 발생되는 예외를 해당 메서드에서 처리하지 않고 메서드를 사용하는 곳에서 처리하도록 예외를 전가하는 것이다.


다음 예제를 작성해본다.

package exam;

import java.util.Scanner;

public class Exam {
	public int division(int a, int b) {
		return a / b;
	}

	public static void main(String[] args) {
		Scanner in = new Scanner(System.in);
		Exam ex = new Exam();
		System.out.print("첫 번째 수 : ");
		int n1 = in.nextInt();
		System.out.print("두 번째 수 : ");
		int n2 = in.nextInt();
		int ret = ex.division(n1, n2);
		System.out.println(n1 + "/" + n2 + "=" + ret);
	}
}

정상실행


예외발생


위 예제를 보면 main메서드에서 Exam객체의 division메서드를 호출하고 있다.
division메서드에서 나눗셈을 할 때 예외가 발생되면
그것은 main메서드에서 division을 호출하는 부분에서 예외가 발생한 것으로 볼 수 있다.

그럼 위 division메서드에 발생될 수 있는 ArithmeticException은 정수를 0으로 나눌 때 발생된다.
이 예외가 발생되기 전에 b에 0이 들어오면 직접 MyExceptiont을 발생시키도록 작성해본다.

package exam;

import java.util.Scanner;

class MyException extends Exception {
	public MyException(String msg) {
		super(msg);
	}
}

public class Exam {
	public int division(int a, int b) throws MyException {
		if (b == 0)
			throw new MyException("0으로 나눌 수 없음");
		return a / b;
	}

	public static void main(String[] args) {
		Scanner in = new Scanner(System.in);
		Exam ex = new Exam();
		System.out.print("첫 번째 수 : ");
		int n1 = in.nextInt();
		System.out.print("두 번째 수 : ");
		int n2 = in.nextInt();
		int ret = ex.division(n1, n2);
		System.out.println(n1 + "/" + n2 + "=" + ret);
	}
}

작성하면 다음과 같이 ex.division(n1, n2)를 호출하는 부분에서 문제가 발생된다.

 

이것은 division()메서드에서 b에 0이 들어올 경우 MyException이 발생된다는 문제이다.

 

이것을 division에서 try~catch로 직접 처리하지 않고 throws를 선언했다.

이 때문에 division에서 발생되는 예외를 처리(try~catch)해야 main에서 division메서드를 사용할 수 있다.

즉 throws는 해당 메서드에서 발생되는 예외를 호출하는 측에서 처리하도록 선언(예외 처리 위임)한 것이다.
따라서 main에서 division()을 호출하려면 다음과 같이 예외 처리가 필요하다.

만일 호출하는 메서드에서도 처리하지 않는다면 역시 throws선언으로 상위 메서드에 전가할 수 있다.


자바 프로그래밍을 하면서 위와 같이 실행 중에 어떤 예외가 발생한다면
try, catch, finally, throw, throws를 적절히 활용할 수 있겠다.

추가로 입출력이나 자원 해제 시 예외처리가 필수가 되는데

이 때 필요한 예외처리는 try-with-resource(jdk 1.7에서 추가)라는 방식으로도 처리하니 찾아보도록 하자.

 

반응형

'교육자료 > Java' 카테고리의 다른 글

Java IO - File class  (0) 2017.06.30
Java Thread (쓰레드)  (0) 2017.06.30
Java Collection part2  (0) 2017.06.28
Java Collection part1  (0) 2017.06.27
Java 인터페이스 (interface)  (0) 2017.06.25

+ Recent posts