10장. 예외 처리
예외(Exception) 개념
예외란?
예외(Exception)는 프로그램 실행 중 발생하는 오류나 비정상적인 상황입니다. 예외가 발생하면 프로그램이 중단될 수 있으므로, 적절한 처리가 필요합니다.
예외의 종류
1. 컴파일 오류 (Compile Error)
- 문법 오류로 인해 컴파일 시점에 발견되는 오류
- 예: 세미콜론 누락, 괄호 불일치
2. 런타임 오류 (Runtime Error)
- 프로그램 실행 중 발생하는 오류
- 예: 배열 인덱스 초과, null 참조
3. 논리 오류 (Logic Error)
- 프로그램은 실행되지만 의도한 대로 동작하지 않는 오류
- 예: 잘못된 계산식
예외 클래스 계층 구조
Throwable
├── Error (시스템 오류, 복구 불가능)
└── Exception (예외, 처리 가능)
├── RuntimeException (실행 시점 예외)
│ ├── NullPointerException
│ ├── ArrayIndexOutOfBoundsException
│ ├── ArithmeticException
│ └── ClassCastException
└── Checked Exception (컴파일 시점 예외)
├── IOException
├── SQLException
└── ClassNotFoundException주요 예외 종류
| 예외 | 발생 상황 | 설명 |
|---|---|---|
NullPointerException |
null 참조 | null 객체의 메서드나 필드에 접근 |
ArrayIndexOutOfBoundsException |
배열 인덱스 초과 | 배열의 범위를 벗어난 인덱스 접근 |
ArithmeticException |
산술 연산 오류 | 0으로 나누기 등 |
ClassCastException |
타입 변환 오류 | 잘못된 타입 캐스팅 |
NumberFormatException |
숫자 형식 오류 | 문자열을 숫자로 변환 실패 |
IllegalArgumentException |
잘못된 인자 | 메서드에 잘못된 인자 전달 |
예외 처리의 필요성
// 예외 처리가 없으면 프로그램이 중단됨
int[] numbers = {1, 2, 3};
System.out.println(numbers[5]); // ArrayIndexOutOfBoundsException 발생!
// 이후 코드는 실행되지 않음
try-catch-finally
try-catch 문
예외를 처리하기 위한 기본 구조입니다.
기본 형식
try {
// 예외가 발생할 수 있는 코드
} catch (예외타입 변수명) {
// 예외 처리 코드
}
예제
try {
int[] numbers = {1, 2, 3};
System.out.println(numbers[5]); // 예외 발생 가능
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("배열 인덱스가 범위를 벗어났습니다.");
}
여러 catch 블록
여러 종류의 예외를 각각 처리할 수 있습니다.
try {
// 예외가 발생할 수 있는 코드
} catch (ArrayIndexOutOfBoundsException e) {
// 배열 인덱스 예외 처리
} catch (NullPointerException e) {
// null 참조 예외 처리
} catch (Exception e) {
// 기타 모든 예외 처리 (가장 마지막에)
}
catch 블록 순서
자식 예외를 부모 예외보다 먼저 처리해야 합니다.
try {
// 코드
} catch (ArrayIndexOutOfBoundsException e) {
// 자식 예외 (먼저)
} catch (RuntimeException e) {
// 부모 예외 (나중에)
} catch (Exception e) {
// 최상위 예외 (가장 마지막)
}
finally 블록
finally 블록은 예외 발생 여부와 관계없이 항상 실행됩니다.
try {
// 예외가 발생할 수 있는 코드
} catch (Exception e) {
// 예외 처리
} finally {
// 항상 실행되는 코드 (리소스 정리 등)
}
finally 사용 예제
try {
int result = 10 / 0;
} catch (ArithmeticException e) {
System.out.println("0으로 나눌 수 없습니다.");
} finally {
System.out.println("finally 블록은 항상 실행됩니다.");
}
try-with-resources (Java 7+)
리소스를 자동으로 닫아주는 문법입니다.
// 기존 방식
FileReader fr = null;
try {
fr = new FileReader("file.txt");
// 파일 읽기
} catch (IOException e) {
// 예외 처리
} finally {
if (fr != null) {
try {
fr.close();
} catch (IOException e) {
// 예외 처리
}
}
}
// try-with-resources (간단)
try (FileReader fr = new FileReader("file.txt")) {
// 파일 읽기
} catch (IOException e) {
// 예외 처리
}
// 자동으로 close() 호출됨
예외 발생(throw)
throw 키워드
프로그래머가 직접 예외를 발생시킬 수 있습니다.
기본 형식
throw new 예외타입("예외 메시지");
예제
public int divide(int a, int b) {
if (b == 0) {
throw new ArithmeticException("0으로 나눌 수 없습니다.");
}
return a / b;
}
throws 키워드
메서드에서 발생할 수 있는 예외를 선언합니다.
기본 형식
반환타입 메서드명() throws 예외타입1, 예외타입2 {
// 메서드 본문
}
예제
public void readFile(String filename) throws IOException {
FileReader fr = new FileReader(filename);
// 파일 읽기
fr.close();
}
// 호출하는 쪽에서 예외 처리
try {
readFile("data.txt");
} catch (IOException e) {
System.out.println("파일 읽기 오류: " + e.getMessage());
}
throw vs throws
| 구분 | throw | throws |
|---|---|---|
| 용도 | 예외를 발생시킴 | 예외를 선언함 |
| 위치 | 메서드 내부 | 메서드 선언부 |
| 개수 | 여러 번 사용 가능 | 여러 예외 선언 가능 |
사용자 정의 예외
사용자 정의 예외란?
프로그램의 특정 상황에 맞는 예외 클래스를 직접 만드는 것입니다.
사용자 정의 예외 생성
1. Exception을 상속받는 예외 클래스
public class MyException extends Exception {
public MyException(String message) {
super(message);
}
}
2. RuntimeException을 상속받는 예외 클래스
public class MyRuntimeException extends RuntimeException {
public MyRuntimeException(String message) {
super(message);
}
}
사용자 정의 예외 예제
// 사용자 정의 예외 클래스
public class InsufficientBalanceException extends Exception {
private double balance;
private double amount;
public InsufficientBalanceException(double balance, double amount) {
super("잔액 부족: 현재 잔액 " + balance + "원, 요청 금액 " + amount + "원");
this.balance = balance;
this.amount = amount;
}
public double getBalance() {
return balance;
}
public double getAmount() {
return amount;
}
}
// 사용 예제
public class BankAccount {
private double balance;
public void withdraw(double amount) throws InsufficientBalanceException {
if (amount > balance) {
throw new InsufficientBalanceException(balance, amount);
}
balance -= amount;
}
}
Checked Exception vs Unchecked Exception
Checked Exception
- 컴파일 시점에 반드시 처리해야 하는 예외
Exception을 상속 (RuntimeException 제외)try-catch또는throws필수
public class CheckedException extends Exception {
public CheckedException(String message) {
super(message);
}
}
Unchecked Exception
- 컴파일 시점에 처리하지 않아도 되는 예외
RuntimeException을 상속try-catch또는throws선택
public class UncheckedException extends RuntimeException {
public UncheckedException(String message) {
super(message);
}
}
예외 처리 설계 방법
예외 처리 원칙
1. 구체적인 예외 처리
// 나쁜 예: 모든 예외를 하나로 처리
try {
// 코드
} catch (Exception e) {
// 모든 예외를 같은 방식으로 처리
}
// 좋은 예: 구체적인 예외 처리
try {
// 코드
} catch (NullPointerException e) {
// null 예외 처리
} catch (ArrayIndexOutOfBoundsException e) {
// 배열 인덱스 예외 처리
} catch (Exception e) {
// 기타 예외 처리
}
2. 적절한 예외 메시지
// 나쁜 예
throw new Exception("오류");
// 좋은 예
throw new IllegalArgumentException("나이는 0 이상이어야 합니다. 입력값: " + age);
3. 예외를 숨기지 않기
// 나쁜 예: 예외를 무시
try {
// 코드
} catch (Exception e) {
// 아무것도 하지 않음
}
// 좋은 예: 예외를 로깅하거나 처리
try {
// 코드
} catch (Exception e) {
System.err.println("예외 발생: " + e.getMessage());
e.printStackTrace();
}
4. 리소스 정리
// finally 블록으로 리소스 정리
FileReader fr = null;
try {
fr = new FileReader("file.txt");
// 파일 읽기
} catch (IOException e) {
// 예외 처리
} finally {
if (fr != null) {
try {
fr.close();
} catch (IOException e) {
// 예외 처리
}
}
}
예외 처리 패턴
1. 예외 전파
public void method1() throws IOException {
method2(); // 예외를 호출자에게 전파
}
public void method2() throws IOException {
// 예외 발생
throw new IOException("파일 오류");
}
2. 예외 변환
try {
// 코드
} catch (SQLException e) {
// 다른 예외로 변환
throw new DataAccessException("데이터 접근 오류", e);
}
3. 예외 복구
int retryCount = 0;
while (retryCount < 3) {
try {
// 작업 수행
break; // 성공 시 루프 종료
} catch (Exception e) {
retryCount++;
if (retryCount >= 3) {
throw e; // 재시도 실패 시 예외 발생
}
}
}
예외 처리 실습 예제
예제 1: 기본 예외 처리
public class ExceptionExample {
public static void main(String[] args) {
try {
int[] numbers = {1, 2, 3};
System.out.println(numbers[5]);
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("배열 인덱스 오류: " + e.getMessage());
}
}
}
예제 2: 여러 예외 처리
public class MultipleExceptionExample {
public static void main(String[] args) {
try {
String str = null;
int length = str.length();
int result = 10 / 0;
} catch (NullPointerException e) {
System.out.println("null 참조 오류");
} catch (ArithmeticException e) {
System.out.println("산술 연산 오류");
} catch (Exception e) {
System.out.println("기타 오류: " + e.getMessage());
}
}
}
예제 3: 사용자 정의 예외
public class CustomExceptionExample {
public static void main(String[] args) {
try {
validateAge(-5);
} catch (InvalidAgeException e) {
System.out.println("오류: " + e.getMessage());
}
}
public static void validateAge(int age) throws InvalidAgeException {
if (age < 0 || age > 150) {
throw new InvalidAgeException("유효하지 않은 나이: " + age);
}
}
}
class InvalidAgeException extends Exception {
public InvalidAgeException(String message) {
super(message);
}
}
연습 문제
기본 예외 처리
- 배열 인덱스 예외를 처리하는 프로그램을 작성하세요.
여러 예외 처리
- NullPointerException과 ArithmeticException을 각각 처리하는 프로그램을 작성하세요.
사용자 정의 예외
- 점수가 0~100 범위를 벗어나면 예외를 발생시키는 프로그램을 작성하세요.
예외 전파
- 메서드에서 예외를 발생시키고, 호출하는 쪽에서 처리하는 프로그램을 작성하세요.
finally 블록
- try-catch-finally를 사용하여 리소스를 정리하는 프로그램을 작성하세요.
종합 예제
- 은행 계좌에서 잔액 부족 예외를 처리하는 프로그램을 작성하세요.
다음 장 예고
다음 장에서는 파일 입출력(File I/O)을 통해 데이터를 파일에 저장하고 읽는 방법을 학습합니다.
'BackEnd > Java' 카테고리의 다른 글
| 12장. 메모리 구조 & JVM 이해 (0) | 2026.01.03 |
|---|---|
| 11장. 입출력(IO) & 파일 처리 (0) | 2026.01.02 |
| 9장. 컬렉션 프레임워크 (0) | 2026.01.02 |
| # 8장. 객체지향 핵심 개념 (0) | 2026.01.01 |
| 7장. 객체지향 프로그래밍(OOP) 기초 (0) | 2026.01.01 |