JAVA

Java - 예외 처리(Exception Handling)

jonghyeon6084 2024. 1. 16. 15:43
728x90

프로그램 실행 중 어떤 원인에 의해 오작동하거나 비정상적으로 종료되는 경우가 있는데 이런 결과를 초래하는 원인을 프로그램 에러 또는 오류라고 한다. 발생 시점에 따라 다음과 같이 두 종류로 구분한다.

 

  • 컴파일 에러(compile error) : 컴파일 시에 발생하는 에러
  • 런타임 에러(runtime error) : 실행 시에 발생하는 에러
  • 논리적 에러(logical error) : 실행은 되지만 의도와 다르게 동작하는 것

자바는 실행시에(run time) 발생하는 프로그램 오류를 다음과 같이 구분한다.

  • 에러(error) : i.e OutOfMemory, StackOverflowError와 같이 수습될 수 없는 심각한 오류
  • 예외(exception) : 코드에 의해 수습될 수 있는 다소 미약한 오류

1. 예외처리하기  - try-catch 문

예외 처리란?

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

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

특징

  • if else와 달리 try-catch는 블록내에 포함된 문장이 하나여도 {}를 생략할 수 없다.
  • try 블럭 다음에 는 여러 종류의 예외를 처리할 수 있도록 여러개의 catch블럭을 사용할 수 있다.
  • catch 내에 또 다른 try-catch 블럭을 사용할 수 있는데 이때는 다음과 같이 변수 e를 중복해서 사용할 수 없다.
public class Exception1 {
    public static void main(String[] args) {
        try {
            try { } catch (Exception e) { }
        } catch (Exception e) {
            try{} catch (Exception e){ } // error 'e'가 중복선언
        }
    }
}

예제1

public class ExceptionEx2 {
    public static void main(String[] args) {
        int number = 100;
        int result = 0;

        for (int i = 0; i < 10; i++) {
            result = number / (int) (Math.random() * 10);
            System.out.println("result = " + result);
        }
    }
}

출력해보면 

 

Exception in thread "main" java.lang.ArithmeticException: / by zero
at exception.Exception2.main(Exception2.java:9)

 

에러가 발생한다. 예외처리를 해보자면

 

public class ExceptionEx2 {
    public static void main(String[] args) {
        int number = 100;
        int result = 0;

        for (int i = 0; i < 10; i++) {
            try {
                result = number / (int) (Math.random() * 10);
                System.out.println("result = " + result);
            } catch (ArithmeticException e) {
                System.out.println("0");
            }
        }
    }
}

2. try-catch 문에서의 흐름

try-catch문에서 예외가 발생한 경우와 발생하지 않았을 때의 흐름을 다음과 같이 정리할 수 있다.

  • try 블럭 내에서 예외가 발생한 경우
    • 발생한 예외와 일치하는 catch 블럭이 있는지 확인한다.
    • 일지하는 catch 블럭을 찾게 되면 그 catch블럭 내의 문장들을 수행하고 전체 try-catch문을 빠져나가서 그 다음 문장을 계속해서 수행한다, 만일 일치하는 catch 블럭을 찾지 못하면 예외는 처리되지 못한다.
  • try 블럭내에서 예외가 발생하지 않는 경우
    • catch 블럭을 거치지 않고 전체 try-catch문을 빠져나가서 수행을 계속 한다.

3. 예외의 발생과 catch 블럭

  1. catch 블럭은 괄호()와 블럭{} 두 부분으로 나눠져 있는데, 괄호 ()내에서는 처리하고자 하는 예외와 같은 타입의 참조변수 하나를 선언해야 한다.
  2. 예외가 발생하면 해당 클래스의 인스턴스가 만들어진다.
  3. 예외가 발생한 문장이 try-catch문의 try블럭에 있다면, 이 예외는 처리할 수 있는 catch블럭을 찾게 된다.
  4. 첫번째 catch 블럭부터 차례로 내려가면서 catch블럭의 괄호()내에 선언된 참조변수의 종류와 생성된 예외클래스의 인스턴스에 instanceof를 이용해 검사하고 결과가 true이면 예외처리한다. 모든 예외 클래스는 Exception 클래스의 자손이므로 Exception 클래스 타입의 참조변수를 선언해 놓으면 어떤 종류의 예외가 발생해도 이 catch블럭에 의해 처리된다.

