
리소스 자동 닫기
리소스(resource)란 데이터를 제공하는 객체를 말합니다. 리소스는 사용하기 위해 열어야(Open) 하며, 사용이 끝난 다음에는 닫아야(close) 합니다. 예를 들어, 파일 내용을 읽기 위해서는 파일을 열어야 하며, 다 읽고 난 후에는 파일을 닫아야 다른 프로그램에서 사용할 수 있습니다. 리소스를 사용하다가 예외가 발생된 경우에도 안전하게 닫는 것이 중요합니다. 그렇지 않으면 리소스가 불안정한 상태로 남아있게 됩니다.
FileInputStream fis = null;
try {
fis = new FileInputStream("file.txt"); // 파일 열기
...
} catch(IOException e) {
...
} finally {
fis.close(); // 파일 닫기
}
try - with - resources
try-with-resources 블록을 사용하면 예외 발생 여부와 상관없이 리소스를 자동으로 닫아줍니다.
try(FileInputStream fis = new FileInputStream("file.txt")) {
...
} catch(IOException e) {
...
}
try-with-resources 블록을 사용하기 위해서는 조건이 하나 있습니다. 리소스는 java.lang.AutoCloseable 인터페이스를 구현해서 AutoCloseable 인터페이스의 close() 메서드를 재정의 해야합니다.
public class FileInputStream implements AutoCloseable {
...
@Override
public void close() throws Exception {...}
}
복수 개의 리소스를 사용해야 한다면 다음과 같이 try() 괄호 안에 세미콜론(;)으로 구분해서 리소스를 여는 코드를 작성하면 됩니다. Java8 이전 버전은 try 괄호 안에서 리소스 변수를 반드시 선언해야 했지만, Java9 이후부터는 외부 리소스 변수를 사용할 수 있습니다.
// Java 8 이전 버전
try(
FileInputStream fis1 = new FileInputStream("file1.txt");
FileInputStream fis2 = new FileInputStream("file2.txt")
) {
...
} catch(IOException e) {
...
}
// Java 9 이후 버전
FileInputStream fis1 = new FileInputStream("file1.txt");
FileInputStream fis2 = new FileInputStream("file2.txt");
try(fis1; fis2) {
...
} catch(IOException e) {
...
}
예외 떠넘기기
메서드 내부에서 예외가 발생할 때 try-catch 블록으로 예외를 처리하는 것이 기본이지만, 메서드를 호출한 곳으로 예외를 떠넘길 수도 있습니다. 이 때 사용하는 키워드가 throws 입니다. throws는 메서드 선언부 끝에 작성하는데, 떠넘길 예외 클래스를 쉼표로 구분해서 나열해주면 됩니다.
throws 키워드가 붙어 있는 메서드에서 해당 예외를 처리하지 않고 떠넘겼기 때문에 이 메서드를 호출한 곳에서 예외를 받아 처리해야 합니다. 예를 들어 ClassNotFoundException을 throws 하는 method2()의 예외를 method1()에서 호출할 때 처리하고 있습니다.
public void method1() {
try {
method2();
} catch(ClassNotFoundException e) { // 호출한 곳에서 예외 처리
System.out.println("예외 처리: " + e.getMessage());
}
}
public void method2() throws ClassNotFoundException {
Class.forName("java.lang.String2");
}
나열해야 할 예외 클래스가 많은 경우에는 throws Exception 또는 throws Throwable 만으로 모든 예외를 간단히 떠넘길 수도 있습니다. main() 메서드에서도 throws 키워드를 사용해서 예외를 떠넘길 수 있는데, 결국 JVM이 최종적으로 예외 처리를 하게 됩니다. JVM은 예외의 내용을 콘솔에 출력하는 것으로 예외 처리를 합니다.
public class ThrowsExample2 {
public static void main(String[] args) throws Exception{
findClass();
}
public static void findClass() throws ClassNotFoundException {
Class.forName("java.lang.String2");
}
}
Exception in thread "main" java.lang.ClassNotFoundException: java.lang.String2 at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641) at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188) at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:525) at java.base/java.lang.Class.forName0(Native Method) at java.base/java.lang.Class.forName(Class.java:375) at ch11.sec05.ThrowsExample2.findClass(ThrowsExample2.java:9) at ch11.sec05.ThrowsExample2.main(ThrowsExample2.java:5)
사용자 정의 예외
표준 라이브러리 외에 존재하지 않는 예외 클래스를 정의해서 사용해야 할 때가 있습니다. 이것을 사용자 정의 예외라고 합니다.
사용자 정의 예외는 일반 예외로 선언할 수도 있고, 실행 예외로 선언할 수도 있습니다. 통상적으로 일반 예외는 Exception의 자식 클래스로 선언하고, 실행 예외는 RuntimeException의 자식 클래스로 선언합니다.
사용자 정의 예외 클래스에는 기본 생성자와 예외 메시지를 입력 받는 생성자를 선언해 줍니다. 예외 메시지는 부모 생성자 매개값으로 넘겨주는데, 그 이유는 예외 객체의 공통 메서드인 getMessage()의 리턴값으로 사용하기 위해서 입니다.
public class InsufficientException extends Exception{ // 일반 예외로 선언
public InsufficientException() {
}
public InsufficientException(String message) {
super(message);
}
}
예외 발생 시키기
throw된 예외는 직접 try-catch 블록으로 예외를 처리할 수도 있지만, 대부분은 메서드를 호출한 곳에서 예외를 처리하도록 throws 키워드로 예외를 떠 넘깁니다.
// 직접 예외 처리
void method() {
try {
...
throw new Exception("예외메시지");
...
} catch(Exception e) {
String message = e.getMessage();
}
}
// 호출한 곳으로 예외 떠 넘기기
void method() throws Exception{
...
throw new Exception("예외메시지");
...
}
'Java > 기초문법' 카테고리의 다른 글
| [Java] 제네릭 (0) | 2024.10.08 |
|---|---|
| [Java] JDBC 개념 (1) | 2024.10.04 |
| [Java] 예외와 예외 클래스(1) (1) | 2024.09.29 |
| [Java] 접근 제한자 (0) | 2024.09.25 |
| [Java] final 필드와 상수 (0) | 2024.09.24 |