컴파일 에러(compile-time error) : 컴파일 시 발생하는 에러
런타임 에러(runtime error) : 실행 시 발생하는 에러 (프로그램 종료)
논리적 에러(logical error) : 작성 의도와 다르게 동작
runtime error 에서, error는 어쩔 수 없지만, exception은 처리하자 => 예외 처리
정의 : 프로그램 실행 시 발생할 수 있는 예외의 발생에 대비한 코드를 작성하는 것.
목적 : 프로그램의 비정상 종료를 막고, 정상적인 실행 상태를 유지.
상속 계층도
Object // 최고 조상
|
Trowable // 클래스. 모든 오류의 조상
|
------------
| |
Exception Error // 둘다 Runtime Error
| |
RuntimeException, OOME(Out Of Memory Error)...
IOException...
IOException : 입출력 예외
ClassNotFoundException : 클래스가 존재하지 않음 (*.class)
...
RuntimeException + 자손들 : 프로그래머의 실수로 발생하는 예외
try {
// 예외가 발생할 가능성이 있는 문장들
}
catch (Exception1 e1) {
// Exception1이 발생했을 경우, 이를 처리하기 위한 문장 작성
}
catch (Exception2 e2) {
// Exception2가 발생했을 경우, 이를 처리하기 위한 문장 작성
}
catch (ExceptionN eN) {
// ExceptionN이 발생했을 경우, 이를 처리하기 위한 문장 작성
}
try 블럭에서 프로그램을 수행하다가, 예외 발생 시 특정 타입의 "예외 객체"가 생성됨 -> 발생한 예외에 대한 정보가 들어있음 (메서드를 갖고 있음) -> 메서드를 통해 정보를 가져올 수 있음
printStackTrace() : 예외 발생 당시의 호출 스택(call stack)에 있었던 메서드의 정보와 예외 메세지를 화면에 출력
getMessage() : 발생한 예외 클래스의 인스턴스에 저장된 메세지를 얻을 수 있음
// ex 1
try {
// ...
}
catch (ExceptionA | ExceptionB e) { // ExceptionA와 B가 부모자식관계일 경우,
// 멀티 catch 블럭을 사용 할 필요가 없음.
// instanceof 연산자로 체크하기 때문에, 부모 클래스 하나만 보면 됨
e.printStackTrace();
}
// ex 2
try {
//...
}
catch (ExceptionA | ExceptionB e) {
e.methodA(); // ExceptionA에만 있는 method는 사용하면 안됨
// ExceptionA와 B의 공통 멤버만 사용 가능
if (e instanceof ExceptionA) {
ExceptionA e1 = (ExceptionA) e; // 형변환 필요
e1.methodA(); // ExceptionA에 선언된 메서드 호출 가능
}
else {
// if (e instanceof ExceptionB)
}
}
예외 처리 방법
: try-catch문(직접 처리), 예외 선언하기(예외 떠넘기기, 알리기), 은폐
예외 선언
: 메서드가 호출시 발생가능한 예외를 호출하는 쪽에 알리는 것
오버라이딩
: 선언부 일치, 접근 제어자 좁게 X, 조상보다 많은 예외 선언 X
// 예외 발생 : throw
// 예외 선언 : throws
// 메서드에 예외 선언 : 메서드 호출 시, 해당 예외들이 발생할 수 있다는 걸 알려줌
void method() throws Exception1, Exception2, ... , ExceptionN {
// 메서드 내용
}
// method() 내에서 Exception과 그 자손에 대한 예외가 발생 가능함을 알림
void method() throws Exception { // 모든 예외의 최고조상
// 메서드 내용
}
// ex
// 두 예외가 발생할 수 있음을 알림
static void startInstall() throws SpaceException, MemoryException {
if (!enoughSpace())
throw new SpaceException("설치 공간이 부족합니다.");
if (!enoughMemory())
throw new MemoryException("메모리가 부족합니다.");
}
예외 발생 여부와 관계 없이, 수행되어야 하는 코드를 넣는다.
try 블럭 안에 return 문이 있어서, try 블럭을 벗어 날 때도, finally 블럭은 반드시 실행된다.
try {
// 예외 발생 가능성이 있는 문장 삽입
}
catch (Exception1 e1) {
// 예외 처리
}
finally {
// 예외 발생 여부와 관계 없이, 항상 수행되어야 하는 문장 삽입
// try-catch 문의 맨 마지막에 위치해야 함
}
직접 예외 클래스 정의
상속을 통해 만들며, 조상은 Exception or RuntimeException 중 선택
class MyException extends Exception {
// 에러 코드 값 저장하기 위한 필드 추가
private final int ERR_CODE;
// 생성자를 넣어주어야 함!
MyException(String msg, int errCode) { // 문자열을 매개변수로 받는 생성자
super(msg); // 조상인 Exception 클래스의 생성자 호출
ERR_CODE = errCode;
}
MyException(String msg) {
this(msg, 100);
}
public int getErrCode() { // 에러코드를 얻는 메서드
return ERR_CODE; // getMessage()와 함께 사용 될 것
}
}
exception re-throwing
예외를 처리한 후에 다시 예외를 발생시키는 것
호출한 메서드와 호출된 메서드 양쪽 모두에서 예외처리 하는 것
한 예외가 다른 예외를 발생시킬 수 있다.
세부적인 예외들을 포괄적인 예외로 감싼다.
예외 A가 예외 B를 발생시키면, A는 B의 원인 예외(cause exception) 라고 한다.
사용하는 이유
여러 예외를 하나로 묶어서 다루기 위해서 (너무 많으면, catch 블럭이 많아짐)
checked 예외(Exception 자손, 필수처리)를 unchecked 예외(RuntimeException의 자손, 선택처리)로 변경하려 할 때
// Throwable : Exception, Error의 조상클래스
// 하나의 예외(Throwable) 안에, 또다른 예외(원인 예외)를 포함시키는 것
public class Trowable implements Serializable {
...
// 원인 예외를 저장하기 위한 iv
private Throwable cause = this; // 객체 자신(this)을 원인 예외로 등록
...
public synchronized Throwable initCause(Throwable cause) {
...
this.cause = cause; // cause를 원인 예외로 등록
return this;
}
...
}
// ex : 이유 1번
void install() throws InstallException { // InstallException만 던지게 했음
try {
startInstall(); // SpaceException 발생
copyFiles();
}
catch (SpaceException e) { // 예외 연결!
InstallException ie = new InstallException("설치 중 예외 발생"); // 예외 생성
ie.initCause(e); // InstallException의 원인 예외를 SpaceException으로 지정
throw ie; // InstallException을 발생시킨다
}
catch (MemoryException me) { // 예외 연결!
...
}
}
// 실제로 발생한 것은 SpaceException인데, InstallException을 만들어서 그 안에 넣어준것.
// ex : 이유 2번
// Exception 자손 : 예외 선언. 예외 필수 처리는 예외 선언을 꼭 해줘야 함
static void startInstall() throws SpaceException, MemoryException {
if (!enoughSpace())
throw new SpaceException("설치할 공간이 부족합니다");
if (!enoughMemory())
throw new MemoryException("메모리가 부족합니다");
}
// RuntimeException으로 변경.
// 클래스 자체를 변경 할 경우, 다른 프로그램에 영향을 줄 수 있으므로 연결 예외를 이용한다.
static void startInstall() throws SpaceException {
if (!enoughSpace())
throw new SpaceException("설치할 공간이 부족합니다");
// 필수 처리에서 선택처리로 변경. 예외 선언 안 해도됨!
if (!enoughMemory())
throw new RuntimeException(new MemoryException("메모리가 부족합니다"));
// 생성자를 사용한 원인 예외 지정
// 생성자가 다음과 같이 지정되어 있음. -> RuntimeException(Throwable cause)
}