본문 바로가기

독서찰기(讀書札記)

(90)
[아이템 14] Comparable을 구현할지 고려하라 ※ compareTo는 Object의 메서드가 아니다. (equals, hashCode, toString, clone은 Object의 메서드) ※ compareTo 메서드 : 객체가 주어진 객체보다 작으면 음의 정수를, 같으면 0을, 크면 양의 정수를 반환한다. 객체와 비교할 수 없는 객체가 주어지면 ClassCastException을 던진다. [Why] compareTo는 단순 동치성 비교에 더해 순서까지 비교할 수 있으며, 제네릭하다. Comparable을 구현한 객체들의 배열은 손쉽게 정렬할 수 있다. e.g.) Arrays.sort(a); [When] 알파벳, 숫자, 연대 같이 순서가 명확한 값 클래스를 작성한다면 반드시 Comparable 인터페이스를 구현하자. [How] equals 규약과 똑..
[아이템 13] clone 재정의는 주의해서 진행하라 ※ Cloneable 인터페이스에 대하여 더보기 복제해도 되는 클래스임을 명시하는 용도의 mixin interface아이템20. 메서드 하나 없는 Cloneable 인터페이스는 Object의 protected 메서드인 clone의 동작 방식을 결정한다. 가장 큰 문제는 clone 메서드가 선언된 곳이 Cloneable이 아닌 Object라는 점과 protected로 선언되어있다는 점이다. Cloneable은 상위 클래스에 정의된 protected 메서드의 동작 방식을 변경한 것이다. (따라하지 말자!) 그래서 Cloneable을 구현하는 것만으로는 외부 객체에서 clone 메서드를 호출할 수 없다. Cloneable을 구현한 클래스의 인스턴스에서 clone을 호출하면 그 객체의 필드들을 하나하나 복사한 ..
[아이템 12] toString을 항상 재정의하라 [Why] toString의 일반 규약에 따르면 '간결하면서 사람이 읽기 쉬운 형태의 유익한 정보'를 반환해야 한다. toString의 규약은 '모든 하위 클래스에서 이 메서드를 재정의하라'고 한다. [When] 모든 구체 클래스에서 Object의 toString을 재정의하자. 상위 클래스에서 이미 알맞게 재정의한 경우는 예외다. toString 메서드는 객체를 println, printf, 문자열 연결 연산자(+), assert 구문에 넘길 때, 혹은 디버거가 객체를 출력할 때 자동으로 불린다. toString을 제대로 정의하지 않는다면 쓸모없는 메시지(클래스_이름@16진수로_표시한_해시코드)만 로그에 남을 것이다. 정적 유틸리티 클래스는 toString을 제공할 이유가 없다. 대부분의 열거 타입아이템..
[아이템 11] equals를 재정의하려거든 hashCode도 재정의하라 [Why] equals를 재정의한 클래스에서 hashCode를 재정의하지 않으면 hashCode의 일반 규약을 어기게 되어 해당 클래스의 인스턴스를 HashMap, HashSet 같은 컬렉션의 원소로 사용할 때 문제를 일으킬 것이다. [When] Object 명세에서 발췌한 규약 equals 비교에 사용되는 정보가 변경되지 않았다면, 애플리케이션이 실행되는 동안 그 객체의 hashCode 메서드는 몇 번을 호출해도 일관되게 항상 같은 값을 반환해야 한다. 단, 애플리케이션을 다시 실행한다면 이 값이 달라져도 상관없다. equals(Object)가 두 객체를 같다고 판단했다면, 두 객체의 hashCode는 똑같은 값을 반환해야 한다. equals(Object)가 두 객체를 다르다고 판단했더라도, 두 객체의..
[아이템 10] equals는 일반 규약을 지켜 재정의하라 [Why] equals 규약을 어기면 그 객체를 사용하는 다른 객체들이 어떻게 반응할지 알 수 없다. Object 명세에 적힌 규약 equals 메서드는 동치관계(equivalence relation)을 구현하며, 다음을 만족한다. 반사성(reflexivity): null이 아닌 모든 참조 값 x에 대해, x.equals(x)는 true다. 대칭성(symmetry): null이 아닌 모든 참조 값 x, y에 대해, x.equals(y)가 true면 y.equals(x)도 true다. 추이성(transitivity): null이 아닌 모든 참조 값 x, y, z에 대해, x.equals(y)가 true이고 y.equals(z)도 true면 x.equals(z)도 true다. 일관성(consistency):..
[아이템 9] try-finally보다는 try-with-resources를 사용하라 [Why] 자바 라이브러리에는 close 메서드를 호출해 직접 닫아줘야 하는 자원이 많다. InputStream, OutputStream, java.sql.Connection 등 전통적으로 자원이 제대로 닫힘을 보장하는 수단으로 try-finally가 쓰였다. // 자원이 둘 이상이면 try-finally 방식은 너무 지저분하다! static void copy(String src, String dst) throws IOException { InputStream in = new FileInputStream(src); try { OutputStream out = new FileOutputStream(dst); try { byte[] buf = new byte[BUFFER_SIZE]; int n; while ..
[아이템 8] finalizer와 cleaner 사용을 피하라 [Why] finalizer는 예측할 수 없고, 상황에 따라 위험할 수 있으니 기본적으로 '쓰지 말아야' 한다. cleaner 역시 예측할 수 없고, 느리고, 일반적으로 불필요하다. finalizer 동작 중 발생한 예외는 무시되며, 처리할 작업이 남았더라도 그 순간 종료된다. 보통의 경우에는 예외가 발생하여 스택 추적 내역을 출력하겠지만, finalizer에서는 경고조차 출력되지 않는다. finalizer와 cleaner는 성능도 심각하다. 가비지 컬렉터보다 50배 정도 느리다. finalizer를 사용한 클래스는 finalizer 공격에 노출되어 심각한 보안 문제를 일으킬 수도 있다. 생성자나 직렬화 과정에서 만들어지다 만 객체를 악용할 수 있다. [When] 파일 닫기 finalizer와 clean..
[아이템 7] 다 쓴 객체 참조를 해제하라 [Why] 자바처럼 가비지 컬렉터를 갖춘 언어는, 다 쓴 객체를 알아서 회수해가기 때문에 자칫 메모리 관리에 더 이상 신경을 쓰지 않아도 된다고 오해할 수 있는데, 절대 사실이 아니다. [When] public class Stack { private Object[] elements; private int size = 0; private static final int DEFAULT_INITIAL_CAPACITY = 16; public Stack() { elements = new Object[DEFAULT_INITIAL_CAPACITY]; } public void push(Object e) { ensureCapacity(); elements[size++] = e; } public Object pop() { ..