1. 예외 처리
1) 프로그램 오류
프로그램이 실행 중 어떤 원인에 의해서 오작동을 하거나 비정상적으로 종료가 되는 경우가 있다. 이런 결과를 초래하는 원인을 프로그램 에러 또는 오류라고 한다. 이를 발생하는 시점에 따라 두 가지로 나눌 수 있다.
컴파일 에러 : 컴파일 시에 발생하는 에러
런타임 에러 : 실행 시에 발생하는 에러
소스 코드를 컴파일하면 컴파일러가 소스코드(.java)에 대해 잘못된 구문, 오타 등의 기본적인 검사를 수행해서 오류가 있는지 알려준다. 컴파일러가 알려준 에러들을 모두 수정해서 컴파일을 성공적으로 마치고 나면, 클래스 파일(.class)가 생성되고, 생성된 클래스 파일을 실행할 수 있게 되는 것이다.
하지만 컴파일을 에러 없이 성공적으로 마쳤다고 해도 프로그램 실행 중에 에러가 발생할 수 있다. 자바는 런타임 시에 발생할 수 있는 프로그램 오류를 '에러'와 '예외' 두 가지로 구분하였다.
에러 : 프로그램 코드에 의해서 수습될 수 없는 심각한 오류
ex) 메모리부족(OutOfMemoryError), 스택오버플로우(StackOverFlowError) 등
예외 : 프로그램 코드에 의해서 수습될 수 있는 다소 미약한 오류
에러가 발생하면, 프로그램의 비정상적인 종료를 막을 길이 없지만, 예외는 발생하더라도 프로그래머가 이에 대한 적절한 코드를 미리 작성해 놓음으로써 프로그램의 비정상적인 종료를 막을 수 있다.
2) 예외 클래스의 계층 구조
모든 클래스의 조상은 Object 클래스이므로 Exception과 Error 클래스 역시 Object의 자손들이다.
모든 예외의 최고 조상은 Exception 클래스이다. 아래 그림에서 볼 수 있듯이, 예외 클래스를 두 그룹으로 나눌 수 있다.
1) Exception 클래스와 그 자손들(RuntimeException 제외)
2) RuntimeException 클래스와 그 자손들
RuntimeException 클래스들은 배열의 범위가 벗어나거나 정수를 0으로 나눌려고 하는 등 주로 프로그래머들의 실수에 의해서 발생될 수 있는 예외로 자바의 프로그래밍 요소들과 관계가 깊다. Exception 클래스들은 존재하지 않는 파일이름을 입력하는 등 주로 외부 영향으로 발생할 수 있는 것들로서, 프로그램의 사용자들의 동작에 의해서 발생하는 경우가 많다.
3) 예외 처리하기 - try-catch 문
예외처리란, 프로그램 실행 시 발생할 수 있는 예기치 못한 예외의 발생에 대비한 코드를 작성하는 것이며, 목적은 예외의 발생으로 인한 실행 중인 프로그램의 갑작스러운 비정상 종료를 막고, 정상적인 실행 상태를 유지할 수 있도록 하는 것이다.
발생한 예외를 처리하지 못하면, 프로그램은 비정상적으로 종료되며, 처리되지 못한 예외는 JVM의 예외처리기가 받아 예외의 원인을 화면에 출력한다.
예외를 처리하기 위해 try-catch문을 사용한다. 하나의 try 블럭 다음에는 여러 종류의 예외를 처리할 수 있도록 하나 이상의 catch 블럭이올 수 있으며, 이 중 발생한 예외의 종류와 일치하는 단 한 개의 catch 블럭만 수행된다.
try{
// 예외가 발생할 가능성이 있는 문장들을 넣는다
} catch (Exception e1) {
// e1이 발생했을 경우, 이를 처리하기 위한 문장을 적는다
} catch (Exception e2) {
// e2이 발생했을 경우, 이를 처리하기 위한 문장을 적는다
}
4) try-catch 문에서의 흐름
try-catch 문에서, 예외가 발생한 경우와 발생하지 않았을 때의 흐름은 달라진다.
- try 블럭 내에서 예외가 발생한 경우
1) 발생한 예외와 일치하는 catch 블럭이 있는지 확인한다
2) 일치하는 catch 블럭을 찾게 되면, 그 catch 블럭 내의 문장들을 수행하고 전체 try-catch 문을 빠져나가서 그 다음 문장을 계속해서 수행한다. 만일 일치하는 catch 블럭을 찾지 못한다면, 예외는 처리되지 못한다.
- try 블럭 내에서 예외가 발생하지 않은 경우
1) catch 블럭을 거치지 않고, 전체 try-catch 문을 빠져나가서 수행을 계속한다.
try 블럭에서 예외가 밸생하면, 예외가 발생한 위치 이후에 있는 try 블럭의 문장들은 수행되지 않으므로, try 블럭에 포함시킬 코드의 범위를 잘 선택해야 한다.
5) 예외의 발생과 catch 블럭
catch 블럭은 괄호()와 블럭{} 두 부분으로 나누어져 있는데, 괄호 () 내에는 처리하고자 하는 예외와 같은 타입의 참조 변수 하나를 선언해야 한다.
예외가 발생하면, 발생한 예외에 해당하는 클래스의 인스턴스가 만들어진다. 예를 들어 ArithmeticException이 발생하면 ArithmeticException 인스턴스가 생성이 된다. 예외가 발생한 문장이 try 블럭에 포함되어 있다면, 이 예외를 처리할 수 있는 catch 블럭이 있는지 찾게 된다.
첫 번째 catch 블럭부터 차례로 내려가면서 catch 블럭의 괄호 () 내에 선언된 참조 변수의 종류와 생성된 예외 클래스의 인스턴스에 instanceof 연산자를 이용해서 검사하게 되는데, 검사 결과가 true인 catch 블럭을 만날 때까지 검사는 계속된다.
검사 결과가 true인 catch 블럭을 찾게 되면 블럭에 있는 문장들을 모두 수행한 후에 try-catch 문을 빠져나가고 예외는 처리되지만, 검사 결과가 true인 catch 블럭이 하나도 없으면 예외는 처리되지 않는다.
printStackTrace()와 getMessage()
예외가 발생했을 때 생성되는 예외 클래스의 인스턴스에는 발생한 예외에 대한 정보가 담겨 있으며, getMessage()와 printStackTrace()를 통해서 이 정보들을 얻을 수 있다. catch 블럭의 괄호()에 선언된 참조 변수를 통해 이 인스턴스에 접근할 수 있다. 이 참조 변수는 선언된 catch 블럭 내에서만 사용 가능하다
printStackTrace() : 예외 발생 당시에 호출 스택에 있었던 메서드의 정보와 예외 메시지를 화면에 출력한다
getMessage() : 발생한 예외 클래스의 인스턴스에 저장된 메시지를 얻을 수 있다.
멀티 catch 블럭
여러 catch 블럭을 '|' 기호를 이용해서 하나의 catch 블럭으로 합칠 수 있게 되었으며, 이를 멀티 catch 블럭이라고 한다.
try{
...
} catch(ExceptionA | ExceptionB e){
e.printStackTrace();
}
만일 멀티 catch 블럭의 '|' 기호로 연결된 예외 클래스가 조상과 자손의 관계에 있다면 컴파일 에러가 발생한다. 두 예외 클래스가 조상과 자손의 관계에 있다면, 그냥 조상 클래스만 써주는 것과 똑같기 때문이다.
6) 예외 발생시키기
키워드 throw를 사용해서 프로그래머가 고의로 예외를 발생시킬 수 있다.
1. 먼저, 연산자 new를 이용해서 발생시키려는 예외 클래스의 객체를 만든 다음
Exception e = new Exception("고의로 발생시킴");
2. 키워드 throw를 이용해서 예외를 발생시킨다
throw e;
try{
Exception e = new Exception("고의 예외");
throw e;
} catch(Exception e){
e.printStackTrace();
}
- Exception 인스턴스를 생성할 때, 생성자에 String을 넣어주면, 이 String이 Exception 인스턴스에 메시지로 저장된다. 이 메시지는 getMessage()를 이용해서 얻을 수 있다.
- Exception 클래스들이 발생한 가능성이 있는 문장에 대해 예외 처리를 해주지 않으면 컴파일조차 되지 않지만, RuntimeExcpetion 클래스들에 해당하는 예외는 프로그래머들에 의해 실수로 발생하는 것들이기 때문에 예외 처리를 강제하지 않는 것이다.
7) 메서드에 예외 선언하기
메서드에 예외를 선언하려면, 메서드의 선언부에 키워드 throws를 사용해서 메서드 내에서 발생할 수 있는 예외를 적어주기만 하면 된다. 그리고 예외가 여러 개일 경우에는 쉼표(,)로 구분한다.
void method() throws Exception1, Exception2 ... ExceptionN {
// 메서드 내용
}
- 예외를 발생시키는 키워드 throw와 예외를 메서드에 선언할 떄 쓰이는 throws를 구별하자
8) finally 블럭
finally 블럭은 예외의 발생 여부에 상관 없이 실행되어야 할 코드를 포함시킬 목적으로 사용된다. try-catch-finally의 순서로 구성된다.
try{
// 예외가 발생할 가능성이 있는 문장을 넣는다
} catch(Exception1 e1){
// 예외 처리를 위한 문장을 적는다.
} finally {
// 예외의 발생 여부에 관계없이 항상 수행되어야 하는 문장들을 넣는다.
}
9) 사용자 정의 예외 만들기
보통 Exception 클래스나 RuntimeException 클래스로부터 상속받아 클래스를 만들지만, 필요에 따라서 알맞은 예외 클래스를 선택할 수 있다.
class MyException extends Exception{
}
'백엔드 > JAVA' 카테고리의 다른 글
[JAVA의 정석] Ch 9. java.lang 패키지와 유용한 클래스 (1) | 2025.04.12 |
---|---|
[JAVA의 정석] Ch 7. 객체 지향 프로그래밍 2 (0) | 2025.04.05 |
[JAVA의 정석] Ch 6. 객체 지향 프로그래밍 1 (0) | 2025.03.30 |
[JAVA의 정석] Ch 5. 배열 (0) | 2025.03.19 |
[JAVA의 정석] Ch 3. 연산자(operator) (0) | 2025.03.11 |