본문 바로가기

프로그래밍/JAVA

[JAVA} 예외처리(Exceptpion Handing)



1. 프로그램 오류의 종류


컴파일 에러(Compile-time Error) : 컴파일 도중 발생하는 에러

런타임 에러(Runtime Error) : 프로그램 구동 중 발생하는 에러


2. 예외처리의 정의와 목적


정의 : 프로그램 실행 시 발생할 수 있는 예외 발생에 대비한 코드 작성

목적 : 프로그램의 비정상적인 종류를 막고, 정상적인 실행상태를 유지하는 것



3. 예외처리 구문 ( try - catch )


try { 예외가 발생할 수 있는 문장   }
catch (Exception1 e1) { Exception1이 발생했을 경우, 이를 처리하기 위한 문장 }


예제를 통해서 살펴보도록 하자.

1. class exercise1{
2. public static void main(String args[]){
3. int number = 100;
4. int result = 0;
5.
6. for(int i=0; i<10; i++){
7. result = number / (int)(Math.random() *10);
8. System.out.println(result);
9. }
10. }
11. }


위 코드는 다음과 같은 에러를 발생시킨다.

ArithmeticException이 발생했다.


컴파일을 계속 시도하다보면 어떤 경우에는 정상적으로 출력되기도 한다.



하지만 위와같은 예외가 자주 발생한다. 위 컴파일 에러에서 명시된 것처럼 7번째 줄 코드를 살펴보자.


result = number / (int)(Math.random() * 10)


위 코드에서 Math.random()의 범위에 대해 살펴보도록 하자.


0.0 <= Math.random() < 1.0

0.0 <= Math.random() * 10 < 10.0


(int) 0.0 <= (int)(Math.random()*10 < (int)10.0      int를 선언해주면
정수 0,1,2,3,4,5,6,7,8,9 중에서 임의의(랜덤의) 한 값을 갖게 된다.

그런데 이 코드에 의해서 0이 나오면? ( result = number / 0 )
숫자를 0으로 나눌 수는 없다. 

따라서 ArithmeticException (산술연산에 대한 오류가 있을떄 발생하는 예외)이 발생하게 된다.
이를 해결하기위해서 예외처리를 한다.

try안에는 실행될 문장

catch안에는 예외처리 할 문장을 넣는다.


     ---------> 예외처리 결과 ---------> 

예외처리를 한 결과 다음과 같은 결과가 나온다.


4. try-catch문에서의 흐름


try 블럭 내에서 예외가 발생한 경우

1. 발생한 예외와 일치하는 catch 블럭이 있는지 확인

2. 일치하는 catch 블럭을 찾으면, 블럭 내 문장을 수행하고, 전체 try-catch를 빠져 나감

   일치하는 catch 블럭이 없으면, 예외는 처리되지 못한다.



try 블럭 내에서 예외가 발생하지 않는 경우

1. catch 블럭을 거치지 않고 전체 try-catch문을 빠져나가서 문장 수행을 계속함.


5. 예외 발생시키기


키워드 throw를 활용해 고의로 예외를 발생시킬 수 있다.


1. 먼저 연산자 new를 이용해, 발생시키려는 예외 클래스의 객체를 만듬

ex) Exception e = new Exception("이거는 고의임");

2. 키워드 throw를 이용해 예외를 발생시킴

ex) throw e



한줄로 쓰길 원한다면 throw new Exception("고의로 발생시켰음"); 을 써주면 된다.


Q.강졔예외가 왜 필요한가?

A. 객체를 잘못 사용하는 사람에게  예외를 강제로 발생시켜 주의를 주거나, 고쳐달라고 요청함

   테스트를 위해서도 사용할 수 있겠다.


6. 예외 클래스의 계층구조

실행 시 발생하는 오류 역시 "클래스"이다.

이 오류 클래스의 최고조상은 "Object" 클래스이다.

예외의 최고 조상은 Exception 클래스이다


Exception의 클래스를 도식화하면 위와 같다. (출처 : JAVA의 정석)


RuntimeException 클래스주로 프로그래머의 실수에 의해서 발생된다.

IndexOutOfBoundsException : 배열의 범위를 벗어남

NullPointerException : 값이 null인 참조변수의 멤버를 호출하려 함

ArithmeticException : 정수를 0으로 나누려 하는 등 산술적인 문제


해결법 : RuntimeException이 발생하는 경우에는 try-catch보다 적절한 코드 수정이 필요하다.

컴파일해도 아무런 문제가 발생하지 않는다.


Exception 클래스는 외부의 영향으로 발생할 수 있는 것들로서, 프로그램 사용자들의 동작에 의해 발생하는 경우가 많다.

FileNotFoundException : 존재하지 않는 파일의 이름을 입력함

ClassNotFoundException : 클래스의 이름을 잘못 적음

DataFormatException : 입력한 데이터 형식이 잘못됨


해결법 : 이러한 예외는 반드시 처리를 해주어야 한다.


RuntimeException과 달리 Exception은 catch까지 해주지 않으면 위와 같은 오류를 발생시킨다.



7. 예외의 발생과 catch 블럭

catch블럭을 자세히 보면

catch (      예외  내용      ) {    블럭  내용   } 두 부분으로 나뉜다.

괄호 () 내에는 처리하고자 하는 예외 + 같은 타입의 참조변수 하나를 선언해야 한다.


