Checked Exception : 컴파일 시점에 확인
public void readFile(String fileName) throws FileNotFoundException {
BufferedReader reader = new BufferedReader(new FileReader(fileName));
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
reader.close();
}
→ FileNotFoundException 예외가 발생할 가능성이 있으므로 메소드 선언부에 throws FileNotFoundException을 추가하여 예외 처리를 미루고 있음
→ 이 코드를 호출할 때는 try-catch 블록으로 FileNotFoundException을 처리하거나, 호출한 메소드에서 다시 throws로 예외 처리를 미룰 수 있음
Unchecked Exception : 런타임 시점에 확인되는 예외
public class ArrayExample {
public static void main(String[] args) {
int[] arr = {1, 2, 3};
System.out.println(arr[3]);
}
}
→ ArrayIndexOutOfBoundsException이 발생
→ 이 예외는 컴파일 시에 체크되지 않기 때문에, 이 예외를 처리하기 위해서는 예외 처리 코드를 추가해야 함
→ 예외 처리 코드 (try catch 블록)
public static void main(String[] args) {
int[] arr = {1, 2, 3};
try {
System.out.println(arr[3]);
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("배열 인덱스를 벗어났습니다.");
}
}
throws 절을 이용하여 예외 처리를 미루는 경우에는 해당 예외를 처리할 책임이 있는 상위 메서드에서 예외 처리를 수행해야 함.
public static void main(String[] args) throws IOException {
BufferedReader reader = new BufferedReader(new FileReader("file.txt"));
try {
String line = reader.readLine();
System.out.println(line);
} finally {
reader.close();
}
}
→ throws 절을 이용해서 예외처리를 했으므로, main 메서드를 호출한 상위 메서드에서 예외를 처리해야 한다. (즉, main 메서드를 호출하는 코드는 없으므로 JVM이 예외 처리를 책임)
→ JVM은 기본 예외 처리기를 호출하여 예외 처리 가능 But, 예외 처리는 일반적으로 프로그램에서 적절한 처리를 수행하는 것이 바람직
try 블록 안에서 예외가 발생하면 해당 예외를 던지고, catch 블록에서 예외를 처리, finally 블록에서는 예외 발생 여부와 관계없이 항상 수행되는 코드
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class FileHandler {
public static void main(String[] args) {
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader("file.txt"));
String line = reader.readLine();
System.out.println(line);
} catch (IOException e) {
System.err.println("Error reading file: " + e.getMessage());
} finally {
try {
if (reader != null) {
reader.close();
}
} catch (IOException e) {
System.err.println("Error closing file: " + e.getMessage());
}
}
}
}
→ 파일을 닫는 코드를 finally 블록 내에 작성하여, 파일 핸들을 안전하게 해제할 수 있도록 함 즉, 핸들링!!
import java.io.*;
public class FileReaderClass {
public static void readFile(String fileName) throws IOException {
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader(fileName));
String line = null;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
} finally {
if (reader != null) {
reader.close();
}
}
}
}
public class MainClass {
public static void main(String[] args) {
String fileName = "input.txt";
try {
FileReaderClass.readFile(fileName);
} catch (IOException e) {
System.out.println("파일을 읽는 도중 오류가 발생했습니다: " + e.getMessage());
} finally {
System.out.println("프로그램을 종료합니다.");
}
}
}
→ readFile 클래스 안에서는 BufferReader 객체를 생성하여 파일을 읽어들이는 동작을 수행하는데 이때 try-finally블록을 사용하여, 파일을 읽어들이는 도중 예외가 발생하더라도 BufferedReader
객체를 항상 닫아주도록 구현
→ try블록에서 예외가 발생하든 발생하지 않든 항상 실행되므로, finally에서 BufferReader 객체를 닫는 동작을 수행한다.
→ readFile 메서드에서 IOException 예외를 미루는 이유는 파일을 읽어들이는 도중 예외가 발생할 가능성이 있기 때문이다. 만약 예외가 발생하면 main 메서드로 던진다.
→ main 메서드가 예외를 받으면 try-catch-finally블록을 사용하여 예외를 처리
→ 예외 처리를 강제하기 때문에 checked exception
(어떤 메서드가 예외를 발생시킬 가능성이 있으며, 이 예외를 해당 메서드를 호출한 곳에서 처리할 수 있다면, throws키워드를 사용하여 예외를 미루는 것이 좋다)
import java.io.*;
public class FileReaderClass {
public static void readFile(String fileName) throws FileNotFoundException, IOException {
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader(fileName));
String line = null;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
} catch (FileNotFoundException e) {
// 파일을 읽는 도중에 예외가 발생한 경우, 예외를 잡아서 새로운 예외를 던진다.
throw new IOException("파일을 읽는 도중 오류가 발생했습니다.");
newException.initCause(e);
throw newException;
} finally {
if (reader != null) {
reader.close();
}
}
}
}
public class MainClass {
public static void main(String[] args) {
String fileName = "input.txt";
try {
FileReaderClass.readFile(fileName);
} catch (IOException e) {
// 예외를 처리할 때, getCause() 메서드를 이용해서 chained exception을 확인할 수 있다.
System.out.println("파일을 읽는 도중 오류가 발생했습니다: " + e.getMessage());
System.out.println("원인 예외: " + e.getCause().getMessage());
} finally {
System.out.println("프로그램을 종료합니다.");
}
}
}
→ readFIle 메서드에서 파일을 읽는 도중 예외가 발생하면 catch 블록에서 IOException을 새로운 예외로 wrapping하여 던지고 있다.
→ 이때, IOException의 생성자에 e를 인자로 전달하여 원래 발생한 예외를 chained exception으로 포함시켜주고 있다.
→ 예외가 발생한 경우 getMessage()메서드를 이용해서 메시지를 출력하고, getCause()메서드를 이용해서 chained exception을 확인하고 있다.
→ FileNotFoundException은 IOException의 하위 클래스 중 하나이다. 그래서 FileNotFoundException은 IOException을 상속받아 구현한다.
즉, FileNotFoundException은 파일을 열 때 파일을 찾을 수 없는 경우에 발생하며, 이 예외는 IOException으로 wrapping된다. 이것이 Chained Exception
(FileNotFoundException은 Java API에서 제공하는 예외 클래스 중 하나로, 파일을 열 때 파일을 찾을 수 없는 경우에 발생 → checked Exception)
→ initCause()를 사용하여 원인 예외를 설정
→ 즉, IOException이 발생한 원인으로 FileNotFoundException을 감싸고 있고 이를 통해 예외 처리에 있어서 원인 예외 FileNotFoundException에 대한 정보를 함께 전달
→ e.getMessage()는 해당 예외 클래스에 정의된 예외 메시지를 반환. 위 코드에서는 IOException
이 발생하므로 "파일을 읽는 도중 오류가 발생했습니다."가 출력
→ e.getCause().getMessage()는 Chained Exception에서 사용된다.
→ 각 예외는 이전 예외의 원인을 저장
→ getCause() 메서드를 호출하면 이전 예외를 반환
→ 즉, 이전 예외의 메시지를 반환하므로 FileNotFoundException에서 정의된 메시지를 반환