Book 30

[Effective Java]10.equals는 일반 규약을 지켜 재정의하라

이전에 equals, hashCode, toString에 대해서 정리한 적이 있으나 고려하지 않은 부분이 많은 것 같아 작성한다. equals 메서드는 재정의하기 쉬워보이지만 곳곳에 함정이 도사리고 있어서 자칫하면 끔찍한 결과를 초래한다. 문제를 회피하는 가장 쉬운 길은 아예 재정의하지 않는 것이다. 그냥 두면 그 클래스의 인스턴스는 오직 자기 자신과만 같게 된다. 따라서 다음에서 열거한 상황 중 하나에 해당된다면 재정의하지 않는 것이 최선이다. 1.각 인스턴스가 본질적으로 고유하다. 값을 표현하는 게 아니라 동작하는 개체를 표현하는 클래스가 여기 해당한다. Thread가 좋은 예로 Object의 equals 메서드는 이러한 클래스에 딱 맞게 구현되었다. 2.인스턴스의 '논리적 동치성'을 검사할 일이 없..

Book/Effective Java 2022.06.04

[Effective Java] 9.try-finally보다는 try-with-resources를 사용하라

자바 라이브러리에는 close 메서드를 호출해 직접 닫아줘야 하는 자원이 많다. InputStream, OutputStream, java.sql.Connection 등이 좋은 예다. 자원 닫기는 클라이언트가 놓치기 쉬워서 예측할 수 없는 성능 문제로 이어지기도 한다. 이런 자원 중 상당수가 안전망으로 finalizer를 활용하고는 있지만 finalizer는 그리 믿을만 하지 못하다. 전통적으로 자원이 제대로 닫힘을 보장하는 수단으로 try-finally가 쓰였다. // 코드 9-1 try-finally - 더 이상 자원을 회수하는 최선의 방책이 아니다! (47쪽) static String firstLineOfFile(String path) throws IOException { BufferedReader ..

Book/Effective Java 2022.06.03

[Effective Java] 8.finalizer와 cleaner 사용을 피하라

자바는 두 가지 객체 소멸자를 제공한다. 그 중 finalizer는 예측할 수 없고 상황에 따라 위험할 수 있어 일반적으로 불필요하다. 오동작, 낮은 성능, 이식성 문제의 원인이 되기도 한다. finalizer는 나름의 쓰임새가 몇 가지 있긴 하지만 기본적으로 '쓰지 말아야' 한다. 그래서 자바 9에서는 finalizer를 deprecated API로 지정하고 cleaner를 그 대안으로 소개했다. 하지만 자바 라이브러리에서도 finalizer를 여전히 사용한다.. cleaner는 finalizer보다는 덜 위험하지만 여전히 예측할 수 없고 느리고 일반적으로 불필요하다. finalizer와 cleaner는 즉시 수행된다는 보장이 없다. 객체에 접근할 수 없게 된 후 finalizer나 cleaner가 실..

Book/Effective Java 2022.06.03

[Effective Java] 7.다 쓴 객체 참조를 해제하라

가비지 컬렉터가 메모리 관리를 해주니 메모리 관리에 더 이상 신경쓰지 않아도 된다고 오해할 수 있는데 절대로 사실이 아니다. 스택을 간단히 구현한 다음 코드를 보면서 메모리 누수가 일어나는 위치가 어디인지 확인해보자 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 Objec..

Book/Effective Java 2022.06.03

[Effective Java] 6.불필요한 객체 생성을 피하라

똑같은 기능의 객체를 매번 생성하기보다는 객체 하나를 재사용하는 편이 나을 때가 많다. 특히 불변 객체(아이템 17)는 언제든 재사용할 수 있다. 다음 코드는 하지 말아야할 극단적인 예이다. String s = new String("bikini"); // 따라 하지 말 것! 이 문장은 실행될 때 마다 String 인스턴스를 새로 만들어 HEAP 영역에 할당된다. 완전히 쓸데없는 행위이므로 아래와 같이 사용하자. String s = "bikini"; 생성자 대신 정적 팩터리 메서드를 제공하는 불변 클래스에서는 정적 팩터리 메서드를 사용해 불필요한 객체 생성을 피할 수 있다. 예컨대 Boolean(String) 생성자 대신 Boolean.valueOf(String) 팩터리 메서드를 사용하는 것이 좋다. 그래..

Book/Effective Java 2022.06.03

[Effective Java] 5.자원을 직접 명시하지 말고 의존 객체 주입을 사용하라

