예외 처리의 종류는 다양하다.
실제 개발에선 여러 가지 예외를 동시에 처리해야 하는 경우가 많은데 여러 예제를 통해 각종 예외에 대한 코드를 연습해보겠다.
파일 읽기 및 데이터 변환
파일을 읽고 내용이 숫자라면 숫자를 처리하는 프로그램을 짜보자.
파일이 없을 경우 FileNotFoundException이 발생하고 파일 내용이 숫자가 아닐 경우엔 NumberFormatException이 발생할 것이다.
import java.io.*;
import java.util.*;
public class MultipleExceptionHandlingExample {
public static void main(String[] args) {
File file = new File("numbers.txt");
try {
// 파일이 존재하지 않으면 예외 발생
FileReader fr = new FileReader(file);
//FileReader를 감싸서 성능을 개선하고 파일을 한 줄씩 읽을 수 있게 함
BufferedReader br = new BufferedReader(fr);
String line;
List<Integer> numbers = new ArrayList<>();
while ((line = br.readLine()) != null) {
try {
// 숫자로 변환 불가하면 예외 발생
int num = Integer.parseInt(line);
numbers.add(num);
} catch (NumberFormatException e) {
System.out.println("잘못된 숫자 형식: " + line);
}
}
br.close();
System.out.println("파일에서 읽은 숫자: " + numbers);
} catch (FileNotFoundException e) {
System.out.println("파일을 찾을 수 없습니다: " + file.getPath()); //getPath()는 파일 경로 반환
} catch (IOException e) {
System.out.println("파일을 읽는 중 오류가 발생했습니다.");
} finally {
System.out.println("파일 처리 종료.");
}
}
}
numbers.txt 파일에서 각 줄을 읽고, 각 줄이 숫자일 경우 이를 리스트에 추가, 숫자가 아닌 형식이 있을 경우 예외 처리를 한다.
사용자 입력과 배열 접근 예외 처리
사용자 입력을 받아서 배열에 인덱스로 접근하고 그 과정에서 발생할 수 있는 InputMismatchException과 ArrayIndexOutOfBoundsException 예외를 처리한다.
import java.util.*;
public class UserInputExceptionExample {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int[] array = {1, 2, 3, 4, 5};
try {
System.out.print("배열의 인덱스를 입력하세요 (0-4): ");
int index = scanner.nextInt(); // 잘못된 타입 입력 시 예외 발생
System.out.println("배열의 값: " + array[index]); // 잘못된 인덱스 입력 시 예외 발생
} catch (InputMismatchException e) {
System.out.println("잘못된 입력입니다. 숫자를 입력하세요.");
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("잘못된 배열 인덱스입니다. 범위는 0~4입니다.");
} finally {
scanner.close();
System.out.println("입력 종료.");
}
}
}
예외 처리를 통해 사용자가 잘못된 입력을 했을 때 상황에 맞는 메시지를 출력한 후 finally 블록을 사용하여 리소스 해제를 한다.
다중 예외 처리
사용자가 올바른 숫자를 입력할 때까지 재시도를 요구하는 프로그램이다.
import java.util.*;
public class RetryInputExceptionExample {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
while (true) {
try {
System.out.print("정수를 입력하세요: ");
String input = scanner.nextLine();
int number = Integer.parseInt(input); // 잘못된 숫자 형식 예외 발생
if (number < 0) {
throw new IllegalArgumentException("음수는 입력할 수 없습니다.");
}
System.out.println("입력한 숫자: " + number);
break; // 올바른 숫자가 입력되면 반복문 종료
} catch (NumberFormatException e) {
System.out.println("숫자 형식이 잘못되었습니다. 정수를 입력해주세요.");
} catch (IllegalArgumentException e) {
System.out.println(e.getMessage());
} catch (Exception e) {
System.out.println("예상치 못한 오류가 발생했습니다: " + e.getMessage());
}
}
scanner.close();
System.out.println("입력 종료.");
}
}
사용자가 숫자가 아닌 값을 입력하면 NumberFormatException 예외가 발생하고
음수를 입력하면 IllegalArgumentException 예외가 발생한다.
NumberFormatException 발생 시 e.getMessage()를 통해 "For input string: "abc"" 이런 식으로 설정되고
IllegalArgumentException 발생 시 e.getMessage()를 통해 위에 throw에서 지정했던 "음수는 입력할 수 없습니다"가 출력된다.
복합 예외 처리
사용자 정의 예외를 던지고 예외 체이닝을 통해 예외의 원인을 추적할 수 있다.
throw로 예외를 던지며 예외 객체에 다른 예외를 감싸서 전달할 수 있다.
class InvalidAgeException extends Exception {
public InvalidAgeException(String message) {
super(message);
}
}
public class ExceptionChainingExample {
public static void main(String[] args) {
try {
checkAge(-5);
} catch (InvalidAgeException e) {
System.out.println("입력된 나이가 유효하지 않습니다: " + e.getMessage());
e.printStackTrace(); // 예외 체이닝을 통해 더 자세한 정보 제공
}
}
public static void checkAge(int age) throws InvalidAgeException {
if (age < 0) {
throw new InvalidAgeException("나이는 음수일 수 없습니다.");
}
System.out.println("나이: " + age);
}
}
InvalidAgeException을 통해 Exception 클래스를 상속하여 새로운 예외 유형을 만든다.
여기선 나이가 잘못 입력되었을 때 사용한다.
ExceptionChainingExample을 보면 checkAge(-5)를 호출하고 있다.
여기서 -5는 잘못된 나이 값이다.
checkAge 메서드가 호출되면서 age가 음수일 경우 InvalidAgeException이 발생한다.
나이가 음수일 경우 InvalidAgeException을 throw하여 예외를 발생시킨다.
e.printStackTrace()는 예외가 발생한 위치를 포함한 스택 트레이스를 출력하여 디버깅에 유용한 정보를 제공한다.
이번에 공부하면서 예외 체이닝이라는 개념을 제대로 공부했다.
예외 체이닝은 예외가 발생했을 때 원인 예외를 다른 예외에 포함시켜서 더 많은 정보를 제공하는 방법이다.
여기서 명시적으로 나타내진 않았지만 예외를 던질 때 원인 예외를 포함시킬 경우 더 자세한 예외 정보를 얻을 수 있다.
예를 들어서 예외를 다시 던지는 경우
public static void checkAge(int age) throws InvalidAgeException {
try {
if (age < 0) {
throw new InvalidAgeException("나이는 음수일 수 없습니다.");
}
} catch (InvalidAgeException e) {
throw new InvalidAgeException("나이 검사 중 오류가 발생했습니다.", e);
}
}
이렇게 원인 예외를 포함시킬 수 있다.
두 번째 InvalidAgeException 예외는 첫 번째 예외를 체이닝해서 던지므로 원인 예외도 함께 처리할 수 있다.
e.getCause()를 통해서 원인 예외에 접근할 수 있다.
'Language > Java' 카테고리의 다른 글
[Java] 빌더 패턴과 스프링에서의 활용 (1) | 2025.01.08 |
---|---|
[Java] toString을 왜 쓸까? (0) | 2025.01.07 |
[Java] 다형성의 본질과 활용 (0) | 2024.12.31 |
[Java] String / StringBuffer / StringBuilder 차이점 (0) | 2024.10.04 |
[Java] 인터페이스 (48) | 2024.06.22 |