본문 바로가기

JAVA/개념

자바(JAVA) - 예외 처리(Exception Handling)

728x90
반응형

예외 처리(Exception Handling)

자바에서 예외(exception)란 사용자의 잘못된 조작이나 개발자의 코딩 실수로 인해 발생하는 프로그램 오류를 말합니다. 예외가 발생되면 프로그램은 곧바로 종료된다는 점에서 에러와 동일하나, 예외는 예외 처리를 통해 프로그램을 종료하지 않고 정상 실행 상태가 유지되도록 할 수 있습니다.

자바의 예외에는 일반 예외와 실행 예외가 있고,

각각 Checked ExceptionUnchecked Exception으로 부를 수 있습니다.

전자인 일반 예외, 즉 Checked Exception은 개발자가 반드시 예외 처리를 직접 진행해야 합니다.

 

요약 - 외란 error에 일종이며, 발생시 시스템 및 프로그램을 불능상태를 야기함

하지만 이를 막기 위해 예외 처리를 통해, 시스템 및 프로그램을 정상실행 상태로 유지하도록 함

 

 

예외(Exception)의 종류(일반예외, 실행예외)

Exception에는 크게 2가지 종류가 있습니다. 

컴파일 시점에 발생하는 예외를 Exception(일반예외) 라고 하고,

프로그램 실행시에 발생하는 예외를 RuntimeException(실행예외) 라고 합니다.

즉, 예외가 발생하는 시점에 프로그램이 실행 전 후 상태에 따라 이를 구분하면 됩니다.

 

아무튼 이 2가지 종류의 Exception 을 처리하기 위해 자바에서는 java.lang.Exception 이라는 최상위 부모 클래스를 제공합니다. 따라서 모든 Exception 들의 조상은 결국  java.lang.Exception 입니다. 아래 그림을 보겠습니다.

 

보라색 java.lang.Exception 은 자바에서 예외처리를 할 수 있게 제공해주는 최상위 보무 클래스이며

하늘색 Exception들은 단순 Exception입니다. 즉 컴파일 시 발생하는 Exception입니다. (일반예외)

하지만 초록색 RuntimeException은 프로그램 실행시 발생하는 런타임 Exception 입니다. (실행예외)

 

실행 예외 (Unchecked Exception)의 종류

RuntimeException의 자식 클래스들 모두 포함, Unchecked Exception이다. try-catch문으로 예외 처리를 직접 하기보다는 예외가 발생하지 않도록 프로그래머가 주의해야 한다.

1-1. NullPointerException(java.lang.NullPointerException) 객체 참조가 없는 상태일 때 발생합니다. null 값을 갖는 참조 변수로 객체 접근 연산자인 도트(.)를 사용했을 때 발생합니다. 객체가 없는 상태에서 객체를 사용하려 했으니 당연히 예외가 발생합니다.

 

1-2. ArrayIndexOutOfBoundsException 배열에서 인덱스 범위를 초과하여 사용할 때 발생합니다. int[] arr = new int[3]; 이라 해 놓고 arr[4]=5; 같은 대입 연산을 시도할 때 발생할 수 있습니다.

 

1-3. NumberFormatException 문자열로 되어 있는 데이터를 숫자로 변경하는 경우가 많은데, 문자열을 숫자로 변환하는 방법 중 가장 많이 사용되는 코드는 Integer.parseInt(String s) 메소드와 Double.parseDouble(String s) 메소드입니다. 그런데 아래 코드를 확인해 보겠습니다.

 

String data1 = "100";
String data2 = "이건안돼요";
int val1 = Integer.parseInt(data1);
int val2 = Integer.parseInt(data2); //NumberFormatException 발생
 

매개값인 문자열이 숫자로 변환될 수 있다면 숫자를 정상적으로 리턴하지만, 숫자로 변환할 수 없는 문자열이 포함되어 있으면 java.lang.NumberFormatException을 발생시킵니다.

 