많은 클래스가 하나 이상의 자원에 의존한다. 가령 맞춤법 검사기는 사전에 의존하는데 이런 클래스를 정적 유틸리티 클래스로 구현한 모습을 드물지 않게 볼 수 있다. 정적 유틸리티를 잘못 사용한 예 - 유연하지 않고 테스트하기 어렵다. public class SpellChecker { private static final Lexicon dictionary = ...; private SpellChecker() {} // 객체 생성 방지 public static boolean isValid(String word) { ... } public static List suggestions(String typo) { ... } } 비슷하게 싱글턴으로 구현하는 경우도 흔하다. 싱글턴을 잘못 사용한 예 - 유연하지 않고 테..

Book/Effective Java 2022.06.03

[Effective Java] 4.인스턴스화를 막으려거든 private 생성자를 사용하라

단순히 정적 메서드와 정적 필드만을 담은 클래스를 만들고 싶을 때가 있을 것이다. 객체 지향적으로 사고하지 않는 이들이 종종 남용하는 방식이지만 나름의 쓰임새가 있다고한다. 정적 멤버만 담은 유틸리티 클래스는 인스턴스로 만들어 쓰라고 설계한 것이 아니다. 하지만 생성자를 명시하지 않으면 컴파일러가 자동으로 기본 생성자를 만들어준다. 사용자의 입장에서는 생성자가 자동 생성된 것인지 구분할 수 없다. 실제 공개된 API들 중에서도 이처럼 의도치 않게 인스턴스화할 수 있게 된 클래스가 종종 있다고 한다. 추상 클래스로 만드는 것은 인스턴스화를 막을 수 없다. 하위 클래스를 만들어서 그것을 인스턴스화할 수 있기 때문이다. 이를 본 사용자는 상속해서 쓰라는 뜻으로 오해할 수 있으니 더 큰 문제일 수 있다. 따라서..

Book/Effective Java 2022.06.03

[Effective Java] 3.private 생성자나 열거 타입으로 싱글턴임을 보증하라

싱글턴이란 인스턴스를 오직 하나만 생성할 수 있는 클래스를 말한다. 스프링에서 빈을 관리하는 방식이 싱글턴이다. 그런데 클래스를 싱글턴으로 만들면 이를 사용하는 클라이언트가 테스트하기가 어려워질 수 있다. 타입을 인터페이스로 정의한 다음 그 인터페이스를 구현해서 만든 싱글턴이 아니라면 싱글턴 인스턴스를 가짜(mock) 구현으로 대체할 수 없기 때문이다. 싱글턴을 만드는 방식을 보통 둘 중 하나이다. 두 방식 모두 생성자는 private으로 감춰두고 유일한 인스턴스에 접근할 수 있는 수단으로 public static 멤버를 하나 마련해둔다. 우선 public static 멤버가 final 필드인 방식을 살펴보자. // 코드 3-1 public static final 필드 방식의 싱글턴 (23쪽) publi..

Book/Effective Java 2022.06.03

[Effective Java] 2. 생성자에 매개변수가 많다변 빌더를 고려하라

정적 팩터리와 생성자에는 똑같은 제약이 하나 있다. 선택적 매개변수가 많을 때 적절히 대응하기 어렵다는 점이다. 점층적으로 생성자 패턴을 쓸 수는 있지만 매개변수의 갯수가 많아지면 많아질수록 코드를 작성하거나 읽기가 어렵다. 이렇게 선택 매개변수가 많을 때 활용할 수 있는 방법 중 하나로 자바빈즈 패턴이 있다. 매개변수가 없는 생성자로 객체를 만든 후 setter 메서드를 호출해서 값을 설정하는 방식이다. 하지만 이 방식도 객체 하나를 만드려면 메서드를 여러개 호출해야 하고 객체가 완전히 생성되기 전까지는 일관성이 무너진다. 이처럼 일관성이 무너지는 문제 때문에 자바빈즈 패턴에서는 클래스를 불변(아이템 17)으로 만들 수 없으며 스레드 안정성을 얻으려면 프로그래머가 추가 작업을 해줘야만 한다. 이러한 단..

Book/Effective Java 2022.06.03

[Effective Java] 1.생성자 대신 정적 팩터리 메서드를 고려하라

예전에 읽다가 난이도가 높아서 미뤄두고 다른 공부했는데 눈으로만 읽으면 그냥 넘어가게 되는 부분이 있는 것 같아서 다시 읽고 아이템 별로 글을 작성하려한다. 완벽하게 익히려면 여러번 읽어야겠지만 이해가 닿는데까지 이해하고 일단 1회독을 목표로 하자. 클라이언트가 클래스의 인스턴스를 얻는 전통적인 수단은 public 생성자다. 생성자와 별도로 정적 팩터리 메서드를 제공할 수 있다고 한다. 이 방식은 장점과 단점이 모두 존재하는데, 장점/단점을 알아보면 왜 사용하는지 답이 나오겠다. 장점 1. 이름을 가질 수 있다. 생성자에 넘기는 매개변수와 생성자 자체로는 반환될 객체의 특성을 제대로 설명하지 못한다. 예를들어 생성자인 BigInteger(int, int, Random)과 정적 팩터리 메서드인 BigInt..

Book/Effective Java 2022.06.03