티스토리 뷰
예외 클래스의 계층 구조
자바에서는 실행시 발생할 수 있는 오류를 클래스로 정의하여 Exception과 Error클래스가 있다.
위와 같이 최상위 클래스는 Exception 클래스이며 그 밑으로 IOException, RuntimeException 등이 자리하고 있다.
앞으로 그렇다면 RuntimeException 클래스와 그 서브 클래스들을 RuntimeException 클래스들 이라고 하고 그 외 클래스들을 Exception 클래스들로 구분해보자. 그렇다면 Exception과 RuntimeException에 대해 알아보자
실행 중에 발생하는 RuntimeException
실행 중에 발생하며 시스템 환경적으로나 input 값이 잘못된 경우 또는 프로그래머가 의도적으로 잡아내기 위한 조건 등에 부합할 때 던지는(throw) Excpetion이다.
대표적으로 알 수 있는 것이 ArrayIndexOutOfBoundsException이 있다. 배열이나 리스트 문자열의 인덱스 범위를 벗어나면 던지는 오류이다. 예제를 살펴보자.
package exception;
public class RuntimeEx1 {
public static void main(String[] args) {
int[] arr = new int[4];
try {
arr[6] = 5;
System.out.println(arr[6]);
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("인덱스 벗어났습니다.");
}
}
}
다음은 길이가 4인 배열에 6번째 인덱스에 값을 넣고 출력하는 예제이다. 봐도 알 수 있듯이 오류가 날 수 밖에 없는 예제이다. 이 예제 코드를 실행시키면 다음과 같은 결과가 나올 것이다.
당연한 결과이다. 이런 식으로 RuntimeException은 실행 중에 개발자가 실수할 수 있는 상황에 오류를 던져준다.
이 외에 Exception 클래스들은 주로 외부의 영향으로 발생할 수 있는 것들로서, 프로그램 사용자들의 동작에 의해서 발생하는 경우가 많다. 대표적으로 존재하지 않는 파일의 이름을 입력했을 때 발생하는 FileNotFoundException이나 입력한 데이터 형식이 잘못된 경우 던지는 DataFormatException이 있다.
try-catch문에서의 흐름
try-catch문은 위 코드에서도 보다 싶이 예외처리를 하기 위해 자바에서 제공하는 문법이다. 프로그램 실행 시 발생할 수 있는 예외에 대비한 코드를 작성하는 것이다.
- try 블럭 내에서 예외가 발생한 경우
1. 발생한 예외와 일치하는 catch블럭이 있는지 확인한다.
2. 일치하는 catch블럭을 찾게 되면, 그 catch블럭 내의 문장들을 수행하고 전체 try-catch문을 빠져나가서 그 다음 문장을 계속해서 수행한다. 만일 일치하는 catch블럭이 없다면, 예외는 처리되지 못한다.
- try 블럭 내에서 예외가 발생하지 않는 경우
1. catch블럭을 거치지 않고 전체 try-catch문을 빠져나가 수행을 계속한다.
예제를 살펴보자
package exception;
public class TryEx1 {
public static void main(String[] args) {
System.out.println(1);
System.out.println(2);
try {
System.out.println(3);
System.out.println(4);
} catch (Exception e) {
System.out.println(5); // 실행 x
}
System.out.println(6);
}
}
// 실행 결과
/**
1
2
3
4
6
*/
다음 코드를 실행시키면 5를 제외한 나머지 구문들이 실행될 것이다.
그렇다면 위에서 살펴보았던 예제를 다시 살펴보자.
package exception;
public class RuntimeEx1 {
public static void main(String[] args) {
int[] arr = new int[4];
try {
arr[6] = 5;
System.out.println(arr[6]); // 실행되지 않음
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("인덱스 벗어났습니다."); // 실행 O
}
}
}
이 코드를 실행시키면 try에 있는 구문이 아닌 catch 블럭에 있는 코드가 출력된다는 것을 알 수 있다. 이처럼 자바에서는 try-catch문을 통해 상황에 따라 다른 동작을 하여 예외 상황에서 개발자가 예외 발생을 알 수 있게 도와준다.
예외 발생과 catch블럭
catch블럭은 괄호()와 블럭 {} 두 부분으로 나뉘어져 있는데, 괄호 ()내에는 처리하고자 하는 예외와 같은 타입의 참조 변수 하나를 선언해야 한다. 위에 예제 처럼 ArrayIndexOutOfBoundsException같은 던질 Exception의 인스턴스를 생성해 주어야 한다.
첫 번째 catch블럭부터 차례로 내려가면서 catch블럭의 괄호() 내에 선언된 참조 변수의 종류의 생성된 예외 클래스의 인스턴스에 instanceof연산자를 이용해 검사하게 되는데 검사 결과가 true인 catch블럭을 만날 때 까지 계속된다.
사실 저 catch문안에 이렇게 들어가도 문제는 생기지 않는다.
catch (Exception e) {
System.out.println("인덱스 벗어났습니다."); // 실행 O
}
이유는 간단하다 모든 Exception은 Exception 클래스의 서브 클래스이므로 어떤 종류의 예외가 발생하더라도 이 catch블럭에서 처리된다.
다만, 이럴 경우 모든 예외들이 해당 catch에 걸려 각 예외에 따라 다르게 핸들링 해야하는 상황임에도 한 곳에서 다양하게 처리하기엔 무리가 있을 수 있다. 그렇기 때문에 최대한 범위를 좁혀서 catch문을 좀 늘리는 방법을 고려하자
PrintStackTrace()와 getMessage()
예외가 발생했을 때 생성되는 예외 클래스의 인스턴스에는 발생한 예외에 대한 정보가 담겨져 있으며 getMessage()와 printStackTrace() 함수를 통해 이 정보를 얻을 수 있다.
printStackTrace() : 예외 발생 당시의 호출 스택(Call Stack)에 있었던 메소드의 정보와 예외 메시지를 화면에 출력한다.
getMessage() : 발생한 예외클래스의 인승턴스에 저장된 메시지를 얻을 수 있다.
예제를 살펴보자
package exception;
public class PrintStackEx {
public static void main(String[] args) {
System.out.println(1);
System.out.println(2);
try {
System.out.println(3);
System.out.println(0 / 0); // 예외 발생
System.out.println(4);
} catch (ArithmeticException e) {
e.printStackTrace(); // e 참조 변수를 통해 인스턴스에 접근
System.out.println("message : " + e.getMessage());
}
System.out.println("end");
}
}
// 실행 결과
// 1
// 2
// 3
// java.lang.ArithmeticException: / by zero
// at exception.PrintStackEx.main(PrintStackEx.java:12)
// message : / by zero
// end
언뜻 보면 비정상적으로 종료되었을 때의 결과와 비슷해 보이지만 마지막 end를 출력 후 프로그램이 종료되면서 프로그램이 모두 실행되고 정상적으로 종료가 된 것을 알 수 있다. 이런 식으로 프로그램이 비정상적으로 종료 되지 않게 해주면서 예외 발생 원인이 무엇인지도 Exception을 던져 개발자에게 알려주고 있다.
예외 발생시키기
키워드 throw를 사용하여 개발자가 고의로 예외를 던질 수 있다. 방법은 아래와 같다.
1. 먼저, 연산자 new를 이용해서 발생시키려는 예외 클래스의 객체를 만든 다음
Exception e = new Exception("고의로 발생시킨 예외")
2. 키워드 throw를 이용해서 예외를 발생시킨다. -> throw e
package exception;
public class TrowExcpEx {
public static void main(String[] args) {
try {
// Exception e = new RuntimeException("고의로 발생시킨 예외");
// throw e;
throw new RuntimeException("고의로 발생시킨 예외"); // 줄여서 사용한 코드
} catch (Exception e) {
System.out.println("e.getMessage() = " + e.getMessage());
e.printStackTrace();
}
System.out.println("프로그램 종료");
}
}
인스턴스를 생성할 때 넣어준 생성자에 넣어준 문자가 정확히 getMessage를 통해 출력된 것이 보인다. 이러한 상황이 웹 개발을 하면서도 많이 발생하는데 대표적인 것이 BadRequestException이다.
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus(HttpStatus.BAD_REQUEST)
public class BadRequestException extends RuntimeException {
private static final long serialVersionUID = 1L;
public BadRequestException(String message) {
super(message);
}
}
요청 받은 값에 null이 포함되어 제대로 처리가 힘든 상황에서 던져주는 Exception이다. 이 Exception을 통해 클라이언트가 잘못한 오류기 때문에 서버쪽에서 미리 대비를 해두고 던져주어 서버가 아닌 클라이언트의 잘못이라고 알려주고 있는 것이다.
이 외에도 컴파일러에서 개발자의 실수를 알리기 위해 예외 처리를 하는 경우도 있지만 RuntimeException 클래스들은 그렇게 친절하지 않다 unchecked Exception이라고도 불리는 이유가 이것이다. 컴파일러에서 따로 처리를 안하기 때문에 이러한 실수를 예방하기 위해 개발자는 반드시 예외 처리를 해주어야한다.
try {
List<Integer> list = new ArrayList<>();
Sytem.out.println(list.get(0));
} catch (ArrayIndexOutOfBoundsException ae) {
...
} catch (NullPointerException ne) {
...
}
'Java' 카테고리의 다른 글
[자바 고급 스터디 2주차 - 1부] Wrapper Class (0) | 2022.02.13 |
---|---|
[자바 고급 스터디 1주차 - 3부] Stream과 Optional (0) | 2022.02.10 |
[자바 고급 스터디 1주차 - 2부] Stream 심화 (0) | 2022.02.09 |
[자바 고급 스터디 1주차 - 1부] Stream이란? (0) | 2022.02.08 |
Day2_ExceptionHandling 2부 (0) | 2021.11.20 |
- Total
- Today
- Yesterday
- 면접 준비
- JPA
- thread
- java
- 게시판
- 인터뷰
- 자바
- 면접
- Redis
- 백엔드
- 개발자
- DB
- Kotlin
- 면접준비
- 프로그래밍
- Spring
- 프로젝트
- CS
- 취준
- swarm
- 개발
- DevOps
- IT
- 취업
- 동시성
- 취업준비
- docker
- 코드
- MySQL
- 코딩
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |