예외처리

혜얌·2022년 6월 3일
0

JAVA

목록 보기
1/1

예외처리



에러와 예외의 차이

에러(Error)란 프로그램이 코드로 복구될 수 없는 오류를 의미한다. 예를 들어 메모리가 부족한 경우 직접 제어가 불가능하므로 메모리 부족 에러가 발생한다.

예외는 프로그래머가 직접 예측하여 막을 수 있는 처리가능한 오류를 의미한다. 어떤수를 0으로 나누면 예외가 발생한다.

예외처리하기

try {
	...
} catch (예외 1) {
	...
} catch (예외 2) {
	...
}

try 문 안의 수행할 문장들에서 예외가 발생하지 않는다면 catch문 다음의 문장들은 수행이 되지 않는다.
하지만 try 문안의 문장들을 수행하는 도중에 예외가 발생하면 예외에 해당되는 catch 문이 수행된다.

숫자를 0으로 나누었을 때 발생하는 예외를 처리하려면 다음과 같이 할 수 있다.

int c;

try {
	c = 4 / 0;
} catch (ArithmeticException e) {
	c = -1;
}

final

프로그램 수행 도중 예외가 발생하면 프로그램이 중지되거나 예외 처리에 의해 catch 구문이 실행된다. 하지만 어떤 예외가 발생하더라도 반드시 실행되어야 하는 부분이 있어야 한다면 finally 키워드를 사용하면 된다.

public class Sample {
    public void shouldBeRun() {
        System.out.println("ok thanks.");
    }

    public static void main(String[] args) {
        Sample sample = new Sample();
        int c;
        try {
            c = 4 / 0;
        } catch (ArithmeticException e) {
            c = -1;
        } finally {
            sample.shouldBeRun();  // 예외에 상관없이 무조건 수행된다.
        }
    }
}

다음과 같은 코드를 실행하면 sample.shouldBeRun() 에소드가 수행된다.

Exception

public class Sample {
    public void sayNick(String nick) {
        if("fool".equals(nick)) {
            return;
        }
        System.out.println("당신의 별명은 "+nick+" 입니다.");
    }

    public static void main(String[] args) {
        Sample sample = new Sample();
        sample.sayNick("fool");
        sample.sayNick("genious");
    }
}

이 코드를 보면 sayNick 메소드는 fool이라는 문자열이 입력되면 return 으로 메소드를 종료해 별명이 출력되지 못하도록 하고 있다.

예외를 직접 만들어보기 전에 Exception은 크게 RuntimeException과 Exception로 나눠진다.

RuntimeException은 실행시 발생하는 예외 이고 Exception은 프로그램 작성시 이미 예측 가능한 예외 이다.

그래서 Exception을 Checked Exception, RuntimeException을 Unchecked Exception이라고 한다.

RuntimeException

class FoolException extends RuntimeException {
}

public class Sample {
    public void sayNick(String nick) {
        if("fool".equals(nick)) {
            throw new FoolException();
        }
        System.out.println("당신의 별명은 "+nick+" 입니다.");
    }

    public static void main(String[] args) {
        Sample sample = new Sample();
        sample.sayNick("fool");
        sample.sayNick("genious");
    }
}

위의 예제를 다음과 같이 throw new FoolException() 으로 변경하면 실행시 다음과 같은 예외가 발생한다.

Exception in thread "main" FoolException
    at Sample.sayNick(Sample.java:7)
    at Sample.main(Sample.java:14)

Exception

위 RuntimeException의 예제에서 FoolException 부분을 다음과 같이 변경해보자.

class FoolException extends Exception {
}

이렇게 변경하면 예측 가능한 Checked Exception이기 때문에 Sample클래스에서 컴파일 오류가 발생할 것이다.

컴파일을 하기 위해선 다음과 같이 변경해야 한다.

}

public class Sample {
    public void sayNick(String nick) {
        try {
            if("fool".equals(nick)) {
                throw new FoolException();
            }
            System.out.println("당신의 별명은 "+nick+" 입니다.");
        }catch(FoolException e) {
            System.err.println("FoolException이 발생했습니다.");
        }
    }

    public static void main(String[] args) {
        Sample sample = new Sample();
        sample.sayNick("fool");
        sample.sayNick("genious");
    }
}

예외 던지기 (throws)

위 예제들에선 sayNick 메서드에서 FoolException을 발생시키고 예외처리도 sayNick 메서드에서 했는데 이렇게 하지 않고 sayNick을 호출한 곳에서 예외를 처리하도록 위로 던질 수 있다.

