프로그래밍 공부
[자바] #03. 예외 클래스의 구분, try-with-resources 구문 본문
예외 클래스의 최상의 클래스는 Throwable,
이를 상속하는 예외 클래스들은 다음과 같이 나뉜다.

Throwable 클래스의 구조
Throwable
├── Error
│ ├── OutOfMemoryError
│ ├── StackOverflowError
│ ├── VirtualMachineError
│ ├── ...
│
├── Exception
│ ├── IOException
│ ├── SQLException
│ ├── ClassNotFoundException
│ ├── RuntimeException
│ ├── NullPointerException
│ ├── IndexOutOfBoundsException
│ ├── ArithmeticException
│ ├── IllegalArgumentException
│ ├── ...
Error와 Exception으로 크게 나뉜다.
Error 클래스
- Error는 개발자가 직접 처리하지 않는 심각한 시스템 오류.
- JVM(Java Virtual Machine) 관련 오류, 일반적으로 프로그래머가 직접 예외 처리를 하지 않는다.
- 프로그램이 복구할 수 없는 심각한 오류이기 때문에, 예외 처리를 시도하지 않고 프로그램이 종료되는 경우가 많다.
🔍 주요 Error 예시
| 예외 클래스 | 설명 |
| OutOfMemoryError | 메모리가 부족할 때 발생 (Heap 영역 부족) |
| StackOverflowError | 무한 재귀 호출로 인해 스택 메모리가 초과될 때 발생 |
| VirtualMachineError | JVM 관련 치명적인 오류 발생 |
| NoClassDefFoundError | 클래스 파일을 찾을 수 없을 때 발생 |
📌 Error 발생 예제
public class StackOverflowExample {
public static void recursiveMethod() {
recursiveMethod(); // 무한 재귀 호출!
}
public static void main(String[] args) {
recursiveMethod(); // StackOverflowError 발생!
}
}
Exception 클래스
Exception은 프로그램에서 발생할 수 있는 일반적인 예외를 나타낸다! 예외 처리를 통해 복구할 수 있다.
🔍 Exception의 두 가지 유형
1. Checked Exception (체크 예외)
- 컴파일 타임에 반드시 예외 처리를 해야 함.
- IOException, SQLException 등이 있음.
- try-catch 블록을 사용하거나 throws 키워드를 통해 처리해야 함.
2. Unchecked Exception (언체크 예외 / 런타임 예외)
- 컴파일 타임에는 예외 처리가 강제되지 않음.
- RuntimeException을 상속하는 예외들이 포함됨.
- 예: NullPointerException, IndexOutOfBoundsException, ArithmeticException 등
✔ Checked Exception (체크 예외)
✅ 반드시 예외 처리를 해야 하는 예외
컴파일러가 예외 처리를 강제함 (try-catch 또는 throws 필요)
| 예외 클래스 | 설명 |
| IOException | 파일이나 네트워크 입출력 과정에서 발생하는 예외 |
| SQLException | 데이터베이스 작업 중 발생하는 예외 |
| ClassNotFoundException | 클래스 파일을 찾지 못할 때 발생 |
📌 Checked Exception 발생 예제
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
public class CheckedExceptionExample {
public static void main(String[] args) {
try {
File file = new File("nonexistent.txt");
FileReader reader = new FileReader(file); // IOException 발생 가능
} catch (IOException e) {
System.out.println("파일을 찾을 수 없습니다: " + e.getMessage());
}
}
}
✅ 컴파일러가 IOException을 예외 처리하도록 강제
✅ try-catch 블록을 사용하거나 throws로 예외를 위임해야 컴파일이 가능하다.
✔ Unchecked Exception (언체크 예외)
✅ 예외 처리가 강제되지 않는 예외
RuntimeException을 상속하는 예외들.
| 예외 클래스 | 설명 |
| NullPointerException | null 객체를 참조하려고 할 때 발생 |
| ArrayIndexOutOfBoundsException | 배열의 잘못된 인덱스를 참조할 때 발생 |
| ArithmeticException | 숫자 연산 오류 (/ 0 등) |
| IllegalArgumentException | 잘못된 인자를 메서드에 전달할 때 발생 |
| NegativeArraySizeException | 배열 생성 시 길이를 음수로 지정할 때 발생 |
| ArrayStoreException | 배열에 적절치 않은 인스턴스를 저장할 때 발생 |
| InputMismatchException | 사용자가 예를 들어 int형 변수에 int값이 아닌 다른 값을 입력했을 때 발생 |
📌 Unchecked Exception 발생 예제
public class UncheckedExceptionExample {
public static void main(String[] args) {
String str = null;
System.out.println(str.length()); // NullPointerException 발생!
}
}
✅ 컴파일러가 예외 처리를 강제하지 않음
✅ 실행 중(Runtime)에 예외가 발생한다.
만약 사용자 정의 예외(Custom Exception)가 필요하다면 Exception을 상속하여 만들 수 있다.
// 사용자 정의 예외 클래스 (Checked Exception)
class MyCustomException extends Exception {
public MyCustomException(String message) {
super(message);
}
}
public class CustomExceptionExample {
public static void validateAge(int age) throws MyCustomException {
if (age < 18) {
throw new MyCustomException("18세 미만은 가입할 수 없습니다.");
}
}
public static void main(String[] args) {
try {
validateAge(16);
} catch (MyCustomException e) {
System.out.println("예외 발생: " + e.getMessage());
}
}
}
✅ MyCustomException은 Exception을 상속하여 Checked Exception으로 동작
여기서, super(message)와 e.getMessage()는 정확히 뭘 의미하는 걸까?
자바의 예외 클래스는 대부분 Throwable 클래스를 상속하는데,
이 Throwable 클래스에는 예외 메시지를 저장하는 생성자가 존재한다.
public class Throwable {
private String detailMessage;
public Throwable(String message) {
detailMessage = message;
}
public String getMessage() {
return detailMessage;
}
}
✅ 즉, super(message)를 호출하면 부모 클래스(Throwable)의 message 필드에 메시지가 저장된다.
✅ 이후 getMessage() 메서드를 호출하면 저장된 메시지를 가져올 수 있다.
// 사용자 정의 예외 클래스
class MyCustomException extends Exception {
public MyCustomException(String message) {
super(message); // 부모 클래스(Exception)의 생성자 호출
}
}
public class CustomExceptionExample {
public static void main(String[] args) {
try {
throw new MyCustomException("이것은 사용자 정의 예외입니다!");
} catch (MyCustomException e) {
System.out.println("예외 발생: " + e.getMessage()); // super(message)를 통해 저장된 메시지 출력
}
}
}
예외 발생: 이것은 사용자 정의 예외입니다!
✅ super(message) 덕분에 Exception 클래스에 메시지가 전달되고,
e.getMessage()를 호출하면 부모 클래스(Throwable)의 message 필드 값을 반환한다.
super(message)뿐만 아니라, 예외의 원인(cause)을 함께 저장하는 생성자인 super(message, cause)를 사용해서
e.getCause()로 예외의 원인을 추적할 수도 있다.
class MyCustomException extends Exception {
public MyCustomException(String message, Throwable cause) {
super(message, cause); // 부모 클래스(Exception)의 생성자 호출
}
}
public class ExceptionChainingExample {
public static void main(String[] args) {
try {
try {
int result = 10 / 0; // ArithmeticException 발생
} catch (ArithmeticException e) {
throw new MyCustomException("사용자 정의 예외 발생!", e);
}
} catch (MyCustomException e) {
System.out.println("예외 메시지: " + e.getMessage());
System.out.println("원인 예외: " + e.getCause()); // ArithmeticException 확인 가능
}
}
🔍 try-with-resources 구문
try-with-resources는 try 블록에서 자동으로 리소스를 닫아주는 기능을 제공한다.
기존에는 리소스를 사용한 후 finally 블록에서 명시적으로 close()를 호출해야 했지만 try-with-resources를 사용하면 자동으로 리소스가 닫히므로 코드가 간결해지고 예외 처리가 더 안전하다.
try (리소스 선언) {
// 리소스를 사용하는 코드
} catch (예외 타입) {
// 예외 처리 코드
}
() 안에 닫아야 하는 리소스(예: 파일, DB 연결, 네트워크 스트림 등)를 선언하면
try 블록이 끝날 때 자동으로 close() 메서드가 호출되는 것이다.
📌 try-with-resources 예제
👀 기존 방식 (finally 블록 사용)
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class OldTryCatchExample {
public static void main(String[] args) {
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader("example.txt"));
System.out.println(reader.readLine()); // 파일에서 한 줄 읽기
} catch (IOException e) {
System.out.println("파일 읽기 중 오류 발생: " + e.getMessage());
} finally {
try {
if (reader != null) {
reader.close(); // 리소스 닫기
}
} catch (IOException e) {
System.out.println("파일 닫기 중 오류 발생: " + e.getMessage());
}
}
}
}
💡 문제점
- finally 블록에서 명시적으로 close()를 호출해야 한다.
- close() 호출 중에도 IOException이 발생할 수 있어 추가적인 try-catch가 필요하다.
👀 try-with-resources를 사용하면?
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class TryWithResourcesExample {
public static void main(String[] args) {
try (BufferedReader reader = new BufferedReader(new FileReader("example.txt"))) {
System.out.println(reader.readLine()); // 파일에서 한 줄 읽기
} catch (IOException e) {
System.out.println("파일 읽기 중 오류 발생: " + e.getMessage());
}
}
}
- BufferedReader 객체가 try() 안에서 선언된다.
- try 블록이 끝나면 자동으로 close()가 호출된다.
- finally 블록이 필요 없으므로 코드가 짧고 가독성이 좋아진다.
try-with-resources를 사용하려면 리소스가 반드시 AutoCloseable 인터페이스를 구현해야 하는데,
public interface AutoCloseable {
void close() throws Exception;
}
✔ BufferedReader, FileReader, Socket, Database Connection 등 많은 리소스가 이미 AutoCloseable을 구현하고 있다.
즉, close() 메서드가 있는 리소스는 try-with-resources에서 사용 가능하다.
try (
FileReader fr = new FileReader("example.txt");
BufferedReader br = new BufferedReader(fr);
)
✅ 여러 개의 리소스를 선언할 때는 ;로 구분하면 된다!
✅ try 블록이 끝날 때 br.close() → fr.close() 순서로 자동 호출된다.
여기에 추가로, finally 블록은 리소스를 닫는 용도가 아니라 추가적인 정리 작업을 할 때 사용하기 때문에 finally 블록도 추가할 수 있다.
'자바' 카테고리의 다른 글
| [자바] #06. 배열의 정렬 sort (0) | 2025.02.15 |
|---|---|
| [자바] #05. Object 클래스에 정의되어 있는 주요 메소드 (equals, clone) (0) | 2025.02.14 |
| [자바] #04. 자바에서의 메모리 구조 - 메소드 영역과 힙 영역 (0) | 2025.02.14 |
| [자바] #02. 디폴트 메소드(Default Method) (0) | 2025.02.14 |
| [자바] #01. 업캐스팅(Upcasting)과 다형성(Polymorphism) (1) | 2025.02.14 |