예외는 오직 예외상황에서만 사용해야 한다. 절대로 일상적인 제어 흐름용으로 쓰여선 안 된다.
아이템 70. 복구할 수 있는 상황에는 검사 예외를, 프로그래밍 오류에는 런타임 예외를 사용하라
자바는 문제 상황을 알리는 타입으로 검사 예외, 런타임 예외, 에러 세 가지를 제공
검사 예외 : 호출하는 쪽에서 복구하리라 여겨지는 상황이라면 검사 예외를 사용 -> 이것지 검사와 비검사 예외를 구분하는 기본 규칙
Checked Exception은 컴파일 시점에서 체크되는 예외입니다. 컴파일러는 프로그램 코드에서 이러한 예외가 적절히 처리되었는지를 확인합니다. 체크드 예외는 반드시 try-catch 블록으로 처리하거나, 메서드 선언부에 throws 키워드를 사용하여 던져야 합니다.
특징
컴파일 타임 체크: 컴파일 시점에서 예외 처리 여부를 검사합니다.
필수 처리: 예외를 반드시 처리하거나 메서드에 선언해야 합니다.
일반적인 예: IOException, SQLException, ClassNotFoundException 등이 있습니다.
public class CheckedExample {
public static void main(String[] args) {
try {
FileReader file = new FileReader("test.txt");
BufferedReader fileInput = new BufferedReader(file);
for (int counter = 0; counter < 3; counter++)
System.out.println(fileInput.readLine());
fileInput.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Unchecked Exception는 런타임 시점에서 발생하는 예외로, 컴파일러가 예외 처리 여부를 검사하지 않습니다. 이러한 예외는 일반적으로 프로그래머의 실수로 발생하는 경우가 많습니다. 언체크드 예외는 RuntimeException 클래스와 그 하위 클래스들입니다.
특징
런타임 체크: 런타임 시점에서 예외가 발생합니다.
선택적 처리: 예외 처리가 필수가 아닙니다.
일반적인 예: NullPointerException, ArrayIndexOutOfBoundsException, ArithmeticException 등이 있습니다.
public class UncheckedExample {
public static void main(String[] args) {
int[] arr = {1, 2, 3, 4, 5};
System.out.println(arr[5]); // This will throw ArrayIndexOutOfBoundsException
}
}
주요 차이점
체크 시점: 체크드 예외는 컴파일 시점에서, 언체크드 예외는 런타임 시점에서 체크됩니다.
처리 필요성: 체크드 예외는 반드시 처리하거나 선언해야 하지만, 언체크드 예외는 선택적으로 처리할 수 있습니다.
용도: 체크드 예외는 주로 외부 환경과의 상호작용에서 발생하는 예외를 처리하기 위해 사용되고, 언체크드 예외는 주로 프로그래머의 실수나 논리적 오류를 나타냅니다.
프로그래밍 오류를 나타낼 때는 런타임 예외를 사용하자. 런타임 예외의 대부분은 전제조건을 만족하지 못했을 때 발생
전제조건 위배 : 단순히 클라이언트가 해당 API의 명세에 기록된 제약을 지키지 못했다는 뜻(예를 들어, 배열의 인덱스는 0에서 배열크기-1)
에러는 보통 JVM이 자원 부족, 불변식 깨짐 등 더 이상 수행을 계속할 수 없는 상황을 나타낼 때 사용 (OOM)
비검사 throwable은 모두 RuntimeException의 하위 클래스여야 하고, Error는 상속하지 말아야할 뿐 아니라, throw로 던지는 일도 없어야 한다.
throwable은 절대로 사용하지 말자 !!
아이템 71. 필요 없는 검사 예외 사용은 피하라
검사 예외는 발생한 문제를 프로그래머가 처리하여 안전성을 높이게끔 해준다. 물론, 검사 예외를 과하게 사용하면 오히려 쓰기 불편한 API가 된다.
검사 예외를 던지는 메서드는 스트림 내부에서 직접 사용할 수 없다.
검사 예외를 회피하는 가장 쉬운 방법은 적절한 결과 타입을 담은 옵셔널을 반환하는 것, 검사 예외를 던지는 대신 단순히 빈 옵셔널을 반환(검사 예외가 단 하나뿐인 경우에는 빈 옵셔널을 리턴하는 것도 방법일듯)
단점 : 예외가 발생한 이유를 알려주는 부가 정보를 담을 수 없다는 것 -> 로깅 시스템으로 단점 극복 가능할듯 ?
예외를 사용하면 구체적인 예외 타입과 그 타입이 제공하는 메서드들을 활용해 부가 정보 제공이 가능
아이템 72. 표준 예외를 사용하라
예외 클래스 수가 적을수록 메모리 사용량도 줄고 클래스를 적재하는 시간도 적게 걸린다. 그렇기에 표준 예외를 사용하는게 좋다.
가장 많이 재사용 되는 예외는 IllegalArgumentException, 호출자가 인수로 부적절한 값을 넘길 때 던지는 예외
Exception, RuntimeException, Throwable, Error는 직접 재사용하지 말자. 이 클래스들은 추상 클래스라고 생각
IllegalArgumentException : 허용하지 않는 값이 인수로 건네졌을때(null은 따로 NullPointerExcetpion)
IllegalStateException : 객체가 메서드를 수행하기에 적절하지 않은 상태일 때
NullPointerException : null을 허용하지 않는 메서드에 null을 건넸을 때
IndexOutOfBoundsException : 인덱스가 범위를 넘어섰을 때
ConcurrentModificationException : 허용하지 않는 동시 수정이 발견되었을 때
UnsupportedOperationException : 클라이언트가 요청한 동작을 대상 객체가 지원하지 않을 때 던지는 표준 예외
예외는 직렬화할 수 있다. 직렬화에는 많은 부담이 따르니 표준 예외를 확장할때 많은 고민이 필요하다.
직렬화(Serialization)는 객체를 바이트 스트림으로 변환하여 파일에 저장하거나 네트워크를 통해 전송할 수 있게 하는 기능
단점
성능 부담
직렬화 과정에서의 성능 저하: 객체를 직렬화하고 역직렬화하는 과정은 CPU와 메모리를 많이 소모할 수 있습니다. 특히 대형 객체 그래프의 경우, 성능 부담이 상당합니다 .
파일 크기 및 네트워크 대역폭
직렬화된 객체의 크기: 자바 직렬화는 효율성이 떨어질 수 있으며, 직렬화된 데이터가 상당히 큰 파일이나 데이터 스트림을 생성할 수 있습니다. 이는 저장 공간을 많이 차지하고 네트워크 전송 시 대역폭을 많이 소모하게 됩니다 .
호환성 문제
버전 관리: 클래스의 버전이 변경되면(예: 필드 추가/삭제), 기존에 직렬화된 객체와의 호환성 문제가 발생할 수 있습니다. 이를 해결하기 위해 serialVersionUID를 사용하는데, 이는 여전히 관리가 복잡합니다 .
보안 문제
보안 취약점: 직렬화된 객체는 보안 위험을 내포할 수 있습니다. 예를 들어, 악의적인 사용자가 조작된 바이트 스트림을 보내 역직렬화하는 경우, 원하지 않는 객체나 데이터를 로드하여 보안 취약점이 발생할 수 있습니다 .
디버깅 어려움
디버깅과 유지보수의 어려움: 직렬화된 데이터는 사람이 읽을 수 없는 형태이기 때문에 디버깅이 어렵습니다. 또한, 직렬화에 문제가 발생하면 이를 추적하고 수정하는 것이 까다로울 수 있습니다 .
특정 환경 의존성
환경 의존성: 자바 직렬화는 자바 플랫폼에 종속적입니다. 다른 프로그래밍 언어나 시스템과의 상호 운용성을 제공하기 어렵습니다. JSON, XML, 프로토콜 버퍼(Protocol Buffers)와 같은 더 범용적인 데이터 포맷이 대안이 될 수 있습니다 .
아이템 73. 추상화 수준에 맞는 예외를 던져라
예외 번역 : 상위 계층에서는 저수준 예외를 잡아 자신의 추상화 수준에 맞는 예외로 바꿔 던져야 한다.
핵심 : 아래 계층에서는 예외가 발생하지 않도록 하는 것이 최선
상위 계층 메서드의 매개변수 값을 아래 계층 메서드로 건네기 전에 미리 검사하자. 또는 상위 계층에서 예외를 처리하여 문제를 API 호출자에게까지 전파하지 않는 방법, 이럴 경우 로깅시스템을 활용
아이템 74. 메서드가 던지는 모든 예외를 문서화하라
메서드가 던질 가능성이 있는 모든 예외를 문서화하라.
문서화에는 자바독의 @throws 태그를 사용
아이템 75. 예외의 상세 메시지에 실패 관련 정보를 담으라
예외를 잡지 못해 프로그램이 실패하면 자바 시스템은 그 예외의 스택 추적 정보를 자동으로 출력
실패 순간을 포착하려면 발생한 예외에 관여된 모든 매개변수와 필드의 값을 실패 메시지에 담아야 한다.
개인적으로 API 실패와 관련해서는 예외 정보와 request, response 정보를 로깅에 남기는게 좋을 것 같음
아이템 76. 가능한 한 실패 원자적으로 만들라
실패 원자적 : 호출된 메서드가 실패하더라도 해당 객체는 메서드 호출 전 상태를 유지해야 한다.
실패 원자적으로 만드는 방법
불변 객체로 설계 : 메서드가 실패하면 새로운 객체게 만들어지지는 않을 수 있으나 기존 객체가 불안정한 상태에 빠지는 일은 없음 -> 생성 시점으로부터 객체 상태가 변하지 않으므로
가변 객체의 메서드를 실패 원자적으로 만드는 흔한 방법은 작업 수행에 앞서 매개변수의 유효성 검사 -> 불변 객체 쓰자 그냥
객체의 임시 복사본에서 작업을 수행한 다음, 작업이 성공적으로 완료되면 원래 객체와 교체 -> 하지말자
작업 도중 발생하는 실패를 가로채는 복구 코드를 작성 -> 하지말자
아이템 77. 예외를 무시하지 말라
예외를 무시하기로 했다면 catch 블록 안에 그렇게 결정한 이유를 주석으로 남기고, 예외 변수의 이름도 ignored로 바꾸자
아이템 69. 예외는 진짜 예외 상황에만 사용하라
아이템 70. 복구할 수 있는 상황에는 검사 예외를, 프로그래밍 오류에는 런타임 예외를 사용하라
자바는 문제 상황을 알리는 타입으로 검사 예외, 런타임 예외, 에러 세 가지를 제공
Checked Exception
은 컴파일 시점에서 체크되는 예외입니다. 컴파일러는 프로그램 코드에서 이러한 예외가 적절히 처리되었는지를 확인합니다. 체크드 예외는 반드시 try-catch 블록으로 처리하거나, 메서드 선언부에 throws 키워드를 사용하여 던져야 합니다.특징
Unchecked Exception
는 런타임 시점에서 발생하는 예외로, 컴파일러가 예외 처리 여부를 검사하지 않습니다. 이러한 예외는 일반적으로 프로그래머의 실수로 발생하는 경우가 많습니다. 언체크드 예외는 RuntimeException 클래스와 그 하위 클래스들입니다.주요 차이점
체크 시점: 체크드 예외는 컴파일 시점에서, 언체크드 예외는 런타임 시점에서 체크됩니다.
처리 필요성: 체크드 예외는 반드시 처리하거나 선언해야 하지만, 언체크드 예외는 선택적으로 처리할 수 있습니다.
용도: 체크드 예외는 주로 외부 환경과의 상호작용에서 발생하는 예외를 처리하기 위해 사용되고, 언체크드 예외는 주로 프로그래머의 실수나 논리적 오류를 나타냅니다.
프로그래밍 오류를 나타낼 때는 런타임 예외를 사용하자. 런타임 예외의 대부분은 전제조건을 만족하지 못했을 때 발생
에러는 보통 JVM이 자원 부족, 불변식 깨짐 등 더 이상 수행을 계속할 수 없는 상황을 나타낼 때 사용 (OOM)
비검사 throwable은 모두 RuntimeException의 하위 클래스여야 하고, Error는 상속하지 말아야할 뿐 아니라, throw로 던지는 일도 없어야 한다.
throwable은 절대로 사용하지 말자 !!
아이템 71. 필요 없는 검사 예외 사용은 피하라
아이템 72. 표준 예외를 사용하라
예외 클래스 수가 적을수록 메모리 사용량도 줄고 클래스를 적재하는 시간도 적게 걸린다. 그렇기에 표준 예외를 사용하는게 좋다.
가장 많이 재사용 되는 예외는
IllegalArgumentException
, 호출자가 인수로 부적절한 값을 넘길 때 던지는 예외Exception
,RuntimeException
,Throwable
,Error
는 직접 재사용하지 말자. 이 클래스들은 추상 클래스라고 생각IllegalArgumentException
: 허용하지 않는 값이 인수로 건네졌을때(null은 따로 NullPointerExcetpion)IllegalStateException
: 객체가 메서드를 수행하기에 적절하지 않은 상태일 때NullPointerException
: null을 허용하지 않는 메서드에 null을 건넸을 때IndexOutOfBoundsException
: 인덱스가 범위를 넘어섰을 때ConcurrentModificationException
: 허용하지 않는 동시 수정이 발견되었을 때UnsupportedOperationException
: 클라이언트가 요청한 동작을 대상 객체가 지원하지 않을 때 던지는 표준 예외예외는 직렬화할 수 있다. 직렬화에는 많은 부담이 따르니 표준 예외를 확장할때 많은 고민이 필요하다.
직렬화(Serialization)는 객체를 바이트 스트림으로 변환하여 파일에 저장하거나 네트워크를 통해 전송할 수 있게 하는 기능
단점
파일 크기 및 네트워크 대역폭 직렬화된 객체의 크기: 자바 직렬화는 효율성이 떨어질 수 있으며, 직렬화된 데이터가 상당히 큰 파일이나 데이터 스트림을 생성할 수 있습니다. 이는 저장 공간을 많이 차지하고 네트워크 전송 시 대역폭을 많이 소모하게 됩니다 .
호환성 문제 버전 관리: 클래스의 버전이 변경되면(예: 필드 추가/삭제), 기존에 직렬화된 객체와의 호환성 문제가 발생할 수 있습니다. 이를 해결하기 위해 serialVersionUID를 사용하는데, 이는 여전히 관리가 복잡합니다 .
보안 문제 보안 취약점: 직렬화된 객체는 보안 위험을 내포할 수 있습니다. 예를 들어, 악의적인 사용자가 조작된 바이트 스트림을 보내 역직렬화하는 경우, 원하지 않는 객체나 데이터를 로드하여 보안 취약점이 발생할 수 있습니다 .
디버깅 어려움 디버깅과 유지보수의 어려움: 직렬화된 데이터는 사람이 읽을 수 없는 형태이기 때문에 디버깅이 어렵습니다. 또한, 직렬화에 문제가 발생하면 이를 추적하고 수정하는 것이 까다로울 수 있습니다 .
특정 환경 의존성 환경 의존성: 자바 직렬화는 자바 플랫폼에 종속적입니다. 다른 프로그래밍 언어나 시스템과의 상호 운용성을 제공하기 어렵습니다. JSON, XML, 프로토콜 버퍼(Protocol Buffers)와 같은 더 범용적인 데이터 포맷이 대안이 될 수 있습니다 .
아이템 73. 추상화 수준에 맞는 예외를 던져라
아이템 74. 메서드가 던지는 모든 예외를 문서화하라
아이템 75. 예외의 상세 메시지에 실패 관련 정보를 담으라
아이템 76. 가능한 한 실패 원자적으로 만들라
아이템 77. 예외를 무시하지 말라