예제

    public static void main(String[] args) {
        System.out.println(1);
        System.out.println(2);
        System.out.println(3);
        try {
            System.out.println(0 / 0);
            System.out.println(4);
        } catch (ArithmeticException e) {
            System.out.println(5);
        }
        System.out.println(6);
    }
}

0으로 나눴을 때 ArithmeticException 이 발생하므로, 해당 오류를 예외처리 해주면 된다.

예제

public class ExceptionEx4 {
    public static void main(String[] args) {
        System.out.println(1);
        System.out.println(2);
        System.out.println(3);
        try {
            System.out.println(0 / 0);
            System.out.println(4);
        } catch (ArithmeticException ae) {
            if (ae instanceof ArithmeticException) {
                System.out.println("true");
                System.out.println("ArithmeticException");
            }
        } catch (Exception e) {
            System.out.println("Exception");
        }
        System.out.println(6);
    }
}

 

예외가 발생했을 때 생성되는 예외클래스의 인스턴스에는 발생한 예외에 대한 정보가 담겨져 있으며 다음 method들을 통해 정보를 얻을 수 있다.

  • printStackTrace() : 예외발생 당시의 호출스택에 있었던 메서드의 정보와 예외 메시지를 화면에 출력
  • getMessage() : 발생한 예외클래스의 인스턴스에 저장된 메시지를 얻을 수 있다.

예제

public class ExceptionEx5 {
    public static void main(String[] args) {
        System.out.println(1);
        System.out.println(2);
        System.out.println(3);
        try {
            System.out.println(0 / 0);
            System.out.println(4);
        } catch (ArithmeticException ae) {
            ae.printStackTrace();
            System.out.println("예외메세지 : " + ae.getMessage());
        }
        System.out.println(6);
    }
}

 

4. 예외 발생시키기

키워드 throw를 사용해서 프로그래머가 고의로 예외를 발생시킬 수 있으며, 방법은 아래와 같다.

  1. 먼저 연산자 new를 이용해서 발생시키려는 예외 클래스의 객체를 만들고
  2. 키워드 throw를 이용해서 예외 발생
Exception e = new Exception("고의로 발생시킴");
throw e;

예제

public class Execption6 {
    public static void main(String[] args) {
        try {
//            Exception e = new Exception("고의로 발생시킴");
//            throw e;
            throw new Exception("고의로 발생시킴");
        } catch (Exception e) {
            System.out.println("예외메시지 : " + e.getMessage());
            e.printStackTrace();
        }
        System.out.println("프로그램이 정상 종료되었음");
    }

실행결과

public class Exception7Ex {
    public static void main(String[] args) {
        //Checked Exception vs UnChecked Exception
        // throw new Exception(); // 고의로 Exception 발생
        // 실행 결과 : java: unreported exception java.lang.Exception; must be caught or declared to be thrown

        throw new RuntimeException(); // 고의로 RuntimeException 발생
        // 실행 결과 : Exception in thread "main" java.lang.RuntimeException
        //  at exception.Exception6.main(Exception6.java:9)
        //
        //Process finished with exit code 1
    }
}

5. finally 블럭

try 블럭이 끝날 때 항상 수행되는 블럭

  • Exception 이 발생하더라도
  • return, continue 또는 break 등이라도
public class Exception8Ex {
    public static void main(String[] args) {
        PrintWriter out = null;
        try {
            out = new PrintWriter(new FileWriter("OutFile.txt"));
            ...
        } catch (IOException e) {
            ...
        } finally {
            if (out != null) {
                out.close();
            }
        }
    }
}

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

필요에 따라 새로운 예외 클래스를 정의할 수 있다.

public class MyException extends Exception {
    private final int ERR_CODE;

    MyException(String msg, int errCode) {
        super(msg);
        ERR_CODE = errCode;
    }

    MyException(String msg) {
        this(msg, 100);
    }

    public int getERR_CODE() {
        return ERR_CODE;
    }
}

Exception

  • checked
  • 컴파일러에 의해 반드시 예외처리해야 한다.
  • IOException, SQLException, DataAccessException, ClassNotFoundException, InvocationTargetException, MalformedURLException, ..

RuntimeException

  • unchecked
  • 컴파일러가 확인하지 않으며, 잘못된 개발에 의해 발생하는 예외상황
  • NullPointerException, ArrayIndexOutOfBound, IllegalArgumentException, IllegalStateException, ...