본문 바로가기

개인적으로 공부한 것을 정리해 놓은 블로그입니다 틀린 것이 있으면 댓글 부탁 드립니다!


JAVA

JAVA 스터디 15 - 예외처리

반응형

자바에서 예외 처리 방법과 예약어의 개념

 

에러(error)와 예외(exception) 차이

 

 자바 프로그램을 작성할 떄 자바 문법에 맞지 않게 코드를 작성하고 컴파일하려고 하면, 자바 컴파일러는 문법 오류를 발생시킨다. 또한 , 자바 문법에 맞게 작성되었다 하더라도 프로그램이 실행되면서 예상하지 못한 오류가 발생할 수 있다

 이렇게 컴퓨터 시스템이 동작하는 도중에 예상하지 못한 사태가 발생하여 실행 중인 프로그램이 영향을 받는 것을 오류(error)와 예외(exception) 두 가지로 구분할 수 있다.

 

 에러(error)는 시스템 레벨에서 프로그램에 심각한 문제를 야기하여 실행 중인 프로그램을 종료시킨다. 이러한 오류는 개발자가 미리 예측하여 처리할 수 없는 것이 대부분이므로 , 오류에 대한 처리는 할 수 없다 .

 하지만 예외는 오류와 마찬가지로 실행 중인 프로그램을 비정상적으로 종료시키지만, 발생할 수 있는 상황을 미리 예측하여 처리할 수 있다.

 따라서 개발자는 예외 처리(exception handling)를 통해 예외 상황을 처리할 수 있도록 코드의 흐름을 바꿀 필요가 있다.

 

예외처리 방법

 자바에서는 프로그램이 실행되는 도중 발생하는 예외를 처리하기 위해 try/ catch/finally 문을 사용할 수 있다.

import java.io.IOException;
import java.util.InputMismatchException;
import java.util.Scanner;

public class ExceptionTest {
    public static void main(String[] args) {
        try{
            Scanner scan = new Scanner(System.in);
            int inputNum = scan.nextInt();
            if(inputNum == 10){
                System.out.println("10이다");
            }
            // 예외를 처리하길 원하는 실행코드를 try안에 정의
        }catch(InputMismatchException e){
            e.printStackTrace();
            System.out.println("숫자아님 예외터짐");
            //예상되는 예외 타입을 catch에 정의하고 그 예외가 일어날 시에
            //파라미터로 전달 받은 예외를 활용하여
            // 여기서 예외를 처리할 수 있다.
        }catch(NullPointerException e){
            //여러개의 catch 문을 통해
            // 여러 가지 예외를 받아낼 수 있다.
        }finally {
            //finally에는 예외 발생과 상관없이 무조건 실행 되는 코드를 정의
            System.out.println("코드실행됨");
        }
    }
}

 

스캐너 클래스를 통해 사용자 키보드 입력을 받아 예외를 터트리는 예제이다 . 입력 값을  int 타입으로 정의해 놓았기 떄문에 int가 아니면 예외를 터트린다 

"십"을 입력해 보면 결과는 아래와 같다

 

십
java.util.InputMismatchException
	at java.util.Scanner.throwFor(Scanner.java:864)
	at java.util.Scanner.next(Scanner.java:1485)
	at java.util.Scanner.nextInt(Scanner.java:2117)
	at java.util.Scanner.nextInt(Scanner.java:2076)
	at ExceptionTest.main(ExceptionTest.java:9)
숫자아님 예외터짐
코드실행됨

Process finished with exit code 0

 

잘못된 인풋값을 입력했음으로 InputMismatchException이 일어나고 InputMismatchException로 정의한 catch절에서 예외를 잡아내며  e.printstacktrace() 메서드를 통해 예외의 정보가 나오고  다음으로 "숫자아님 예외터짐"이라는 메시지가 출력된다. 그 다음으로는 finally절에 정의된 코드가 실행되어 "코드실행됨"이 출력된다.

 

예외처리 메커니즘

 자바에서 예외 처리는 다음과 같은 순서로 진행된다.

 

1. try 블록에 도달한 프로그램의 제어는 try 블록 내의 코드를 실행한다. 이떄 만약 예외가 발생하지 안고 , finally 블록이 존재한다면 finally 블록으로 이동한다

 

2 .try 블록에서 예외가 발생하면 catch 핸들러는 다음과 같은 순으로 적절한 catch 블록을 찾게 된다.

 

 2-1 . 스택에서 try블록과 가장 가까운 catch 블록부터 차례대로 검사한다.

 2-2 . 만약 적절한 catch 블록을 찾지 못하면 , 바로 다음 바깥쪽 try 블록 다음에 위치한 catch 블록을 차례로 검사한다.

 2-3 . 이러한 과정을 가장 바깥쪽 try 블록까지 계속 검사하게 된다 .

 2-4 . 그래도 적절한 catch 블록이 없으면 예외는 처리되지 못한 채 finally 문을 거친다음 해당 프로그램은 강제로 종료된다.

 

