예외처리(exception handling)
프로그램 오류
프로그램이 실행 중 어떤 원인에 의해서 오작동을 하거나 비정상적으로 종료하는 경우가 있는데 이러한 결과를 초래하는 원인을 프로그램 에러 또는 오류라고 한다.
발생시점에 따라 세가지로 나눈 오류
- 컴파일 에러(compile-time error) 컴파일 시에 발생하는 에러. 클래스 파일이 만들어지지 않는다.
- 런타임 에러(runtime error) 프로그램의 실행 시에 발생하는 에러. 실행은 되는데 에러로 인하여 프로그램이 종료됨.
- 논리적 에러(logical error) 실행은 되지만, 의도와 다르게 동작하는 것.프로그램이 종료되지 않지만 의도와 다르게 동작.
javac.exe(자바 컴파일러)가 하는 일
- 구문체크
- 번역
- 최적화
- 생략된 코드 추가
소스코드를 컴파일 하면 컴파일러가 소스코드(.java)에 대해 오타나 잘못된 구문, 자료형 체크 등의 기본적인 검사를 수행하여 오류를 알려준다. 에러들을 모두 수정해서 컴파일을 성공적으로 마치고 나면, 클래스 파일(.class)이 생성되고 생성된 클래스 파일을 실행할 수 있게 된다.
프로그램의 실행도중 발생할 수 있는 에러를 고려하여 대비를 해야한다.
자바에서는 실행시(runtime) 발생할 수 있는 프로그램 오류를 ‘에러(error)’와 ‘예외(exception)’으로 구분하였다.
- 에러(error) : 프로그램 코드에 의해서 수습될 수 없는 심각한 오류
- 예외(exception) : 프로그램 코드에 의해서 수습될 수 있는 다소 미약한 오류
예외 클래스의 계층구도
모든 클래스의 조상은 Objectzmffotmdlamfh Exception과 Error클래스 역시 Object클래스의 자손들이다.
Throwable : 모든 오류의 조상
Exception : 미약한 오류 / Error : 심각한 오류
OutOfMemoryError : 메모리 부족 오류
예외 클래스들은 두 그룹으로 나눠질 수 있다.
- Exception클래스와 그 자손들(위의 진한 네모영역) 사용자의 실수와 같은 외적인 요인에 의해 발생하는 예외 컴파일러가 예외처리 여부를 체크하는 checked예외에 해당함.(예외처리 필수)
- RuntimeException클래스와 그 자손들(위의 연한 네모영역) 프로그래머의 실수로 발생하는 예외 컴파일러가 예외처리 여부를 체크하지 않는 unchecked예외.(예외처리 선택적)
IOException : 입출력 에러시 발생
ClassNotFoundException : 클래스파일 존재하지 않을 경우 발생
ArithmeticException : 산술연산과정에서의 예외
ClassCastException : 형변환 예외
NullPointerException : 값이 null일 때 예외
IndexOutOfBoundsException : 배열범위 벗어날 경우 예외
예외처리하기 - try-catch문
예외처리(exception handling)의
정의 : 프로그램 실행 시 발생할 수 있는 예외에 대비한 코드를 작성하는 것
목적 : 프로그램의 비정상 종료를 막고, 정상적인 실행상태를 유지하는 것
예외를 처리하지 못하면 프로그램은 비정상적으로 종료되며, 처리되지 못한 예외(uncaught exception)는 JVM의 ‘예외처리기(UncaughtExceptionHandler)’가 받아서 예외의 원인을 화면에 출력한다.
try{
//예외가 발생할 가능성이 있는 문장들을 넣는다.
}catch (Exception e1){
//Exception1이 발생했을 경우, 이를 처리하기 위한 문장을 적는다.
}catch (Exception e2){
//Exception2가 발생했을 경우, 이를 처리하기 위한 문장을 적는다.
}
발생한 예외의 종류와 일치하는 단 한 개의 catch블럭만 수행된다.
try블럭 또는 catch블럭에 또 다른 try-catch문이 포함될 수 있다. 그러나 catch블럭 내에 또 하나의 try-catch문이 포함된 경우, 같은 이름의 참조변수를 사용해서는 안된다.
*if문과 달리, try블럭이나 catch블럭 내에 포함된 문장이 하나뿐이어도 괄호{}를 생략할 수 없다.
try-catch문에서의 흐름
try블럭 내에서 예외가 발생한 경우
- 발생한 예외와 일치하는 catch블럭이 있는지 확인한다.
- 일치하는 catch블럭을 찾게 되면, 그 catch블럭 내의 문장들을 수행하고 전체 try-catch문을 빠져나가서 그 다음 문장을 계속해서 수행한다. 만일 일치하는 catch블럭을 찾지 못하면, 예외는 처리되지 못하고 프로그램이 종료된다.
try블럭 내에서 예외가 발생하지 않은 경우
- catch블럭을 거치지 않고 전체 try-catch문을 빠져나가서 수행을 계속한다.
예외의 발생과 catch블럭
- 예외가 발생하면, 이를 처리할 catch 블럭을 찾아 내려간다.
- 일치하는 catch블럭이 없으면, 예외는 처리되지 않음.
- Exception이 선언된 catch 블럭은 모든 예외처리(제일 마지막에 온다. Exception이 모든 예외의 조상이기 때문)
printStactTrace()와 getMessage()
예외 발생시 생성되는 예외 클래스의 인스턴스에는 발생한 예외에 대한 정보가 담겨져 있다.
printStackTrace() : 예외발생 당시의 호출스택(Call Stack)에 있었던 메서드의 정보와 예외 메시지를 화면에 출력한다.
getMessage() : 발생한 예외클래스의 인스턴스에 저장된 메시지를 얻을 수 있다.
class Exception{
public static void main(String args[]){
System.out.println(1);
System.out.println(2);
try{
System.out.println(3);
System.out.println(0/0); //에러발생
}catch(ArithmeticException ae){
ae.printStactTrace(); //참조변수 ae를 통해, 생성된 ArithmeticException인스턴스에 접근
System.out.println("예외 메세지 : " + ae.getMessage());
}
}
}
멀티 catch블럭
내용이 같은 catch블럭을 ‘|’기호를 통해 하나로 합친 것.(JDK1.7부터)(개수 제한 없음)
try{
...
}catch(ExceptionA e){
e.printStackTrace();
}catch(ExceptionB e2){
e2.printStactTrace();
}
//하나로 합치기
try{
...
}catch(ExceptionA | ExceptionB e){
e.printStactTrace();
}
만약 ‘|’로 연결된 예외 클래스가 조상과 자손 관계에 있다면 컴파일 에러가 발생한다. 왜냐하면, 두 예외 클래스가 조상과 자손의 관계에 있다면, 그냥 조상 클래스만 써주는 것과 똑같기 때문이다.
멀티catch는 여러 예외를 처리하는 것이기 때문에, 실제로 어떤 예외가 발생한 것인지 알 수 없다. 그래서 참조변수 e로 멀티 catch블럭에 ‘|’기호로 연결된 예외 클래스들의 공통 분모인 조상 예외 클래스에 선언된 멤버만 사용할 수 있다.
예외 발생시키기
- 먼저 연산자 new를 이용해서 발생시키려는 예외 클래스의 객체를 만든 다음 Exception e = new Exception(”고의로 발생시켰음");
- 키워드 throw를 이용해서 예외를 발생시킨다. throw e;
위의 두 줄을 한줄로 줄여 쓸 수 있다. → throw new Exception(”고의로 발생시켰음”);
checked필수인 Exception클래스들(Exception클래스와 그 자손들)은 try-catch로 예외처리를 해주지 않는다면 컴파일시 에러가 발생한다.
메서드에 예외 선언하기
try-catch문을 사용하는 것 외에, 예외를 메서드에 선언하는 방법이 있다.
메서드의 선언부에 키워드 throws를 사용해서 메서드 내에서 발생할 수 있는 예외를 적어주기만 하면 된다.
(예외가 여러 개일 경우 쉼표로 구분한다.)
메서드 호출시 발생가능한 예외를 호출한 쪽에 알리는 작업.
void method() throws Exception1, Exception2, ... ExceptionN{
//메서드 내용
}
*예외를 발생시키는 키워드 throw와 예외를 메서드에 선언할 때 쓰이는 throws를 잘 구별하자!
만약 모든 예외의 최고조상인 Exception클래스를 메서드에 선언하면, 이 메서드는 모든 종류의 예외가 발생할 가능성이 있다는 뜻이다. 이렇게 선언하면 그 자손타입의 예외까지 발생할 수 있으므로 오버라이딩할 때는 단순히 선언된 예외의 개수가 아니라 상속관계까지 고려해야 한다.
예외를 처리하는 방법
- try - catch : 직접 처리
- 예외선언하기 : 예외 떠넘기기(알리기)
- 은폐하기 : catch{}을 빈 상태로 놔둔다.
finally블럭
finally블럭은 예외의 발생여부에 상관없이 실행되어야할 코드를 포함시킬 목적으로 사용한다.
try-catch-finally순으로 사용된다.
catch블럭의 문장 수행 중에 return문을 만나도 finally블럭의 문장들은 수행된다.
자동 자원 반환 try-with-resources문
JDK1.7부터 try-with-resources문이라는 try-catch문의 변형이 새로 추가되었다.
주로 입출력에 사용되는 클래스 중에서는 사용한 후에 꼭 닫아 줘야 하는 것들이 있다. 그래야 사용했던 자원(resources)이 반환되기 때문이다.
try-with-resources문의 괄호()안에 객체를 생성하는 문장을 넣으면, 이 객체는 따로 close()를 하지 않아도 try블럭을 벗어나는 순간 자동적으로 close()가 호출된다.
try (FileInputStream fis = new FileInputStream("file.txt")){//try블럭 벗어날 경우 자동으로 close();
...
} catch(IOException e) {
...
}
사용자정의 예외 만들기
필요에 따라 프로그래머가 새로운 예외 클래스를 정의하여 사용할 수 있다.
*보통 Exception클래스 또는 RuntimeException클래스로부터 상속받을 경우 되도록 후자를 사용한다.
→checked예외는 반드시 예외처리를 해주어야 하기 때문에 예외처리가 불필요한 경우에도 try-catch문을 넣어 코드가 복잡해지기 때문.
예외 되던지기(exception re-throwing)
예외 처리 후 다시 예외를 발생시키는 것.
예외 되던지기를 통하여 예외가 발생한 메서드와 호출한 메서드, 양쪽에서 처리하도록 할 수 있다.
연결된 예외(chained exception)
한 예외가 다른 예외를 발생시킬 수도 있다. 예외를 발생시킨 예외를 원인 예외(cause exception)라고 한다.
Throwable initCause(Throwable cause) //지정한 예욀를 원인 예외로 등록
Throwable getCause() //원인 예외를 반환
원인 예외로 등록해서 다시 예외를 발생시키는 이유는 여러가지 예외를 하나의 큰 분류의 예외로 묶어서 다루기 위해서이다. 또 다른 이유는 checked예외를 unchecked예외로 바꿀 수 있도록 하기 위해서이다.
Exception의 자손을 RuntimeException으로 감싸서 unchecked예외로 만들 수 있다.
'Java' 카테고리의 다른 글
람다식(Lambda expression) (1) | 2022.09.30 |
---|---|
java.lang패키지와 유용한 클래스 (0) | 2022.09.03 |
객체지향프로그래밍2 (0) | 2022.08.17 |
객체지향 프로그래밍1 (0) | 2022.08.17 |