Error와 Exception의 차이
Error와 Exception은 프로그램의 중단을 발생시킬 수 있는 오류이다. Error와 Exception 모두 오류이긴 하지만 차이점이 존재한다.
Error: 코드로 대처나 수습이 불가능한 심각한 오류(StackOverflow Error, OutOfMemoryError)
Exception: 코드로 대처나 수습이 가능한 비교적 가벼운 오류
대처가 가능한가와 오류의 심각성에 따라 두 오류를 구분한다.
1. Error
- Error는 예측이 불가능하기 때문에 대처 또한 불가능하다.
- JVM의 프로그램 실행에 문제가 생기기 때문에 프로그램 자체가 종료되어 버린다.
2. Exception
- Exception은 주로 개발자의 설계 로직에 문제가 있어서 발생한다.
- 발생해도 프로그램이 강제 종료될 확률은 비교적 낮고 수습이 가능하기 때문에 가벼운 오류로 분류되는 것이다.
- 대응 코드를 미리 작성하는 Exception Handling을 통해 대응한다. 자바의 경우 try-catch 코드 작성으로 Exception Handling을 한다.
Error
에러의 종류에는 3가지가 있다.
1. 컴파일 Error: 컴파일 시 발생하는 에러
- 컴파일 시 에러가 발생하여 컴파일러가 에러를 출력한다.
2. 런타임 Error: 프로그램 실행 시 발생하는 에러(실행 에러)
- 컴파일 시에는 문제가 없지만 실행 시에 발생하는 에러이다.
- 외부적 요인, 기계 결함으로 프로그램이 비정상적으로 종료된다.
- 외부 요인, 기계 결함의 원인도 있지만 대부분 설계 미숙으로 발생하기 때문에 사전에 런타임 에러에 대해 철저하게 대응하고 코드를 검토하는 것이 중요하다.
3. 논리 Error: 동작 및 실행에는 문제가 없는 에러
- 프로그램이 의도한 바와 다르게 동작하거나 실행 후 원치 않은 결과를 출력한다.
- 컴파일 에러, 런타임 에러와 다르게 프로그램이 에러가 발생한 것을 알려주지 않기 때문에 개발 시 알고리즘과 로직을 면밀히 검토하는 것이 바람직하다.
Exception
자바에서 오류는 Exception과 Error 클래스로 나뉘고 두 클래스들은 Throwable 클래스를 상속받는다.(Throwable 클래스는 Object 클래스를 상속받는다)
JVM에서 프로그램 실행 중에 예외가 발생하면 Exception 클래스를 객체로 생성하고 예외처리 코드에서 생성한 Exception 객체를 이용한다. 아래의 그림처럼 Error와 Exception 모두 Throwable 객체를 상속받는다.
예외 발생 시, 예외 메시지를 알려주는 getMessage()와 printStackTrace() 메서드 모두 Throwable 클래스의 메서드이기 때문에 Throwable을 상속받는 Exception 클래스에서 호출 가능한 것이다.
Error 클래스의 경우 ~Error와 같이 발생 오류 뒤에 Error가 붙고 Exception은 뒤에 Exception이 붙는다.
Exception의 종류
Exception은 컴파일 Exception, 런타임 Exception 두 가지 종류로 나뉜다.
CompileException 클래스를 상속하는 클래스를 Checked Exception, RuntimeException을 상속하는 클래스를 Unchecked Exception이라고 부른다.
1. 컴파일 Exception(Checked Exception)
- 사용자의 실수, 외적 요인에 의해 발생한다.
- 컴파일 시 체크하여 발생한 Exception이다. 컴파일 단계에서 발생한다.
- 런타임 Exception을 제외한 모든 Exception이 포함된다.
- 이름이 Checked Exception인 만큼, 반드시 예외처리를 해 줘야 한다. 예외처리는 try-catch 또는 throws Exception으로 한다.
2. 런타임 Exception(Unchecked Exception)
- 컴파일 시 체크가 불가능하다. 실행(런타임) 단계에서 발생한다.
- 이름이 Unchecked Exception인 만큼, 명시적 예외처리를 해주지 않아도 된다.
런타임(Unchecked) Exception Class 종류
아래는 자주 발생하는 몇 가지 런타임(Unchecked) Exception들이다.
Exception들의 이름을 잘 읽어보면 대체로 어떤 상황에서 발생하는 예외인지 유추할 수 있다.
개인적으로 생소한 Exception은 볼드체 처리했습니다.
예외 타입 | 설명 |
ArithmeticException | 수를 0으로 나누는 것과 같이 비정상적인 연산 발생 |
NullPointerException | NULL 객체 참조시 발생 |
IllegalArgumentException | 메서드의 전달 인자 값이 잘못된 경우 발생 |
IllegalStateException | 객체의 상태가 메서드 호출에 부적합한 경우 발생 |
IndexOutOfBoundsException | 인덱스 값이 주어진 범위를 넘어갈 경우에 발생 |
UnsupportedOperationException | 해당 객체가 메서드를 지원하지 않는 경우 발생 |
SecurityException | 보안 위반 발생 시 보안 관리 프로그램에서 발생 |
ProviderException | 구성 공급자 오류시 발생 |
NoSuchElementException | 더 이상의 구성요소가 없는 경우 발생 |
ArrayStoreException | 객체 배열에 잘못된 객체 유형의 데이터 저장 시 발생 |
ClassCastException | 클래스 간 형 변환 오류시 발생 |
EmptyStackException | 비어있는 스택에서 요소를 제거하려고 할 때 발생 |
1. ArithmeticException
- 수를 0으로 나누는 것과 같이 비정상적인 연산시 발생
public class ExceptionTest {
public static void main(String[] args) {
int a = 5;
System.out.println(a / 0);
}
}
2. NullPointerException
- 참조하는 변수가 Null일 때 발생
public class ExceptionTest {
public static void main(String[] args) {
Object nullObject = null;
System.out.println(nullObject.toString());
}
}
3. IllegalArgumentException
- 메서드에 인자로 넘어간 변수가 올바르지 않을 때 발생
- 예로 Person 객체의 나이를 설정해주는 setAge 메서드의 경우, 나이는 음수가 될 수 없으므로 음수를 인자로 받으면 Exception 발생
public class ExceptionTest {
public static void main(String[] args) {
Person p = new Person();
p.setAge(-1);
System.out.println(p.age);
}
static class Person {
int age;
void setAge(int age) {
if(age < 0) {
throw new IllegalArgumentException("나이는 음수일 수 없습니다.");
} else {
this.age = age;
}
}
}
}
4. IllegalStateException
- 객체의 상태가 메서드를 호출하기에 적합하지 않으면 발생
- 스레드를 다룰 때, java.util의 Collections를 다룰 때 발생하기도 한다.
public class ExceptionTest {
public static void main(String[] args) {
Thread t = new Thread();
t.start();
t.start(); // 이미 start된 thread를 다시 start
}
}
5. IndexOutOfBoundsException
- 인덱스 값이 주어진 범위를 벗어날 경우 발생
import java.util.ArrayList;
public class ExceptionTest {
public static void main(String[] args) {
ArrayList<Integer> arrayList = new ArrayList<>();
arrayList.get(100);
}
}
6. UnsupportedOperationException
- 해당 객체가 호출한 메서드를 지원하지 않을 때 발생
- asList()로 인해 고정된 길이의 배열을 생성했기 때문에 add메서드 호출 시 예외가 발생
import java.util.*;
public class ExceptionTest {
public static void main(String[] args) {
String[] array = {"a", "b", "c"};
// Returns a fixed-size list backed by the specified array.
List<String> list = Arrays.asList(array);
list.add("d");
}
}
7. Security Exception
- 보안 규칙을 어기거나 보안 위반이 있을 경우 발생
아래와 같이 시스템의 설정을 변경하는 코드는 보안정책에 따라 SecurityException을 발생시킬 수 있다.
public class ExceptionTest {
public static void main(String[] args) {
// 인자로 받는 값에 해당 하는 시스템 설정을 변경함
System.setProperty("java.version", "11");
}
}
아래의 코드는 시스템의 루트 디렉토리를 읽어오는 코드인데, 시스템의 권한에 따라 해당 코드 또한 SecurityException을 발생시킬 수 있다.
import java.io.File;
public class ExceptionTest {
public static void main(String[] args) {
try {
// 시스템 파일 시스템의 루트 디렉토리를 읽어오려고 시도
File rootDir = new File("/");
String[] files = rootDir.list();
System.out.println("루트 디렉토리의 파일 리스트: ");
for (String file : files) {
System.out.println(file);
}
} catch (SecurityException e) {
// 보안 예외 처리
System.err.println("SecurityException 발생: 파일 시스템에 접근할 수 없습니다.");
e.printStackTrace();
}
}
}
8. ProviderException
- 구성 공급자 오류 시 발생. 보안 관련 작업 시 발생하는 오류를 나타낸다.
- ProviderException은 아래와 같은 상황에서 발생할 수 있다.
GPT 답변:
1) 보안 제공자 오류
- 보안 서비스를 제공(암호화, 해시, 난수 생성 등)하는 모듈인 보안 제공자 모듈에서 올바르게 구성되지 않은 보안 제공자가 사용될 때 발생
- 보안 제공자는 Security 클래스를 통해 등록된다.
import javax.crypto.*;
import java.security.*;
public class ExceptionTest {
public static void main(String[] args) {
try {
// 잘못된 보안 제공자로 암호화 서비스를 요청
Security.addProvider(new BogusProvider());
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding", "BogusProvider");
} catch (NoSuchProviderException | NoSuchAlgorithmException | NoSuchPaddingException e) {
e.printStackTrace();
}
}
// 임의의 보안 제공자 클래스를 정의(실제로 보안 제공자의 역할을 하지 않음)
static class BogusProvider extends Provider {
public BogusProvider() {
super("Bogus", 1.0, "Bogus security provider");
put("Cipher.AES", "BogusCipher");
}
}
}
2) 알고리즘 지원 오류
- 특정 암호화 알고리즘이나 보안 기능을 지원하지 않는 경우 발생
- 시스템에 필요한 암호화 알고리즘이 없거나 보안 서비스가 제대로 설치되지 않은 경우 발생
import javax.crypto.*;
import java.security.*;
public class ExceptionTest {
public static void main(String[] args) {
try {
// 시스템에 존재하지 않는 암호화 알고리즘 요청(A라는 Provider는 존재하지 않는다.)
Cipher cipher = Cipher.getInstance("DES", "A");
// Cipher cipher = Cipher.getInstance("DES", "SunJCE");
} catch (NoSuchAlgorithmException | NoSuchProviderException | NoSuchPaddingException e) {
e.printStackTrace();
}
}
}
3) 클래스 로드 오류
- 보안 제공자 클래스가 로드되지 않거나 인스턴스화될 수 없는 경우에 발생(잘못된 클래스 이름, 존재하지 않는 클래스 파일 등의 이유)
import javax.crypto.*;
import java.security.*;
public class ExceptionTest {
public static void main(String[] args) {
try {
// 잘못된 보안 제공자로 서비스를 요청
Security.addProvider(new BogusProvider());
// 존재하지 않는 암호화 알고리즘 요청
Cipher cipher = Cipher.getInstance("NonexistentAlgorithm", "BogusProvider");
} catch (NoSuchAlgorithmException | NoSuchProviderException | NoSuchPaddingException e) {
e.printStackTrace();
}
}
// 임의의 보안 제공자 클래스(실제로는 보안 제공자의 역할을 하지 않음)
static class BogusProvider extends Provider {
public BogusProvider() {
super("Bogus", 1.0, "Bogus security provider");
put("Cipher.NonexistentAlgorithm", "NonexistentCipher");
}
}
}
9. NoSuchElementException
- 더 이상 구성요소가 없는 경우에 발생
- "0 0 0 0" 입력을 StringTokenizer로 받고 nextToken() 메서드를 다섯 번 호출할 경우, 데이터가 4개밖에 존재하지 않기 때문에 예외가 발생한다.
import java.io.*;
import java.util.*;
public class ExceptionTest {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
// input: "0 0 0 0"
for(int i = 0; i < 5; i++) {
System.out.println(st.nextToken());
}
}
}
10. ArrayStoreException
- 배열에 잘못된(다른) 타입의 객체 저장 시도를 할 때 발생
- 아래와 같은 String 배열에 int타입의 데이터를 저장하려고 하면 예외 발생
public class ExceptionTest {
public static void main(String[] args) {
Object[] array = new String[3];
array[0] = "1";
array[1] = 2;
}
}
11. ClassCastException
- 클래스 간 형변환 오류시 발생
- Mammal과 Reptile간 캐스팅이 불가능하기 때문에 예외가 발생한다.
public class ExceptionTest {
public static void main(String[] args) {
Mammal mammal = new Dog();
Reptile reptile = (Reptile) mammal;
}
interface Reptile {
void controlTemperature();
}
interface Mammal {
void feed();
}
static class Dog implements Mammal {
@Override
public void feed() {}
void bark() {}
void lick() {}
}
}
12. EmptyStackException
- 비어있는 스택에서 요소를 제거하려고 할 때
- 비어있는 스택을 생성 후 pop() 메서드 호출 시 예외 발생
import java.util.*;
public class ExceptionTest {
public static void main(String[] args) {
Stack<Integer> stack = new Stack<>();
stack.pop();
}
}
컴파일(Checked) Exception Class 종류
예외 타입 | 설명 |
IOException | 입출력 시 발생 |
FileNotFoundException | 접근하려는 파일이 존재하지 않을 때 발생 |
1. IOException
- 프로그램 동작 중 언제 입출력이 들어오고 나가는지 예측하는 것은 어렵기 때문에 입출력 예외는 엄격하고 까다롭게 관리된다.
- BufferedReader 같은 입출력 클래스를 사용할 때 IOException 예외 처리를 해주지 않으면 컴파일 예외가 발생한다.
import java.io.*;
public class ExceptionTest {
public static void main(String[] args) {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
br.readLine();
}
}
따라서 아래와 같이 throws IOException을 추가하여 예외 핸들링을 한다.
import java.io.*;
public class ExceptionTest {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
br.readLine();
}
}
2. FileNotFoundException
- 접근하려는 파일이 존재하지 않으면 예외가 발생한다.
import java.io.*;
public class ExceptionTest {
public static void main(String[] args) {
BufferedReader br = new BufferedReader(new FileReader("file.txt"));
}
}
따라서 아래와 같이 throws FileNotFoundException을 추가하여 예외 핸들링을 한다.
import java.io.*;
public class ExceptionTest {
public static void main(String[] args) throws FileNotFoundException {
BufferedReader br = new BufferedReader(new FileReader("file.txt"));
}
}
References
https://gyoogle.dev/blog/computer-language/Java/Error%20&%20Exception.html
'Java' 카테고리의 다른 글
[Java] Record(레코드) (0) | 2024.04.06 |
---|---|
[Java] Casting(캐스팅) (0) | 2024.04.05 |
[Java] 문자열 클래스 (0) | 2024.02.03 |
[Java] 직렬화(Serialization) (0) | 2024.01.31 |
[Java] 오토박싱(Autoboxing) & 오토 언박싱(Autounboxing) (0) | 2024.01.25 |