public class Sample {
    public void sayNick(String nick) throws FoolException {
        try {
            if("fool".equals(nick)) {
                throw new FoolException();
            }
            System.out.println("당신의 별명은 "+nick+" 입니다.");
        }catch(FoolException e) {
            System.err.println("FoolException이 발생했습니다.");
        }
    }

    public static void main(String[] args) {
        Sample sample = new Sample();
        sample.sayNick("fool");
        sample.sayNick("genious");
    }
}

sayNick 메소드 뒷부분에 throws 구문을 이용하면 예외를 위로 보낼 수 있다.

이렇게 된다면 main메소드에서 컴파일 에러가 발생할 것이다.
-> throws 구문때문에 FoolException의 예외를 처리해야 하는 대상이 sayNick 메소드에서 main 메소드로 변경되었기 때문이다.

따라서 컴파일 오류를 해결하려면 main 메소드에 try-catch를 추가해야 한다.

class FoolException extends Exception {
}

public class Sample {
    public void sayNick(String nick) throws FoolException {
        if("fool".equals(nick)) {
            throw new FoolException();
        }
        System.out.println("당신의 별명은 "+nick+" 입니다.");
    }

    public static void main(String[] args) {
        Sample sample = new Sample();
        try {
            sample.sayNick("fool");
            sample.sayNick("genious");
        } catch (FoolException e) {
            System.err.println("FoolException이 발생했습니다.");
        }
    }
}

어디서 Exception 처리를 하는게 좋을까?

sayNick? main? 어디서 예외처리를 해야 좋은 것일까?

  1. sayNick에서 처리를 하는 경우

이 경우에는 다음과 같은 두가지 문장이 모두 수행된다.

sample.sayNick("fool");
sample.sayNick("genious");

물론, sample.sayNick("fool");문장 수행시에는 FoolException이 발생하겠지만 그 다음 문장인 sample.sayNick("genious"); 역시 수행된다.

  1. main에서 처리하는 경우

이 경우에는 sample.sayNick("genious"); 가 수행되지 않는다.
왜냐하면 이미 첫번째 문장에서 예외가 발생하여 catch문으로 빠져버리기 때문이다.

try {
    sample.sayNick("fool");
    sample.sayNick("genious");  // 이 문장은 수행되지 않는다.
}catch(FoolException e) {
    System.err.println("FoolException이 발생했습니다.");
}

이러한 이유로 Exception을 처리하는 위치는 프로그램의 수행여부를 결정하기도 하고 트랜잭션 처리와도 밀접한 관계가 있기 때문에 중요하다.

트랜잭션

트랜잭션이란 하나의 작업 단위를 뜻한다.

예를 들어 쇼핑몰의 "상품 발송" 이라는 트랜잭션을 가정해보자. "상품발송" 이라는 트랜잭션에는 다음과 같은 작업이 있다.

- 포장 
- 영수증 발행
- 발송 

쇼핑몰의 운영자는 이 3가지 일들 중 하나라도 실패하면 3가지 모두 취소하고 "상품 발송" 전의 상태로 되돌리고 싶을 것이다.

상품발송() {
    포장();
    영수증발행();
    발송();
}

포장() {
   ...
}

영수증발행() {
   ...
}

발송() {
   ...
}

이러한 코드의 경우 다음과 같이 각 메소드에서 예외를 throw하고 상품발송 메서드에서 throw된 예외를 처리하여 모두 취소하는 것이 완벽한 트랜잭션 처리 방법이다.

상품발송() {
    try {
        포장();
        영수증발행();
        발송();
    }catch(예외) {
        모두취소();  // 하나라도 실패하면 모두 취소한다.
    }
}

포장() throws 예외 {
   ...
}

영수증발행() throws 예외 {
   ...
}

발송() throws 예외 {
   ...
}

이렇게 된다면 세개의 단위작업 중 하나라도 실패할 경우 예외가 발생되어 상품 발송이 모두 최소 될 것이다.



예외의 종류


구분Cehcked ExceptionUncheckedException
확인 시점컴파일 시점런타임 시점
처리 여부반드시 예외 처리해야 한다.명시적으로 하지 않아도 된다.
트랜잭션 처리예외 발생시 롤백 하지 않음예외 발생시 롤백 해야 함

checked Exception

  • IOException
  • SQLException
  • DataAccessException
  • ClassNotFoundException
  • InvocationTargetException
  • MalformedURLException

Unchecked Exception

  • NullPointerException
  • ArrayIndexOutOfBound
  • IllegalArgumentException
  • IllegalStateException

출처 : https://wikidocs.net/229 | https://annajinee.tistory.com/64

profile
얌얌

0개의 댓글