일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- greedy
- Counting
- database
- geometry
- Number Theory
- Stack
- 자바
- 파이썬
- Tree
- Class
- sorting
- Method
- Math
- Binary Tree
- Binary Search
- implement
- Data Structure
- hash table
- 코딩테스트
- array
- java
- dynamic programming
- Matrix
- string
- two pointers
- 구현
- 코테
- bit manipulation
- SQL
- simulation
- Today
- Total
코린이의 소소한 공부노트
프로그램 오류의 종류와 예외 처리 본문
코딩을 하다 보면 오류를 가끔(아니고 매번) 마주치게 된다.
오류의 종류는 크게 3가지로 나눌 수 있다.
1. 컴파일 에러(compile-time error)
// 구문체크 - 문법에 맞는지 확인
system.out.println(); // 맨 앞 s가 소문자
// Exception in thread "main"
// java.lang.Error: Unresolved compilation problem:
// system cannot be resolved
System.out.println(); // OK
// 최적화
int i = 3+5; // 우리가 이렇게 써놓으면
// int i = 8; // 컴파일러가 자동으로 8로 계산해 i에 대입해줌
// 생략된 코드 추가 예시 - 기본 생성자 추가
class Test{
// 아무런 생성자를 쓰지 않으면
// 컴파일 단계에서 자동으로 아래와 같은 생성자를 추가해준다.
Test() {}
}
- (프로그램 실행 전) 컴파일을 할 때 발생하는 에러
- 보통 자바 컴파일러(javac.exe)가 파일 저장 시 자동으로 코드를 체크한다.
- 자바 컴파일러는 구문 체크, 번역, 최적화, 생략된 코드 추가 등의 일을 해주는데, 이 과정에서 컴파일 에러가 발생
2. 런타임 에러(runtime error)
int[] num = {1,2,3,4,5}; // 길이가 5 -> 인덱스는 0~4
System.out.println(num[5]); // 인덱스가 5?
// Exception in thread "main"
// java.lang.ArrayIndexOutOfBoundsException:
// Index 5 out of bounds for length 5
- JVM(java virtual machine)이 파일을 실행할 때 발생하는 에러 -> 프로그램이 종료됨
- 컴파일 단계에서는 문제가 없었으나, 실제 실행 시 여러 이유로 런타임 에러가 발생할 수 있다.
- 위 코드의 경우 num[]의 인덱스는 4까지인데 5번에 접근하려고 해서 런타임 에러가 발생한 것이다.
자바에서는 런타임 에러를 2가지로 나눈다.
1) 에러(error): 프로그램 코드로 수습할 수 없는 심각한 오류
- 메모리 부족처럼 프로그램이 더 이상 진행될 수 없는 경우
2) 예외(exception): 프로그램 코드로 수습할 수 있는 다소 미약한 오류
- 예외가 발생했을 때 수습할 수 있는 코드를 작성해 그것을 처리할 수 있다.
3. 논리적 에러(logical error)
// 배열의 요소를 뒤에서부터 출력하기
int[] num = {1,2,3,4,5};
for(int i=0 ; i<num.length ; i++)
System.out.print(num[i]); // 12345
// 아무런 에러가 나지 않았지만, 결과는 앞에서부터 출력된 상황
// 다음과 같이 바꿔야 내 의도대로 결과가 나온다.
for(int i=num.length-1 ; i>=0 ; i--)
System.out.print(num[i]); // 54321
- 작성한 의도와 다르게 동작하는 경우 -> 프로그램이 종료되지는 않음
- 코드를 수정해서 의도에 맞게 동작하도록 해야 한다.
컴파일 에러, 논리적 에러는 코드 수정을 통해 어떻게 할 수 있지만, 런타임 에러는 어떻게 할 수 없다. 하지만 예외는 처리가 가능하다. 예외 처리란
1. 프로그램 실행 시 발생할 수 있는 예외의 발생에 대비한 코드를 작성하는 것을 말한다.
2. 예외 처리를 통해 프로그램의 비정상 종료를 막고, 정상적인 실행상태를 유지할 수 있다.
예외 클래스의 계층구조를 대략적으로 살펴보면 아래 그림과 같다.
1) 예외 클래스의 최고 조상은 Object 클래스이다.
2) Throwable 클래스는 모든 오류의 조상 클래스이다.
3) Error 클래스는 모든 에러(심각한 에러)의 조상이다.
- OutOfMemoryError: 프로그램 실행 중 메모리가 부족해졌을 때 발생하는 에러
4) Exception 클래스는 모든 예외(미약한 오류)의 조상이다.
- 사용자의 실수와 같은 프로그램 외적인 요인에 의해 발생하는 예외
- IOException: 입출력 예외
- ClassNotFoundException: 클래스 사용 시 해당 클래스가 존재하지 않아 생기는 예외
- RuntimeException: 프로그래머의 실수로 발생하는 예외
a. ArithmeticException: 산술 계산 예외 - 0으로 나누기 등
b. ClassCastException: 형변환을 잘못했을 때 발생하는 예외
c. NullPointerException: 빈 곳(널 포인터)에 무언가를 하려 할 때 발생하는 예외 - 빈 문자열의 길이 구하기 등
d. IndexOutOfBoundsException: 배열 범위를 벗어났을 때 발생하는 예외 - 위의 런타임 에러에 있는 코드 등
위와 같은 예외를 처리하는데 쓰는 구문이 try-catch문이다.
try{
// 실행할 문장
} catch(Exception1 e1){
// try문에서 Exception1이 발생했을 경우
// 이를 처리할 문장 작성
} catch(Exception2 e2){
// try문에서 Exception2이 발생했을 경우
// 이를 처리할 문장 작성
} catch(ExceptionK ek){
// try문에서 ExceptionK이 발생했을 경우
// 이를 처리할 문장 작성
}
1. catch 블록은 여러 개 올 수 있다.
- Exception은 모든 예외 클래스의 조상이므로, 여러 catch문이 있을 때 Exception catch문은 가장 마지막에 온다.
2. 블록 내에 있는 문장이 1개여도 괄호{}를 생략할 수 없다.
예시를 보면서 try-catch문의 흐름을 파악해보자.
// 예시1) try문에서 예외가 발생하지 않아 3이 출력이 되지 않음
System.out.print(1);
try {
System.out.print(2);
} catch(Exception e) {
System.out.print(3);
}
System.out.print(4);
// 124
// 예시2) try문에서 산술 예외가 발생해 해당 문장 이후를 수행하지 않고 catch문으로 이동
System.out.print(1);
try {
System.out.print(2);
System.out.print(3/0); // 0으로 나눌 수 없음 -> ArithmeticException
System.out.print(3);
} catch(Exception e) { // Exception은 모든 예외 클래스의 조상
System.out.print(4);
}
System.out.print(5);
// 1245
// 예시3) try문에서 예외가 발생하면, 여러 개의 catch문 중 맞는 것을 찾아감
System.out.print(1);
try {
System.out.print(2);
System.out.print(3/0); // // 0으로 나눌 수 없음 -> ArithmeticException
System.out.print(3);
} catch(ArithmeticException ae) {
if(ae instanceof ArithmeticException)
System.out.print(4);
System.out.print(5);
} catch(Exception e) { // ArithmeticException 이외의 예외 처리 catch문
System.out.println(6);
}
System.out.print(7);
// 12457
// 예시4) 알맞은 catch문을 찾지 못해 12까지 출력 후 예외 메세지 발생 후 프로그램 종료
System.out.print(1);
try {
System.out.print(2);
System.out.print(args[0]); // 배열의 범위를 벗어남
System.out.print(3);
} catch(ArithmeticException ae) { // 여기서 처리할 수 있는 예외가 아님
System.out.print(4);
}
System.out.print(5);
// 12Exception in thread "main"
// java.lang.ArrayIndexOutOfBoundsException:
// Index 0 out of bounds for length 0
1. try문을 수행하다가, 예외가 발생하면 해당 예외를 처리할 수 있는 catch문을 찾아 catch 블록을 수행하고 try-catch문을 완전히 빠져나오고 프로그램이 정상 종료된다.
2. 만약 해당 예외를 처리할 수 있는 catch문이 없다면 예외가 발생한 문장 전까지만 수행하고 프로그램이 비정상 종료된다.
3. Exception이 모든 예외 클래스의 조상이므로 모든 예외를 다 잡고 싶을 때 그냥 Exception catch문 하나만 쓰고 싶겠지만, 그러면 안 된다.
- 가능한 예외를 자세히 나눈 다음, 각 상황에 맞게 처리할 수 있는 블록을 작성하는 것이 좋다.
- 내가 생각할 수 있는 예외 외에도 무언가가 발생할 수 있기 때문에, 마지막에 Exception catch문을 작성해두고 혹시 모를 예외를 잡는 것이 좋다.