1-4. ClassCastException 허용되지 않는데 억지로 타입 변환을 시도할 경우 발생합니다. 추상클래스 Animal을 상속하는 Dog, Cat 클래스와 RemoteControl 인터페이스를 상속하는 Television, Audio 클래스가 있다고 가정해 보겠습니다. 그리고 예외가 발생하는 상황은 아래 코드와 같습니다.

Animal animal = new Dog();
Dog dog = (Dog) animal; // 문제 없음

RemotControl ex1 = new Television();
Television ex2 = (Television) ex1; // 문제 없음

Animal animal = new Animal();
Dog dog = (Dog) animal; // ClassCastException 발생
 

 

예외처리 코드 및 실행 순서(Try-Catch-Finally)

이제 자바 Exception 개념 및 종류에 대해 알아봤으니 예외 처리코드에 대해서 알아보겠습니다.

 

예외 처리 코드 Try - Catch - Finally

 

앞서 말했듯이 예외 처리 코드는 갑작스러운 예외 Exeption 발생으로 인해 시스템 및 프로그램이 불능상태에 빠지지 않고 시스템 및 프로그램이 정상실행 되도록 유지시켜 줍니다.

예외 처리 코드에는 크게 3가지 블록이 존재하는데, 각각에 대해서 알아보겠습니다.

 

Try 블록 : 실제 코드가 들어가는 곳으로써 예외 Exeption이 발생할 가능성이 있는 코드

Catch 블록 : Try 블록에서 Exeption이 발생하면 코드 실행 순서가 Catch 쪽으로 오게됨. 즉 예외에 대한 후 처리 코드

Finally 블록 : Try 블록에서의 Exeption과 발생 유무와 상관 없이 무조건 수행되는 코드 (옵션이라 생략이 가능)

 

자 그렇다면! 예외 처리 코드에 실행 순서에 대해서 알아보겠습니다.

 

Exeption 발생!

Try 블록 수행 -> Catch 블록 수행 -> Finally 블록 수행 (생략가능)

 

Exeption 미발생!

Try 블록 수행 -> Finally 블록 수행 (생략가능)

 

아울러, 예외 처리 코드는 예외 종류(일반예외, 실행예외)에 따라 예외 처리 코드 (Try-Catch-Finally)의 강제여부가 갈린다! 다시 말해, 컴파일시 예외 검사 대상이 되는 일반예외는 예외 처리 코드에 반드시 감싸서 코드를 짜야합니다.. 반면 프로그램 실행이후 발생하는 실행예외는 따로 컴파일러가 예외 처리 코드를 강제하라고 하지 않기 때문에 온전히 개발자의 경험에 의해서 예외 처리 코드를 사용해야합니다.

 

예외 처리 코드 (try~catch~finally~…)

기본 형태는 아래와 같습니다.

try{
///예외가 발생할 가능성이 있는 코드
.... ......... ;
.............. .. ; << 1. 여기서 예외가 발생했을 때
.............. .... ; << 2. 예외 발생한 곳 아래는 실행하지 않고,
}
catch(예외클래스 e) {
   예외 처리; << 3. catch문에서 예외처리를 한다.
}
finally {
///무슨 일이 있든 항상 실행
}

- 다중 catch문

try{
// 예외 1 발생위치
// 예외 2 발생위치
}
catch(예외1을 잡는 곳){
}
catch(예외2를 잡는 곳){
}

발생하는 예외별로 다중 catch문을 구성할 수 있지만, catch 블록이 여러개여도 먼저 발생한 예외에 대하여 단 하나의 catch 블록만 실행됩니다. 그 이유는 위에서 예외 1이 발생했다면, 그 밑의 예외 2 발생 코드는 실행하지 않고 바로 예외 1을 catch하는 곳으로 이동하기 때문입니다.

그러므로 다중 catch 블록을 작성할 때에는 상위 예외 클래스가 하위 예외 클래스보다 밑에 위치하게 작성해야 합니다.

 

try에서 예외가 발생했을 때에는 catch블록이 작성된 순서대로 위에서 아래대로 차례로 검사하는데, 만일 상위 예외 클래스가 더 위에 있었다면 하위 예외 클래스를 실행하지 않게 됩니다. 즉, 하위 예외 클래스가 상위 예외 클래스를 상속했기 때문에, 상위 예외 클래스를 잡는 catch 블록이 실행되게 됩니다.