ArithmeticException 예외가 발생하면 ArithmeticException인스턴스가 생성된다.

예외가 발생했을 때 생성되는 예외클래스(ArithmeticException 클래스)의 인스턴스에는

발생한 예외에 대한 정보가 담겨져 있으며, getMessage()와 printStackTrace()를 통해 정보 접근이 가능.


getMessage() - 발생한 예외클래스의 인스턴스에 저장된 메시지를 얻을 수 있다.

printStackTrace() - 예외발생 당시 호출스택(Call Stack)에 있는 메서드의 정보와 예외 메시지 출력



궁금증 0/0.0을 하면 1,2,3,NaN,4,6이 나오고, 3/0.0을 하면 Infinity가 나온다.


또한 printStackTrace (PrintStream s) 또는 printStackTrace(PrintWriter s)를 사용해

발생한 예외에 대한 정보를 "파일"에 저장할 수도 있다.

이 부분은 파일 입출력에 대한 부분을 공부해야 하므로, 추후에 기술하도록 하겠다.


<호출스택 복습>

<호출스택(Call Stack) 복습>



8. finally 블럭


예외 발생여부와 상관없이 실행되어야 할 코드를 포함시킬 목적으로 사용된다.


예외 발생시 : try → catch → finally 순으로 실행

예외 발생없을 시 : try → finally의 순으로 실행된다.


9. 메서드에 예외선언하기


void method() thorws Exception1, Exception2, Exception3, ........., ExceptionN { 메서드 내용 }


자바에서는 메서드를 작성할 떄 발생할 가능성이 있는 예외를 메서드 선언부에 명시가 가능하다.

따라서 이 메서드를 사용하는 입장에서 이에 대한 처리를 하도록 강요한다.



(자바 API 주소 : http://docs.oracle.com/javase/7/docs/api/index.html?overview-summary.html)

자바 API에서 Object클래스의 wait메서드에 선언된 예외가 interruptException이란 걸 알 수 있다.

아래 InterruptedException을 클릭해보면 아래와 같이 출력된다.


위 그림을 통해서 InterruptedException은 Exception의 자손임을 알 수 있다.


위에서 RuntimeExeption은 예외처리를 안해줘도 되지만, Exception은 반드시 예외처리를 해야 한다고 했다. 따라서 InterruptedException은 반드시 예외처리를 해주어야 한다. 그런데?



wait 메서드에 선언된 IllegalMonitorStateException은 RuntimeException이다.

따라서 예외처리를 안해줘도 문제가 없다.


예외를 메서드의 throws의 명시하는 것은 호출한 메서드에 예외를 떠넘기는 것이다.

따라서 계속 넘어가다가 9번에서도 예외를 처리하지 않으니 main메서드 마저 종료된다.


나는 method2 에게 예외를 처리할 기회를 주었다.

method2가 주어진 기회를 잘 살렸다.



10. 예외 되던지기(exception re-throwing)


하나의 예외에 대해서 예외가 발생한 메서드와 호출한 메서드 모두에서 처리하도록 할 수 있다.

이것은 예외를 처리한 후에 인위적으로 다시 발생시키는 방법을 통해서 가능하다.

이를 예외 되던지기(exception re-throwing)이라고 한다.


왜 이런 짓을 하는지는 아직 내 수준에서는 잘 모르겠다.


한 개의 예외를 메인메서드와 method1 두 곳에서 두 번 처리하고 있다.



11. 사용자정의 예외 만들기


class exercise1{
public static void main(String args[]) throws Exception {

class MyException extends Exception {
MyException(String msg) { 문자열을 매개변수로 받는 생성자
super(msg); // 조상인 Exception 클래스의 생성자를 호출한다.
}
}
}
}

필요에 따라 알맞은 예외 클래스를 "선택"할 수 있다.

Exception 클래스는 생성 시에 String값을 받아서 메시지를 저장할 수 있는 기능(function)이 있다.

따라서 사용자정의 예외 클래스도 이 기능을 물려받아 메시지를 저장할 수 있다.



class MyException extends Exception { // Exception을 상속받는 사용자정의 예외를 만듬.

private final int ERROR_CODE; // 생성자를 통해 초기화할 예정

MyException(String msg, int errorCode){ // 생성자 1 ( 메시지와 에러코드를 초기화 )
super(msg);
ERROR_CODE = errorCode;
}

MyException(String msg) { // 생성자 2 (메시지를 초기화)
this (msg, 100);
}

public int getErrCode(){
return ERROR_CODE;
}
}
class exercise1{
public static void main(String args[]){
try{
happen(); // happen 메서드에 무조건 예외를 발생시키게 했으니 MyException이 발생
} catch (MyException e){
System.out.println("에러메시지 : " + e.getMessage());
e.printStackTrace();
}

}

static void happen() throws MyException{ // happen 메서드는 MyException 예외를 발생시킨다.
throw new MyException("폭탄을 만들거야!");
}
}

다소 조잡한 예제를 만들어보았다.








'프로그래밍 > JAVA' 카테고리의 다른 글

[JAVA] 내부 클래스(inner class)  (0) 2016.08.13
[JAVA} Object 클래스  (1) 2016.08.09
[JAVA] 인터페이스(Interface)  (1) 2016.08.04
[JAVA] 추상클래스(Abstract Class)  (0) 2016.08.02
[JAVA] 다형성(polymorphism)  (3) 2016.08.02