2023. 12. 18. 15:24ㆍ프로그래밍 언어/Java
1. 프로그램 오류와 예외 클래스
프로그램 오류는 발생시점에 따라 컴파일 에러, 런타임 에러, 논리적 에러로 나뉜다. 컴파일 에러는 컴파일 전에 인지할 수 있고 논리적 에러는 단순 실행으로는 인지하기 어렵다. 런타임 에러는 프로그래머의 역량에 따라 사전에 방지할 수 있고 따라서 런타임 에러를 집중적으로 공부할 필요가 있다.
런타임 에러는 에러와 예외로 나뉜다. 에러는 메모리 부족이나 스택오버플로우 같은 일단 발생하면 복구할 수 없는 심각한 오류이고 예외는 발생하더라도 수습할 수 있는 비교적 덜 심각한 것이다. 에러가 발생하면 프로그램은 비정상적으로 종료되지만 예외는 발생하더라도 프로그래머의 적절한 코드로 비정상적인 종료를 막을 수 있다.
예외와 오류 모두 클래스로 정의되어 있다. 모두 공통 조상인 Object를 가지며 모든 예외는 Exception의 자손이고 모든 에러는 Error의 자손이다. 그중 예외 클래스에 대해서 프로그래머가 처리할 수 있는 방법을 알아보자.
2. 예외 처리하기 - try-catch 문
try-catch문으로 예외를 처리할 수 있다.
try {
result = number / (int)(Math.random() * 10);
System.out.println(result);
} catch (ArithmeticException e){
System.out.println("0");
}
간단하게 이런 예시가 있는데, try문 안에서 예시가 발생하면 catch문에서 받아 catch문 내부 코드를 실행하게 된다. 감이 대충 잡힐텐데 예외 처리 순서를 보자.
- 발생한 예외에 해당하는 클래스의 인스턴스를 생성한다.
- 첫 번째 catch블럭부터 차례로 내려가면서 instacneof연산자를 이용해서 검사한다.
- 검사결과가 true인 catch블럭을 찾게 되면 try문을 빠져나가 catch문의 코드를 실행한다. 따라서 예제에서 예외가 발생하면 result는 출력하지 않고 바로 "0"을 출력하게 된다.
이런 식으로 예외처리를 할 수 있고, 예외가 발생했을 때 생성되는 예외 클래스의 인스턴스에는 발생한 예외에 대한 정보가 담겨있다. 이 정보를 얻을 수 있는 유용한 메서드를 2개만 알아보자.
printStackTrace() : 예외발생 당시의 콜스택에 있던 메서드의 정보와 예외 메시지를 출력한다.
getMessage() : 발생한 예외클래스의 인스턴스에 저장된 메세지(원인)을 얻을 수 있다.
실제 사용 예시를 보자.
import java.util.Scanner;
public class DivideByZeroExample {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.print("나눌 숫자를 입력하세요: ");
int dividend = scanner.nextInt();
System.out.print("나누는 숫자를 입력하세요: ");
int divisor = scanner.nextInt();
try {
// 숫자를 0으로 나누려 시도
int result = divide(dividend, divisor);
System.out.println("나눈 결과: " + result);
} catch (ArithmeticException e) {
// 예외가 발생했을 때 처리
System.out.println("0으로 나눌 수 없습니다.");
e.printStackTrace(); // 스택 트레이스 출력
System.out.println("에러 메시지: " + e.getMessage()); // 에러 메시지 출력
} finally {
// Scanner 닫기
scanner.close();
}
}
// 숫자를 나누는 메서드
private static int divide(int dividend, int divisor) {
return dividend / divisor;
}
}
이 예제에서 5/0을 입력하면 에러가 발생하고 스택과 에러 메시지를 보여준다. 실행 결과는 다음과 같다.
나눌 숫자를 입력하세요: 5
나누는 숫자를 입력하세요: 0
0으로 나눌 수 없습니다.
java.lang.ArithmeticException: / by zero
at DivideByZeroExample.divide(DivideByZeroExample.java:33)
at DivideByZeroExample.main(DivideByZeroExample.java:17)
에러 메시지: / by zero
Process finished with exit code 0
결과를 보면 에러가 발생한 시점의 콜스택 가장 위에 있는 메서드 divide가 표시되고, 이를 호출한 main 메서드도 바로 다음에 나온다. 에러 메시지에서 예외가 발생한 원인도 보여준다.
또 유용한 try-catch문의 성질은 멀티 캐치문을 허용한다는 것이다. 사용 예시로 바로 보자.
import java.util.Scanner;
public class MultiCatchExample {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
try {
System.out.print("나눌 숫자를 입력하세요: ");
int dividend = Integer.parseInt(scanner.nextLine());
System.out.print("나누는 숫자를 입력하세요: ");
int divisor = Integer.parseInt(scanner.nextLine());
int result = divide(dividend, divisor);
System.out.println("나눈 결과: " + result);
} catch (NumberFormatException | ArithmeticException e) {
// 멀티 캐치 블록에서 각 예외를 형변환하여 사용
if (e instanceof NumberFormatException) {
System.out.println("올바른 숫자 형식이 아닙니다.");
} else if (e instanceof ArithmeticException) {
System.out.println("0으로 나눌 수 없습니다.");
}
e.printStackTrace(); // 스택 트레이스 출력
System.out.println("에러 메시지: " + e.getMessage()); // 에러 메시지 출력
} finally {
// Scanner 닫기
scanner.close();
}
}
private static int divide(int dividend, int divisor) {
return dividend / divisor;
}
}
이처럼 | 기호로 catch블럭을 합칠 수 있으며 발생한 예외에 대해 instacneof연산자와 형변환을 통해 예외를 다룰 수 있다.
3. 예외 발생시키기
throw 키워드로 예외를 발생시킬 수 있다.
import java.util.Scanner;
public class ThrowExample {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
try {
System.out.print("양수를 입력하세요: ");
int number = scanner.nextInt();
if (number < 0) {
throw new IllegalArgumentException("음수는 입력할 수 없습니다.");
}
System.out.println("입력한 숫자: " + number);
} catch (IllegalArgumentException e) {
System.out.println("예외가 발생했습니다: " + e.getMessage());
e.printStackTrace(); // 스택 트레이스 출력
} finally {
scanner.close();
}
}
}
이와 같은 방식으로 예외를 발생시켜 원하지 않는 입력을 컨트롤할 수 있다.
4. finally 블럭
finally 블록은 위의 예시들에서 나왔지만 try-catch문의 끝에 선택적으로 덧붙여 사용할 수 있으며, 예외 발생에 관계없이 항상 수행되어야 하는 문장들을 넣는다. 예외가 발생한 경우는 try -> catch -> finally 순으로 실행되고 예외가 발생하지 않으면 try -> finally 순으로 실행된다.
이 외에도 예외 처리에 관한 많은 내용들이 있는데, 나머지 내용들은 프로그래밍을 하면서 필요성이 느껴질 때 찾아보고 사용하면 될 것 같다.
'프로그래밍 언어 > Java' 카테고리의 다른 글
[인텔리제이] 레전드 단축키 모음 (4) | 2024.10.10 |
---|---|
[JAVA] 객체지향 프로그래밍 - 인터페이스를 이용한 다형성 (0) | 2023.12.13 |
[JAVA] 객체지향 프로그래밍 - 추상클래스와 인터페이스 (0) | 2023.12.13 |
[JAVA] 객체지향 프로그래밍 - 다형성의 사용처와 벡터 (0) | 2023.12.06 |
[JAVA] 객체지향 프로그래밍 - 제어자 public부터 abstract까지 (1) | 2023.11.29 |