3. 만약 적절한 catch 블록을 찾게 되면 throw 문의 피연산자는 예외 객체의 형식 매개 변수로 전달 된다 .

4. 모든 예외 처리가 끝나면 프로그램의 제어는 finally 블록으로 이동한다.

5. finally 블록이 모두 처리되면 ㅡ 프로그램의 제어는 예외 처리문 바로 다음으로 이동한다.

 

 

Exception 클래스 

 

자바에서 모든 예외의 조상 클래스가 되는 Exception 클래스는 크게 다음과 같이 구분할 수 있다.

 

1. RuntimeException 클래스

 

2. 그 외의 Exception 클래스의 자식 클래스

 

 

예외 클래스의 구조(http://www.tcpschool.com/ 에서 참조)

RuntimeException 클래스를 상속받는 자식 클래스들은 주로 치명적인 예외 상황을 발생시키지 않는 예외들로 구성된다.

따라서 try/catch 문을 사용하기보다는 프로그램을 작성하면서 예외가 발생하지 않도록 주의를 기울이는 편이 좋다 .

 하지만 그 외의 Exceiption 클래스에 속하는 자식 클래스들은 치명적인 예외를 발생시키므로 , 반드시 try/catch 문을 사용하여 예외를 처리해야만 한다.

 따라서 자바 컴파일러는 RuntimeException 클래스 이외의 Exception 클래스의 자식 클래스에 속하는 예외가 발생할 가능성이 있는 구문을 반드시 예외 처리하도록 강제하고 있다.

 만약 이러한 예외가 발생할 가능성이 있는 구문을 예외 처리 하지 않았을 때는 컴파일 시 오류를 발생시킨다.

 

//Runtime Exception에 속하지 않는 예외는 심각한 오류를 발생시키기에
        //자바에서 반드시 예외처리하도록 강제한다.
        //write는 IOException의 처리를 강제하기 때문에  1번은 오류가 난다.
        //2번처럼 예외를 처리 해줘야한다
        
        //---1----
        byte[] list = {'a','b','c'};
        System.out.write(list);
        //---2----
        byte[] list2 = {'a','b','c'};
        try {
            System.out.write(list);
        } catch (IOException e) {
            e.printStackTrace();
        }

 

 

 

자바가 제공하는 예외 계층 구조

 

 자바에서는 예외가 발생하면 , try 블록과 가장 가까운 catch 블록부터 순서대로 검사한다.

따라서 여러 개의 catch 블록을 사용할 때는 Exception 클래스의 계층 관계에도 주의를 기울여야 한다.

 

 

import java.io.IOException;

public class ExceptionHierarchyTest {
    public static void main(String[] args) {
        byte [] list = {'a','b','c'};

        try {
            System.out.write(list);
        }catch (IOException ioe){
            ioe.printStackTrace();
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

 

 위의 코드처럼  범위가 더 좁은 예외를 처리하는 catch 블록을 먼저 명시하고 , 범위가 더 넓은 예외를 처리하는 catch블록은 나중에 명시해야만 정상적으로 해당 예외를 처리할 수 있다. 

 

Throwable 클래스

 자바에서 Throwable 클래스는 모든 예외의 조상이 되는 Exception 클래스와 모든 오류의 조상이 되는 Error클래스의 부모 클래스 이다.

 Throwable 타입과 이 클래스를 상속받은 서브 타입만이 자바 가상 머신(JVM)이나 throw 키워드에 의해 던져질 수 있다.

 

String getMessage() :  해당 throwable 객체에 대한 자세한 내용을 문자열로 반환함.

void printStackTrace() : 해당 throwable 객체와 표준 오류 스트림에서 해당 객체의 스택  트레이스를 출력함

String toString() :  해당 throwable 객체에 대한 간략한 내용을 문자열로 반환함.

 

아래는 일부러 숫자를 0으로 나눠 ArithmeticException 오류를 발생시키는 예제이다 . 

이렇게 발생한 오류에 대해 Throwable 메소드를 사용하여 발생한 오류에 대한 정보를 출력한다.

 

try {

    System.out.println(5 / 0);

} catch (ArithmeticException e) {

    System.out.println("현재 발생한 예외 정보 : " + e.getMessage());

}

 

결과 = 현재 발생한 예외 정보 : /by zero

 

 

 

커스텀한 예외 만드는 방법

 

예외 발생시키기

  

 자바에서는 throw 키워드를 사용하여 강제로 예외를 발생시킬 수 있다. 

 

Exception e = new Exception("오류메시지");


throw e ; 

 위 처럼 생성자에 전달된 문자열은 getMessage()메소드를 사용하여 오류 메시지로 출력할 수 있다.

 

 

예외 회피하기

 

 메소드 선언부에 throws 키워드를 사용하여 해당 메소드를 사용할 때 발생할 수 있는 예외를 미리 명시할 수도 있다.

이렇게 하면 해당 메소드를 사용할 때 발생할 수 있는 예외를 사용자가 충분히 인지할 수 있으며 , 그에 대한 처리까지도 강제할 수 있다. 따라서 더욱 안정성 있는 프로그램을 손쉽게 작성할 수 있도록 한다.

 

public class ThrowsTest {
    static void handlingException(){
        try {
            throw new Exception();
        }catch (Exception e){
            System.out.println("호출 된 메서드 예외 처리");
        }
    }

    public static void main(String[] args) {
        try {
            handlingException();
        }catch (Exception e){
            System.out.println("메인 메서드에서 예외 처리");
        }
    }
}

위의 코드의  출력값은 "호출 된 메서드 예외 처리 " 이다. 

static 으로 정의해 놓은 메서드에서 이미 예외처리를 하였고  그것을 호출한 메인메서드에서는 그 메서드에서 예외가 발생했는지 알 수 없다. 그러므로 메인메서드에서는 catch문이 실행되지 않고 메소드가 종료된다. 

 

 다음 예제는 throws 키워드를 사용하여 호출된 메소드에 발생한 예외를 호출한 메소드로 넘기는 예제이다.

 

public class ThrowsTest {
    static void handlingException() throws Exception{

            throw new Exception();
    }

    public static void main(String[] args) {
        try {
            handlingException();
        }catch (Exception e){
            System.out.println("메인 메서드에서 예외 처리");
        }
        
    }
}
//출력값 :메인 메서드에서 예외 처리

handlingException() 메서드는 선언부에 thorws 예약어를 통해 일어날 예외를 던졌다 . 

그러므로 예외의 처리는 메서드를 사용하는 메인메서드에서 이루어 진다. 

 

 

사용자 정의 예외 클래스

 자바에서는 Exception 클래스를 상속받아 자신만의 새로운 예외 클래스를 정의하여 사용할 수 있다. 사용자 정의 예외 클래스에는 생성자뿐만 아니라 필드 및 메소드도 원하는 만큼 추가할 수 있다 . 또 클래스의 이름만으로 어떤 오류가 발생했는지 알려주어 코드의 직관성을 높인다.

 

Checked Exception 

 

 Exception을 상속받아 정의된 예외 클래스이며 오류처리를 하지 않으면 컴파일 오류가 발생하기 때문에 반드시 예외처리가 필요한 Exception 이다 . 

 

Unchecked Exception(RuntimeException)

 

 오류 처리를 반드시 하지 않아도 컴파일 상에서는 오류를 발생 시키지 않는다. 

 

아래는 사용자 정의 에러를 구현하는 코드 예제이다 

예외 클래스를 구현하기 위해선 해당 클래스가 Exception 혹은 그 자식의 클래스를 상속받아야한다.

부모에게 메시지를 전달하는 생성자와 해당 예외를 전달해주는 생성자가 정의 되어있다 . 

public class TestException extends RuntimeException{
    public TestException(String msg){
        //super()로 부모생성자를 호출하고 msg 를 전달
        super(msg);
    }
    public TestException(Exception e){
        //super()로 부모생성자를 호출하고 Exception을 전달
        super(e);
    }
}

 

위에 정의한 예외를 사용해 보는 코드이다

 

public class TestService {
    static void TestMethod(int i) throws TestException{
        System.out.println("테스트 메서드 시작");
        if(i<0){
            throw new TestException("입력값은 0보다 커야한다.");
        }
        System.out.println("테스트 메서드 종료");
    }
}

class TestExam{
    public static void main(String[] args) {

        //RuntimeException이기 때문에 컴파일상 에러는 발생하지 않는다
        TestService.TestMethod(-1);
    }
}

 

TestService 에는 static 메서드인 TestMethod가 정의되어 있다 TestMethod는 위에 정의한 TestException를 throws하고있다 만약 메소드의 파라미터가 0보다 작다면 예외가 발생할 것이다 . 

 

TestExam 클래스에서 TestService를 사용해 보면 결과는 아래와 같다.

 

테스트 메서드 시작
Exception in thread "main" TestException: 입력값은 0보다 커야한다.
	at TestService.TestMethod(TestService.java:5)
	at TestExam.main(TestService.java:13)

Process finished with exit code 1

 

TestException이 정상적으로 실행되었다. 예외 처리는 따로 되지 않았기 때문에 예외 아래의 코드 는 실행 되지 않았고 "테스트 메서드 종료"는 출력되지 않는다 . TestException은 RuntimeException을 상속 받았기 때문에 컴파일 시 문제가 생기지 않기때문에 위와 같이 출력됬지다  ,만약 예외처리를 강제하고 싶다면. TestException가 Exception 객채를 상속받으면 된다 . 

 

반응형

'JAVA' 카테고리의 다른 글

JAVA 스터디 18 - 애노테이션  (0) 2021.03.26
JAVA 스터디 17 - ENUM  (0) 2021.03.25
JAVA 스터디 14 - 인터페이스  (0) 2021.03.24
JAVA 스터디 13 - 접근제어자  (0) 2021.03.23
JAVA 스터디 12 - 패키지  (0) 2021.03.23