- 컴파일 에러 : 빨간줄 뜨는것! (개발자의 오타나 로직실수로) 실행하지못하는것
- 런타임 에러 : 프로그램이 정상적으로 실행되지만 실행도중에 중간에 에러나는거 EX) 널포인터오류, 배열크기벗어난오류
- 시스템 에러 : 램의 공간이 부족하거나, 전원이 차단되는 에러 (개발자가 처리할 수 없음)
이러한 에러를 해결하기 위해서는 소스 수정으로 해결 가능한 에러를 예외 라고 한다
부모 계층의 클래스들이 자식 계층의 클래스들의 오류를 다 처리해준다
ex) catch문에 RuntimeException 넣으면 ArithmeticExcption, NullPointerException 등 자식 exception들을 다 처리할 수 있음
Exception
throw
키워드 사용 -> 예외도 객체이므로 new
로 생성 후 예외 발생 throws
키워드 사용public class MyCheckedException extends Exception {
public MyCheckedException(String message) {
super(message);
}
}
public class Client {
public void call() throws MyCheckedException { // 해결못해서 밖으로 던지기
throw new MyCheckedException("ex"); // 예외 발생시키기 (ex = 메시지입력)
}
}
public class Service {
Client client = new Client();
// 예외를 잡아서 처리하는 코드
public void callCatch(){
try{
client.call(); // call 메소드에서 예외발생시 catch에서 예외처리
}catch(MyCheckedException e){
// 예외처리로직
}
}
// 체크 예외를 밖으로 던지는 코드
public void catchThrow() throws MyCheckedException { // 예외를 잡지 않고 밖으로 던지기
client.call();
}
}
//ex) 밑에 예외들은 무조건 try ~ catch로 예외처리 해야함!
// try ~ catch로 하지 않을시 프로그램 시작 자체가 안됨!
throw new FileNotFoundException(); //오류
throw new EOFException(); // 오류
throw new SQLException(); // 오류
예외처리 2가지 규칙
try catch
문throws 예외이름
Exception
을 지정하면, 자식 예외들 모두 처리 가능Checked Exception
장단점
// 발생하는 예외에 대해서 처리하지 않아도 되는 예외 (Unchecked Exception)
ArithmeticException : 수학적으로 계산이 불가능할 때 발생하는 Exception
int su = 10;
int su2 = 0;
System.out.println(su/su2);
// ArrayIndexOutOfBounsException
배열의 인덱스범위를 초과해서 접근할 때 발생하는 예외
+int[] intArr = new int[5];
System.out.println(intArr[5]);
// ClassCastException
클래스형변환을 잘못했을 때
Object o = new String("안녕");
Integer num = (Integer)o; // 문자열을 정수형으로 바꿀 수 없음
// NullPointException
참조형변수에 null값이 있을 때 접근연산자를 사용하면 발생하는 예외
String name = null;
name.length(); // name에는 널값이 있는데 .연산자로 접근하고 있어서 발생
// NumberFormatException
문자열을 숫자형으로 변환할 때 변환 불가능한 문자가 있는 경우
String name = null;
su = Integer.parseInt(name); //문자열을 정수로 바꿀 수 없음
// InputMissMatchException
입력값의 타입이 일치하지 않을 때 발생
Scanner sc = new Scanner(System.in);
int su3 = sc.nextInt();
UnChecked Exception
장단점
✅ 체크 예외 vs 언체크 예외
체크예외 : 예외를 잡아서 처리하지 않으면 항상 명시적으로 throws
키워드로 예외를 던져야함
-> Exception
을 상속받은 예외는 체크 예외가 됨
언체크예외 : 예외를 잡아서 처리하지 않아도 throws
키워드를 생략 가능
-> RuntimeException
을 상속받은 예외는 언체크 예외가 됨
try catch
문을 이용해서 예외를 처리할 경우
정상적으로 연결이 되든 안되든 필수적으로 실행해야하는 기능이 있다 (ex : 자원 반환)
그럴 경우 catch 뒤 finally
키워드를 사용한다
try{
// 정상흐름
}catch{
// 예외흐름
}finally{
// 반드시 호출해야하는 마무리 흐름
}
try
문이 시작되면, catch
안에서 잡을 수 없는 예외가 발생해도 finally
코드는 어떤 경우라도 호출됨
try finally
만 사용할 수도 있음
try
에서 외부 자원을 사용하고, try
가 끝나면 외부 자원을 반납하는 패턴이 반복됨
-> 자바7에서부터 try-with-resources
라는 편의 기능을 제공함
이 기능을 사용하려면 먼저 AutoCloseable
인터페이스를 구현해야함
이 인터페이스를 구현하면 try
가 끝나는 시점에 close()
가 자동으로 호출됨
-> try
에서만 자원을 사용하기 때문에, catch
문 시작전에 try
가 끝나는 순간 close
() 호출됨
try-with-resources
장점
close()
호출이 필요 없으므로 코드가 간결해짐try-> catch-> finally
로 catch
이후에 자원을 반납했지만try
블럭이 끝나면 즉시 close()
를 호출한다 (더 빠른 자원해제가 가능)try{
// 정상흐름
}catch(ConnectException e){
// 연결오류
}catch(NetworkClientException e){
// 네트워크 오류
}catch(Exception e){
// 알수없는 오류 -> 위 2개 예외로 처리못한 나머지 모든 예외들은 여기서 처리됨
}finally{
// 무조건실행되는 로직
}
실무에서는 수많은 예외처리들이 있다
하지만 실제로 개발자가 catch
문을 사용해서 처리할 수 있는 것이 많지 않다
그래서 모든 예외들을 잡는 것은 좋지 않다 -> catch
로 처리해도 똑같은 문제가 발생하기 때문에
ex) 데이터베이스 서버 문제
그러면 모든 예외들을 throws
를 사용해서 던지면 되지 않을까?
하지만 요즘 라이브러리도 많이 사용하면서, 수 많은 예외들이 존재한다
모든 예외들을 해결하지 않고 밖으로 던지면 모든 클래스에서 지저분한 코드만 추가해지는 것 뿐이다
-> 해결하지 못한 예외를 폭탄처럼 돌리기 때문에
그러면 최상위 계층인 Exception
을 던져서 처리하면 되지 않을까?
코드는 깔끔해지지만 치명적인 문제가 발생한다
-> 최상위 타입인 Exception
을 던지게되면 다른 체크 예외를 체크할 수 있는 기능이 무효화됨
-> 중요한 체크 예외를 다 놓치게 됨
-> 중간에 중요한 체크 예외가 발생해도 컴파일러는 문법에 맞다 판단해서 컴파일 오류를 발생 X
즉 꼭 필요한 경우가 아니라면, Exception
자체를 밖으로 던지는 것은 좋지 않다
요즘은 본인이 해결할 수 있는 예외만 잡아서 처리하고
해결할 수 없는 예외는 던지는것이 더 나은 선택일 수도 있다
그리고 처리할 수 없는 예외들은 공통
으로 처리할 수 있는 곳을 만들어서 한곳에서 해결한다
ex) 처리할 수 없는 모든 예외들 -> 현재 시스템에 문제가 있습니다 오류 메시지 출력
// 공통 예외처리
private static void exceptionHandler(Exception e) {
//공통 처리
System.out.println("사용자 메시지: 죄송합니다. 알 수 없는 문제가 발생했습니다.");
System.out.println("==개발자용 디버깅 메시지==");
e.printStackTrace(System.out); // 스택 트레이스 출력
//e.printStackTrace(); // System.err에 스택 트레이스 출력
//필요하면 예외 별로 별도의 추가 처리 가능
if (e instanceof SendExceptionV4 sendEx) {
System.out.println("[전송 오류] 전송 데이터: " + sendEx.getSendData());
}
}
try {
networkService.sendMessage(input);
} catch (Exception e) { // 모든 예외를 잡아서 처리
exceptionHandler(e);
}
초기 자바에서 제공하는 컴파일에서 잡아주는 체크 예외가 더 많았고, 많이 사용하였다.
하지만 시간이 지나면서 복구할 수 없는 예외가 많아지고 라이브러리에서 제공하는 모든 예외들을
처리할 수 없을 때마다 throws 예외를 덕지덕지 붙였었다.
이러한 문제점 때문에 최근 만들어진 라이브러리는 대부분 UnChecked Runtime 에러를
사용함
t런타임 예외도 필요한 부분은 잡을 수 있고, 해결할 수 없는 부분은 공통으로 처리하는 부분에 던져줘서 처리 또는 그 부분은 무시하면 효율적이다.