이 글은 백기선님의 라이브 스터디 참여 및 학습 내용에 관한 정리한 글입니다.
자바에서 프로그램이 실행되는 도중에 발생하는 예외 사항들에 대한 처리를 위해서 try
,catch
,finally
문을 사용할 수 있다.
catch 블록과 finally 블록은 선택적인 옵션으로 반드시 사용할 필요는 없으며, 사용할 수 있는 적합한 모든 try 구문은 다음과 같다. (다른 제어문들과는 달리 예외 처리문은 중괄호({})를 생략할 수 없다)
- try / catch
- try / finally
- try / catch / .. / finally
문법 예시
try {
// 예외가 발생할 여지가 있는 코드를 이 블럭안에 기술한다
} catch (Exception e1) {
// e1 예외가 발생할 경우 실행될 코드
} catch (Exception e2) {
// e2 예외가 발생할 경우 실행될 코드
} finally {
// 예외 발생 여부 상관없이 무조건 실행되는 코드
}
예외처리 실행 순서
-이미지 참조 : http://www.tcpschool.com/lectures/img_java_exception_intro.png
위 그림에서 try, finally 블록 구문들은 무조건 수행될 코드들이며 try 블럭 안에서 실행중 예외가 발생하게 된다면 그 예외를 받는 catch 구문으로 자동으로 넘어가 처리될 것이다.
정리하자면 크게 다음과 같은 순서로 진행되는 것이다.
try 블럭 실행 -> (예외발생시) catch 블럭 실행 -> finally 블럭 실행
주의점
이외에도 주의할 사항은 참조 변수 중복이 선언 불가하다는 것이다. 참조 변수 중복 선언이 안된다는 것은 try-catch 문 안에 또 try-catch 구문이 있을때 혼동될 수 있을 것같다. 이는 try-catch구문 안에서도 변수의 스코프가 적용되기 때문이며 중복 선언을 하려하는 경우 컴파일 에러가 발생하게 된다 다음은 억지로 만들어낸 예시이며 가볍게 보자.
public class Main {
public static void main(String[] args) {
int a = 0;
try {
// int a = 1; 컴파일 에러
int b = 1 / 0; // ArithmeticException 발생!
try {
//int b = 0; 컴파일 에러
} catch(Exception e) {
}
} catch(ArithmeticException e) {
int c = 0;
try {
//int a = 1; 컴파일 에러
int b = 1; // b는 try 블럭 안에서 선언된 지역변수이므로
// catch 블럭 안에서는 변수의 Scope이 다르다.
//int c = 1; 컴파일 에러
} catch(Exception e1) { // catch문 매개 변수 네이밍 또한 중복 불가
}
}
}
}
간단히 정리하자면, try/catch 각 구문별 블럭을 기준으로 변수의 스코프를 생각하면 될 것 같다.
자바에서는 throw 키워드를 사용하여 강제로 예외를 발생시키는 것이 가능하다
try {
throw new Exception(); // throw 키워드를 통해 Exception을 강제로 발생시켰다.
} catch(Exception e) {
//예외 핸들링
}
위 사용된 throw키워드랑 비슷하지만 사용되는 의미는 다르다. throws 키워드는 메소드 선언부에서 사용되며 해당 메소드 블럭 내에서 발생할 수 있는 예외를 미리 명시하는 것이 가능하다. 이를 통해 메소드 내에서 try-catch 구문으로 감싸 핸들링을 하지 않아도 되며 일종의 메소드 내부에서 예외 처리에 대한 책임을 회피하는 것으로 보면된다. 메소드를 사용할 때 발생할 예외 사항을 이 메소드 사용자에게 인지 시키는 것이 가능하며, 예외 처리를 강제할 수 있다.
class B {
static void run() throws Exception() {
throw new Exception(); // 강제로 예외를 발생시키고 throws를 통해 예외 회피
}
}
public class Main {
public static void main(String[] args) {
try {
B.run();
} catch(Exception e) {
// 예외 처리..
}
}
}
Java SE 7 부터 사용한 자원을 자동으로 해제해 주는 try-with-resource 문을 사용할 수 있다.
문법
try(파일을 열거나 자원을 할당하는 명령문) { }
try 블록에 괄호를 추가해 파일을 열거나 자원을 할당하는 명령문을 명시했을 경우, 해당 try 블록이 끝나자마자 자동으로 파일을 닫거나 할당된 자원을 해제해 준다.
class TestFile {
// java 7 아래 버전에서
static String readFile1(String filePath) throws IOException {
BufferedReader br = new BufferedReader(new FileReader(filePath));
try {
return br.readLine();
} finally {
if (br != null) br.close();
}
}
// java 7 부터
static String readFile(String filePath) throws IOException {
try (BufferedReader br = new BufferedReader(new FileReader(filePath))) {
return br.readLine();
}
}
}
자바에서 프로그램 실행 중 발생할 수 있는 오류는 Exception, Error 가 있으며, 이 두가지를 클래스로 추상화했다. 예외와 에러는 각각 다음과 같은 계층 구조를 가지고 있다.
- 이미지 참조 : https://madplay.github.io/img/post/2019-03-02-java-checked-unchecked-exceptions-1.png
Exception과 Error는 Throwable 클래스를 상속받고 있으며 Throwable은 Object를 상속 받고 있는 것을 볼 수 있다.
Exception 클래스를 상속받는 여러 Exception들 중에서 RuntimeException을 제외한 나머지는 Checked Exception이라고 표현하며, RuntimeException은 Uncheked Exception이라고도 표현한다.
컴파일 시점에서 확인할 수 있는 예외다. 개발자가 코딩을 하던 중 코드 내에서 CheckedException을 발생하게 된다면 반드시 해당 예외에 대한 핸들링을 해주어야 한다.
아래와 같은 코드 처럼 예외 처리를 안해주면 컴파일을 할 수 없다.
try {
throw new IOException(); // 예외를 던진다
} catch(Exception e) {
// 예외 핸들링..
}
주로 개발자의 실수에 의해서 발생하게 되며 대표적으로 배열에서 범위를 벗어난 index로 접근할 때 발생하는 IndexOutOfBoundException이나, Null 인 값을 참조해서 멤버 변수를 호출하면서 발생하는 NullPointerException과 같은 것들이 대표적이라고 볼 수 있다. 이 Unchecked Exception 항목들은 별다른 예외 처리 없이 컴파일 시 문제가 되진 않으나 이 또한 예외가 발생하지 않도록 개발자가 주의하여 코딩하는 것이 중요하다.
class Main {
public static void main(String[] args) {
throw new RuntimeException(); // 이와 같이 RuntimeException을 던져도 컴파일상 문제 없다
}
}
Checked vs Unchecked 비교
Checked Exception | Unchecked Exception | |
---|---|---|
클래스 | Exception 클래스의 자손들 중 RuntimeException을 제외한 모든 클래스 | Runtime Exception 클래스와 그 자손 클래스 |
처리여부 | 반드시 예외 처리가 필요 | 명시적으로 처리를 강제하지 않음 |
확인시점 | 컴파일 단계 | 실행 단계 |
대표적인 예외 | - IOException - SqlException | - NullPointerException - IllegalArgumentException - IndexOutOfBoundException - SystemException |
자바에서는 Exception 클래스를 상속받아 개발자가 자신만의 새로운 예외 클래스를 정의하고 사용이 가능하다. 커스텀 예외 클래스는 생성자뿐만 아니라 필드, 메소드도 원하는 만큼 추가가 가능하다.
class MyException extends RuntimeException {
public MyException(String errorMessage) {
super(errorMessage);
}
}