오류의 종류
논리적 오류 : 동작은 문제 없지만 작성 의도와 다르게 동작하는 오류
컴파일 오류 : 컴파일 과정에서 발생하는 오류로 컴파일러의 구문체크 과정에서의 오류
런타임 오류 : 실행 할 때 발생하는 오류로 두 가지의 종류가 있음
- Error (오류) : 프로그램 코드에 의해서 수습될 수 없는 심각한 오류
- Exception (예외) : 프로그램 코드에 의해서 수습될 수 있는 무난한 오류
오류는 해결할 수 없는 문제니 처리가 가능한 예외를 처리하는 것을 예외처리 라고 함
예외 클래스의 계층 구조
Object ( Throwable ( Exception, Error) )
Error ( OutOfMemoryError, ... )
Exception ( IOException(입출력), ClassNotFoundException(클래스못찾음), ... , RuntimeException )
- Exception 클래스들은 사용자의 실수 같은 외적인 요인에 의해 발생하는 예외들
RuntimeException ( ArithmeticException, ClassCastException, NullPointerException, ..., IndexOutOfBoundsException )
예외처리
프로그램 실행 시 발생할 수 있는 예외의 발생을 사전에 대비해 코드를 작성하여
비정상적인 종료를 막고, 정상적인 상태를 유지하기 위함
try { // {} 생략불가
//예외가 발생할 가능성이 있는 코드 작성
} catch (Exception1 e1) {
//Exception1이 발생한 경우를 처리하기 위한 코드 작성
} catch (Exception2 e2) {
//Exception2가 발생한 경우를 처리하기 위한 코드 작성
} catch (ExceptionN eN) {
//ExceptionN이 발생한 경우를 처리하기 위한 코드 작성
}
//만약 try블럭 내에서 예외가 발생했을 경우
try {
System.out.println((선언하지 않은 변수);//이 코드에서 오류가 발생하여
System.out.println("Hello");//아래 남은 코드들을 실행하지 않고 catch문으로 이동
} catch (Exception1 e1) {
//Exception1이 발생한 경우를 처리하기 위한 코드 작성
}
//Exception e는 모든 예외의 최고 조상이라 모든 예외 처리 가능하므로
//가장 마지막 catch문에 작성
catch (Exception e) {
//예외 처리 코드
}
printStackTrace / getMessage 메소드
printStackTrace() : 예외 발생 당시 호출스택에 있던 메소드의 정보와 예외 메시지를 화면에 출력
getMessage() : 발생한 예외클래스의 인스턴스에 저장된 메시지를 얻음
예외발생 > 예외객체생성 > 객체에는 발생한 예외에 대한 정보와 메소드들을 가지고 있음
> 메소드들을 사용하여 예외에 대한 정보를 알 수 있음
catch (ArithmeticException ae) {
//참조변수를 통해 메소드 호출 가능
ad.printStacktrace();//어떤 종류의 오류인지, 발생원인, 발생위치
System.out.println(ae.getMessage()); // 발생원인
}
멀티 catch 블럭
내용이 같은 catch 블럭을 하나로 합치지만 예외끼리 부모자식 관계일 수는 없음
부모예외가 자식의 예외도 처리하여 의미 없기 때문에 부모예외만 처리해도 상관없음
한쪽 예외 객체에만 있는 메소드도 사용불가 무조건 공통으로 가지고 있는 메소드만 가능
// 사용 전
catch (ExceptionA e) {
e.printStackTrace();
} catch (ExceptionB e2) {
e2.printStackTrace();
}
// 사용 후
catch (ExceptionA | ExceptionB e) {
e.printStackTrace();
}
//한쪽 예외 객체에만 있는 메소드를 사용하고 싶을 경우는 형변환
catch (ExceptionA | ExceptionB e) {
if (e instanceof ExceptionA) {
ExceptionA e1 = (ExceptionA) e;
e1.methodA();
}
}
예외 발생
// 1. new 연산자 사용해서 예외 객체 생성
Exception e = new Exception("고의 예외 생성");
// 2. thorw 키워드를 사용하여 예외 발생
throw e;
// 한줄로도 가능
throw new Exception("한 줄로 예외 발생시키기");
예외 종류
checked 예외 : 컴파일러가 예외 처리 여부를 체크하여 예외 처리가 필수 (Exception 및 자손)
unchecked 예외 : 컴파일러가 예외 처리 여부를 체크 하지 않아 예외 처리가 선택적 (RuntimeException 및 자손)
메서드에 예외 선언 및 예외 떠넘기기
예외를 처리하지 못할 때 메소드를 호출한 쪽에 예외를 떠넘기는 방법
이 메소드를 사용하면 이러한 예외들이 발생할 수 있다 호출하는 쪽에 가르쳐줘서
호출하는 쪽에서 예외처리를 하도록 함
// 사용법
// 가능하면 필수처리예외(checked)만 떠넘기는게 좋음
접근제어자 반환타입 메소드명 () throws Exception1, Exception2, ..., ExceptionN {
// 메소드 바디
}
// 모든 예외를 떠넘기기도 가능
// 조상보다 많은 예외를 선언할 수는 없음
접근제어자 반환타입 메소드명 () throws Exception {
// 메소드 바디
}
메인 메소드에까지 예외를 떠넘기면 JVM의 기본 예외 처리기로 떠넘겨져
예외가 발생한 당시의 호출스택에 대해 알려줌
사용자 정의 예외 클래스 생성
조상 클래스로 Exception과 RuntimeException 중 선택하여 직접 예외 클래스를 정의할 수 있음
꼭 필요한 경우만 Exception을 상속 받고 가능하면 RuntimeException을 상속 받아 unchacked 예외로 만드는게 좋음
// 1. 조상으로부터 상속 받기
class MyException extends Exception {
// 2. 생성자 추가
//문자열을 매개변수로 받는 생성자
MyException(String message) {
// 3. 상속 받은 상위 클래스의 생성자 호출
super(message);
}
}
예외 되던지기
예외를 처리한 후 예외를 다시 발생시키는 것
호출한 쪽과 호출된 쪽 양쪽 메서드에서 모두 예외를 처리하는 것
static void method() throws Exception { // 예외를 떠넘기고
try {
throw new Exception(); //예외를 생성
} catch (Exception e) {
//예외처리 후
throw e; //예외 되던지기
}
}
finally 블럭
예외 발생여부와 관계없이 수행되어야 하는 코드
try {
} catch (Exception e) {
} finally {
// 예외 발생 여부와 관계 없이 항상 수행되어야 하는 코드
// try-catch문의 마지막에 위치해야 함
// try 블럭 안에 return문이 있어도 반드시 실행 됨
}
연결된 예외
한 예외가 다른 예외를 발생시킬 수 있는 예외
예외 A가 예외 B를 발생 시키면 A는 B의 원인 예외
- 여러 예외를 하나로 묶어서 다룰 때
- checked 예외를 unchecked 예외로 바꿀 때
// Throwable initCause(Throwable cause) : 지정한 예외를 원인 예외로 등록
// Throwable getCause() : 원인 예외를 반환
// 예외를 하나로 묶기
static void method() throws ExceptionA {
try {
} catch (ExceptionB eb) {
NewException ne = new NewException();
ne.initCause(eb) // 새로 만든 예외의 원인 예외를 현재 예외로 지정하여
throw ne; // 원인 예외(ExceptionB)를 되던지면 ExceptionA에 원인 예외가 포함되서 되던짐
}
}
// checked >> unchecked
static void method() throws CheckedException {
throw new RuntimeException(new UncheckedException());
// unchecked 예외를 만들고 원인 예외를 checked 예외로 지정하여 포함시킴
// initCause 메소드를 사용하지 않은 이유는 RuntimeException의 생성자 중
// 매개변수로 Throwable cause를 전달받는 생성자가 있기 때문
}