코드를 작성하고 컴파일 후 실행해 보면 특정 부분에서 오류가 발생하는 경우가 있다. 컴파일하기 전에는 오류가 발생하지 않았지만 실행 시 오류가 발생하는 것이다. 이를 런타임 에러라고 하며, 이 오류로 인한 프로그램 강제 종료를 회피하기 위해서는 해당 오류에 대한 대처 방안을 미리 대비해 놓아야 한다.
에러의 종류
에러는 크게 세 종류로 나눌 수 있다.
컴파일 에러 | 컴파일하는 시점에 발생하는 에러 |
런타임 에러 | 실행하는 도중에 발생하는 에러 |
논리적 에러 | 실행은 문제없지만, 의도와 다르게 동작하는 것 |
컴파일 에러 - 오타, 잘못된 구문, 자료형 체크 등의 검사를 수행하면서 문법적으로 오류가 있는 부분을 검출한다.
런타임 에러 - 실행 시에 발생하는 객체 할당 부재, 배열 크기 초과, 스택 오버플로우 등의 오류를 말한다.
논리적 에러 - 예를 들어, 10번 순회할 목적이었지만 실제 실행 시에는 100번 순회한다거나 게임에서 적에게 공격 받았지만 대미지를 받지 않는 경우 등이 있다.
이 중에서 런타임 에러에 대한 대비를 예외처리를 통하여 할 수 있다. 런타임 에러는 메모리 부족, 스택오버플로우와 같은 되돌릴 수 없는 치명적인 오류를 에러(error)라고 하고, 수습할 수 있는 정도의 경우는 예외(exception)라고 한다.
예외 클래스 계층 구조
예외 클래스의 최상위 조상은 당연히 Object이다. Throwable 아래로 예외와 에러가 있는 것을 볼 수 있다.
Exception의 자손들은 IOException, ClassNotFoundException, ... , RuntimeException이 있다.
그중에서 프로그래머의 실수와 관련이 깊은 클래스가 RuntimeException이다. 주로 배열 범위 초과, 객체 할당 부재, 형변환 관련 등의 이유로 발생한다.
나머지 Exception의 자손 클래스들은 사용자 실수와 같은 외적인 요소에 의해 발생되는 예외와 관련이 있다.
try - catch문
try - catch문 사용
try {
// 예외가 발생할 수 있는 문장들이 이곳에 들어간다.
} catch (Exception e) {
// 예외가 발생했을 경우 해당 블럭이 실행된다.
} finally {
// 예외 발생여부와 상관없이 실행됨
}
try - catch문을 이용하여 예외 처리를 할 수 있다. catch 괄호 안에는 예외 클래스 참조 변수를 작성해야 한다. Exception의 경우 모든 예외의 최상위 클래스이므로 어떤 예외가 발생했든지간에 예외가 발생했으면 catch 블럭 안의 문장들이 무조건 실행된다.
finally 블럭은 try - catch문의 가장 마지막 부분에 위치해야 한다.
catch 블럭 여러 개 사용
try {
// 예외가 발생할 수 있는 문장들이 이곳에 들어간다.
첫 번째 문장...;
두 번째 문장...;
} catch (NullPointerException e1) {
// 예외가 발생했을 경우 해당 블럭이 실행된다.
} catch (ArithmeticException e2) {
// 예외가 발생했을 경우 해당 블럭이 실행된다.
}
catch 블럭은 여러개를 이어서 사용할 수 있다. 하지만 예외가 먼저 발생한 catch 블럭 안의 문장들만 실행되기 때문에 주의해야 한다. 그리고 만약에 첫 번째 문장에서 예외가 발생하게 되면 해당 예외에 대한 catch 블럭 안의 문장들이 실행되고, 다시 두 번째 문장이 실행되는 것이 아니라 try - catch문을 아예 벗어나기 때문에 이를 주의해야 한다.
멀티 catch블럭
try {
// 예외가 발생할 수 있는 문장들이 이곳에 들어간다.
첫 번째 문장...;
두 번째 문장...;
} catch (NullPointerException | ArithmeticException e) {
// 예외가 발생했을 경우 해당 블럭이 실행된다.
}
JDK1.7부터는 여러 catch 블럭을 하나의 블럭으로 합쳐서 사용할 수 있게 되었다. 예외 클래스는 두 가지만 사용할 수 있는 것은 아니고 여러 개를 사용할 수도 있다. 다만, 조상과 자손 관계에 있는 클래스 사용 시에는 조상 클래스만 사용해야 컴파일 에러가 발생하지 않는다.
메서드에 예외 선언
try - catch문을 사용하지 않아도 메서드에 예외를 선언할 수 있다. 이 경우에는 throws 키워드를 사용하며, 예외가 여러 개인 경우는 쉼표(,)로 구분하여 표기한다.
void method() throws ArrayIndexOutOfBoundsException, NullPointerException {
// 함수 내용
}
위 예시는 배열의 인덱스를 넘어서거나 할당 되지 않은 참조 변수를 사용할 때 발생하는 예외를 자신을 호출한 메서드에게 넘겨준다. main() 함수에서 해당 함수를 호출했으면 main() 함수로 예외가 넘어가는 것이다. 위의 함수에서 해당 예외 처리를 하지 않았으면 main() 함수에서 예외 처리를 해주어야 한다. 동일하에 메서드 A(), B(), C()가 있다고 하고, 이 순서대로 메서드를 호출했다고 가정해보자. C()에서 예외가 발생했을 경우 해당 메서드에서 예외 처리를 해주지 않았다면 C()를 호출한 B()로 예외가 넘어간다. 이 경우는 B()에서 예외처리를 해주거나 A()로 예외를 넘겨주게 된다. A()에서 main()인 경우도 동일하게 적용되며, 예외가 main() 메서드까지 넘어왔다면 해당 메서드에서 반드시 예외 처리를 해주어야 프로그램이 종료되지 않는다.
직접 예외 발생시키기
Exception e = new Exception("직접 발생시킨 예외");
throw e;
// 또는
throw new Exception("직접 발생시킨 예외"); // Exception 객체 생성 후, 바로 예외를 발생시킨다.
예외를 직접 발생시킬 수도 있다. Exception 클래스 객체를 생성한 후에 해당 객체를 throw 키워드를 사용하여 예외를 발생시킨다. 해당 키워드를 사용하여 예외를 발생시키면 실제 예외가 발생했을 때처럼 컴파일 시에 오류가 발생하여 컴파일 되지 않는다.
해당 게시글은 자바의 정석을 바탕으로 공부한 내용을 정리한 글입니다.
'공부 > Java' 카테고리의 다른 글
[Java] 람다식 (Lambda expression) (0) | 2024.02.23 |
---|---|
[Java] 생성자 (Constructor) (0) | 2024.02.22 |
[Java] 날짜 및 시간 관련 클래스 (Date, Calendar, Timestamp, LocalDate 등) (0) | 2023.12.25 |
[Java] JDBC (Java Database Connectivity) (0) | 2023.12.22 |
[Java] 컬렉션 프레임워크(Collections Framework) (0) | 2023.05.21 |