이 말이 무슨 의미인지, 예를 들어 FileNotFoundException 예외가 발생했다고 가정해 보겠습니다.

try{
// FileNotFoundException 예외 발생
}
catch(Exception e){
    // FileNotFoundException은 Exception의 자식 클래스이므로 이 부분의 catch가 실행됨
}
catch(FileNotFoundException e){
    // 정작 FileNotFoundException을 처리하려는 이 catch 블록은 실행되지 않음
}

` FileNotFoundException의 상위 예외 클래스인 Exception을 처리하는 catch 블록이 더 위에 있기 때문에, FileNotFoundException` 이 생겼을 때 처리하기 위한 두 번째 catch 블록은 실행되지 않습니다.

따라서 Exception 처럼 상위 예외 클래스를 처리하는 코드는 catch문 맨 마지막에 작성합니다.

 

- 멀티 catch

자바 7부터 하나의 catch 블록에서 여러 개의 예외를 처리할 수 있는 기능이 추가되었습니다.

try{
  // 예외 1 발생위치 (혹은 예외 3 발생위치)
  // 예외 2 발생위치
}
catch( 예외 1 | 예외 3  e) // | 는 or 로 보면 된다. 예외 1 혹은 예외 3이 발생하면
해당한 catch 블록에서 처리하게 된다.
{
  예외 처리
}
catch( 예외2 e)
{
  예외 처리
}
 

 

try catch finally문 

예외처리란 프로그램 실핼 시 발생할 수 있는 예기치 못한 예외의 발생에 대비한 코드를 작성하는 것이며, 예외처리의 목적은 예외의 발생으로 인한 실행중인 프로그램의 갑작스런 종료를 막고, 정상적인 실행상태를 유지할 수 있도록 하는 것이다.

try {
    // 예외가 발생한 요지가 있는 소스코드..
} catch(RuntimeException re) {
    // 예외가 발생시 이를 처리하기 위한 소스코드..
} catch(IOException ie) {
    // 예외가 발생시 이를 처리하기 위한 소스코드..
} catch(Exception e) {
    // 예외가 발생시 이를 처리하기 위한 소스코드..
} finally {
    // 예외가 발생하든 안하든 무조건 터리되는 소스코드..
}

try블럭 안에는 예외 발생가능한 소스코드가 있을 경우 감싸주는 곳이며, catch블럭을 통해 예외발생가능 종류별로 잡아내어 예외발생 시 처리하기 위한 문이다. catch문이 없을 경우 예외처리를 하지 못한다. 여기서 IOException에 관련된 예외가 발생했을때는 첫번째 catch문을 무시하며 2번째 catch문을 받게되는데 이는 내부적으로 예외클래스의 인스턴스에 instanceof연산자를 이용해 검사를 하게되는데 true가 발생한 2번째 catch문인 IOEcption의 처리로직을 타게된다.

참고로 위의 예외 클래스의 계층 구조를 보면 Exception 안에 IOException, RuntimeException이 있다고 하였다. 3번째 catch문을 하나만 사용하여도 위의 모든 예외를 다 잡아 낼 수가 있다.

finally는 예외가 발생을 하게되든 안하게 되든 무조건 마지막에 수행되는 블럭 영역이다.

 

예외 떠넘기기

메소드 내부에서 예외가 발생할 수 있는 코드를 작성할 때, try-catch 블록으로 예외 처리 하는 것이 기본이지만, 경우에 따라서는 메소드를 호출한 곳으로 예외 처리를 떠넘길 수도 있습니다. 이 때 사용하는 키워드가 throws입니다.

throws 키워드는 메소드 선언부 맨 끝에 작성하며, 메소드에서 try-catch를 통해 처리하지 않은 예외를 호출한 곳으로 떠넘기는 역할을 합니다. throws 뒤에는 떠넘길 에외 클래스를 쉼표로 여러개 구분해서 나열할 수 있습니다. 발생할 수 있는 예외별로 throws 뒤에 나열하는 것이 보통이나, Exception 만 작성해서 모든 예외를 간단히 떠넘길 수도 있습니다.

