포스팅 목록 제목 URL 아이템 1. 생성자 대신 정적 팩터리 메서드를 고려하라 https://devfunny.tistory.com/526 아이템 2. 생성자에 매개변수가 많다면 빌더를 고려하라 https://devfunny.tistory.com/527 아이템 3. private 생성자나 열거 타입으로 싱글턴임을 보증하라 https://devfunny.tistory.com/528 아이템 4. 인스턴스화를 막으려거든 private 생성자를 사용하라 https://devfunny.tistory.com/529 아이템 5. 자원을 직접 명시하지 말고 의존 객체 주입을 사용하라 https://devfunny.tistory.com/530 아이템 6. 불필요한 객체 생성을 피하라 https://devfunny.tis..
Comparable 인터페이스 Comparable 인터페이스는 compareTo() 메서드가 유일하다. compareTo() 메서드는 단순 동치성 비교에 더해 순서까지 비교할 수 있으며, 제네릭하다. Comparable을 구현했다는 것은 그 클래스의 인스턴스들에는 자연적인 순서(natural order)가 있음을 뜻한다. 그래서 Comparable을 구현한 객체들의 배열은 손쉽게 정렬할 수 있다. Arrays.sort(a); 자바 플랫폼 라이브러리의 모든 값 클래스와 열거타입이 Comparable을 구현했다. 알파벳, 숫자, 연대 같이 순서가 명확한 값 클래스를 작성한다면 반드시 Comparable 인터페이스를 구현하자. Comparable.java public interface Comparable { ..
Cloneable 복제해도 되는 클래스임을 명시하는 용도의 인터페이스다. Cloneable.java public interface Cloneable { // 실제로 이렇게 비어있음 } 하지만 신기한 점이 있는데, clone 메서드가 선언된 곳이 Cloneable이 아닌 Object이다. Object.java protected native Object clone() throws CloneNotSupportedException; 위 코드를 보면 clone() 메서드는 protected 접근 제한자를 가진다. 이렇게 되면 Cloneable을 구현하는 것만으로는 외부 객체에서 clone 메서드를 호출할 수 없다. 위 방법은 clone() 메서드를 오버라이드하여 public 접근제한자로 변경하여 외부 객체에서 접..
toString() 메서드 재정의 Object의 기본 toString 메서드는 PhoneNumber@adbbd처럼 단순히 클래스이름@16진수로 표시한 해시코드를 반환한다. 해당 인스턴스의 유익한 정보를 반환하기 위해서 toString 메서드는 재정의해야한다. 실전에서 toString은 그 객체가 가진 주요 정보 모두를 반환하는게 좋다. 따라서 toString이 반환한 값에 포함된 정보를 얻어올 수 있는 API를 제공해야한다. PhoneNumber.java public final class PhoneNumber { private final short areaCode, prefix, lineNum; public PhoneNumber(int areaCode, int prefix, int lineNum) { t..
equals()와 hashCode() equals를 재정의한 클래스 모두에서 hashCode도 재정의해야한다. 그렇지 않으면 hashCode 일반 규약을 어기게 되어, 해당 클래스의 인스턴스를 HashMap이나 HashSet 같은 컬렉션의 원소로 사용할 때 문제를 일으킬 것이다. ▶ Object 명세에서 발췌한 규약 equals 비교에 사용되는 정보가 변경되지 않았다면, 애플리케이션이 실행되는 동안 그 객체의 hashCode 메서드는 몇 번을 호출해도 일관되게 항상 같은 값을 반환해야 한다. equals가 두 객체를 같다고 판단했다면, 두 객체의 hashCode는 똑같은 값을 반환해야 한다. equals가 두 객체를 다르다고 판단 했더라도 두 객체의 hashCode가 서로 다른 값을 반환할 필요는 없다...
equals() 메서드 재정의 equals 메서드 재정의는 간단해 보여도 함정이 많은 행위다. 문제를 회피하는 가장 좋은 방법은 아예 재정의를 하지 않는 것인데, 재정의를 하지 않으면 Object 클래스의 equals() 메서드를 호출하므로 그 클래스의 인스턴스는 오직 자기 자신과만 같게된다. ▶ 다음에서 열거한 상황 중 하나에 해당한다면 equals()를 재정의하지 말자. 1) 각 인스턴스가 본질적으로 고유하다. Object의 equals() 메서드가 이에 속한다. 2) 인스턴스의 '논리적 동치성(logical equality)'을 검사할 일이 없다. 논리적 동치성 5만원 지폐가 2개가 있을때, 각 지폐는 다른 지폐다. 하지만 금액은 같다. 어떤것을 비교하느냐에 따라 달라진다. 기본적으로 Object ..
Before. try~finally 자바 라이브러리에는 close 메서드를 호출하여 직접 닫아줘야하는 자원이 많다. 전통적으로 자원을 제대로 닫힘을 보장하는 수단으로 try~finally가 쓰였다. 더이상 자원을 회수하는 최선의 방책이 아니다. public class TopLine { // 코드 9-1 try-finally - 더 이상 자원을 회수하는 최선의 방책이 아니다! (47쪽) static String firstLineOfFile(String path) throws IOException { BufferedReader br = new BufferedReader(new FileReader(path)); try { return br.readLine(); } finally { br.close(); } }..
finalizer, cleaner 사용을 피하라 자바는 finalizer, cleaner이라는 두가지 객체 소멸자를 제공한다. 결론을 미리 말하자면, finalizer, cleaner 사용은 피해야한다. 두 객체 모두 즉시 수행된다는 보장이 없다. 객체에 접근할 수 없게 된 후 finalizer나 cleaner가 실행되기까지 얼마나 걸릴지는 알 수 없다. 때문에 제때 실행되어야 하는 작업은 절대 할 수 없다. finalizer, cleaner를 얼마나 신속하게 수행할지는 전적으로 가비지 컬렉터 알고리즘에 달렸으며, 이는 가비지 컬렉터 구현마다 각기 다르다. finalizer 구현 예제 finalizer 구현예제 public class FinalizerIsBad { @Override protected vo..
예제로 보는 메모리 누수 자바는 가비지 컬렉터를 갖춘 언어이기 때문에 다 쓴 객체를 알아서 해제해준다. 이는 메모리 관리에 더이상 신경쓰지 않아도 된다고 오해할 수 있는데, 절대 사실이 아니다. 메모리 누수가 일어나는 위치는 어디인가? 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) { e..
불필요한 객체 생성을 피하라 똑같은 기능의 객체를 매번 생성하기보다는 객체 하나를 재사용하는 편이 나을 때가 많다. 재사용은 빠르고 세련되다. new String() 하지 말것 아래 코드를 보자. 쓰면 안되는 코드 String s = new String("bikini"); // 따라하지 말 것 위 코드는 실행될 때마다 String 인스턴스를 새로 만든다. 완전히 쓸데없는 행위다. 권장되는 코드 String s = "bikini"; 위 코드는 새로운 인스턴스를 매번 만드는 대신 하나의 String 인스턴스를 사용한다. JVM 안에서 이와 똑같은 문자열 리터럴을 사용하는 모든 코드가 같은 객체를 재사용함이 보장된다. String.matches()를 사용할때 주의할것 생성 비용이 아주 비싼 객체가 있는데, 이..
정적 유틸리티 클래스 잘못 사용한 예제 - 유연하지 않고 테스트하기 어렵다. 싱글턴을 잘못 사용한 예제 - 유연하지 않고 테스트 하기 어렵다. public class SpellChecker { private static final Dictionary dictionary = new DefaultDictionary(); // 자원을 직접 명시 private SpellChecker() {} public static boolean isValid(String word) { // TODO 여기 SpellChecker 코드 return dictionary.contains(word); } public static List suggestions(String typo) { // TODO 여기 SpellChecker 코드 ..
private 생성자 구현 생성자를 명시하지 않으면 컴파일러가 자동으로 기본 생성자를 만들어준다. 즉, 매개변수를 받지 않는 public 생성자가 만들어지며, 사용자는 이 생성자가 자동 생성된 것인지 구분할 수 없다. 실제로 이러한 이유로 의도치 않게 인스턴스할 수 있게된 클래스가 종종 목격된다. 1) 추상 클래스로 만든다. (충분하지 않음) 결론적으로 추상 클래스로 만드는 것은 인스턴스화를 막을 수 없다. 하위 클래스를 만들어 인스턴스화하면 그만이다. public abstract class UtilityClass { ... } 2) private 생성자를 추가한다. 이 방법으로 클래스의 인스턴스화를 막을 수 있다. public class UtilityClass { /** * 이 클래스는 인스턴스를 만들..