public void method1(){
 try {
   method2(); // throws가 붙은 method2는 반드시 이렇게 try문 안에서 호출되어야 함.
   // method2가 떠넘긴 예외를 아래 catch문을 통해 처리해 주고 있다.
}
 catch (ClassNotFoundException e1) {
  System.out.println("클래스가 존재하지 않음.");
}

public void method2() throws ClassNotFoundException {
 Class clazz = Class.forName("java.lang.String22");
}

만일 method1도 예외를 떠넘긴다면 어떻게 될까요? 만일 method1을 main 메소드에서 호출했다면, main 메소드에서 try-catch를 통해 예외 처리를 해 주어야 할 것입니다. 그런데 main() 메소드에서도 예외를 떠넘길 수 있습니다. 그러면 그 예외는 JVM이 최종적으로 처리를 하게 되지만, 이는 권장 사항은 아닙니다.

 

Error와 Exception 차이점

Error와 Exception은 같다고 생각할 수도 있지만, 사실 큰 차이가 있다.

 

- Error : 컴파일 시 문법적인 오류와 런타임 시 널포인트 참조와 같은 오류로 프로세스에 심각한 문제를 야기 시켜 프로세스를 종료 시킬 수 있다.

 

- Exception : 컴퓨터 시스템의 동작 도중 예기치 않았던 이상 상태가 발생하여 수행 중인 프로그램이 영향을 받는 것. 예를 들면, 연산 도중 넘침에 의해 발생한 끼어들기 등이 이에 해당한다.

 

  프로그램이 실행 중 어떤 원인에 의해서 오작동을 하거나 비정상적으로 종료되는 경우를 프로그램 오류라 하고, 프로그램오류에는 에러(error)와 예외(exception) 두 가지로 구분할 수 있다. 에러는 메모리 부족이나 스택오버플로우와 같이 발생하면 복구할 수 없는 심각한 오류이고, 예외는 발생하더라도 수습할 수 있는 비교적 덜 심각한 오류이다. 이 예외는 프로그래머가 적절히 코드를 작성해주면 비정상적인 종류를 막을 수 있다.

  Error의 상황을 미리 미연에 방지하기 위해서 Exception 상황을 만들 수 있다. java에서는 try-catch문으로 Exception handling을 할 수 있다.

 

RuntimeException과 RE가 아닌 것의 차이는?

Exception 클래스의 하위클래스들은 RuntimeException과 그 외 Exception 클래스의 자식 클래스 2가지로 구분할 수 있다.

그렇다면 RuntimeException 클래스와 그 외 클래스들을 구분하는 기준은 무엇일까?

2가지로 구분하는 기준은 CheckedException인지 UncheckedException인지로 판단한다. CheckedException은 반드시 예외를 처리해야하고, UncheckedException은 예외 처리를 강제하지 않는다.

 

RuntimeException

RuntimeException은 런타임 시에 예외를 발생시키며, 실행 전에는 컴파일 에러를 발생시키지 않는다. 즉, RuntimeException은 UncheckedException이다. 예외처리를 강제하지는 않지만 해당 내용이 런타임 시에 예외를 발생시킬 수 있음을 인지하면 예외를 처리해주는 것이 좋다.

대표적인 RuntimeException으론 NullPointerException, IndexOutOfBoundsException, ArithmeticException 등이 있다.

만약 어떠한 정수를 0으로 나누는 코드가 있다고 가정하자. 0으로 정수를 나누는 경우 ArithmeticException을 발생시키지만 컴파일 단계에서 에러를 발생시키지 않는다.

package com.livestudy.ninth;
public class RuntimeExceptionTest {
    public static void main(String[] args) throws IOException {

        System.out.println(3/0);    //컴파일 에러 발생 X
    }
}

하지만, 해당 코드를 실행하면 아래와 같이 런타임 시에 에러를 발생시키는 것을 확인할 수 있다. 즉, ArithmeticException은 UncheckedException임을 확인할 수 있다.

728x90
반응형