equals 메서드는 재정의하기 쉬워 보이지만 곳곳에 함정이 도사리고 있다. 가장 쉬운 길은 아예 재정의하지 않는 것이다. 1. equals를 재정의하지 않아도 되는 경우 1.1 각각의 객체가 본질적으로 고유할 때 값 표현 객체가 아닌 동작하는 개체를 표현하는 클래스일 때를 말한다. 대표적인 예로 Thread, Controller, Service 등이 이 조건에 부합한다. 1.2 인스턴스의 논리적 동치성을 검사할 일이 없을 때 Pattern의 인스턴스가 같은 정규 표현식을 나타내는지 검사하거나 Random 클래스의 equals 메서드가 큰 의미를 가지지 못하는 것처럼 클라이언트가 이 방식이 필요 없다고 판단되면 equals를 재정의 하지 않아도 된다. 1.3 상위 클래스에서 재정의한 equlas가 하위 ..
자바에는 close 메서드로 직접 닫아줘야 하는 자원이 많다. ex) InputStream, OutputStream, java.sql.Conncetion 등 자원 닫기는 클라이언트가 놓치기 쉬워 예측할 수 없는 성능 문제로 이어지기 때문에 자원을 닫는 수단으로 try-finally가 많이 사용되었다. 1. try-finally의 단점 1.1 코드 가독성이 떨어진다. String firstLineOfFile(String path) throw IOException { BufferedReader br = new BufferedReader(new FileReader(path)); try { return br.readLine(); } finally { br.close(); } } 가독성이 나쁘지않다. 하지만 자원..
자바의 객체와 관련된 자원 회수는 가비지 컬렉터가 담당하고 있다. 따라서 프로그래머에게 객체 자원 수거에 대한 아무런 작업도 요구하지 않지만, 그럼에도 자바는 두 가지 객체 소멸자를 가지고 있다. 비메모리 자원 회수는 try-with-resources와 try-finally를 사용해 해결한다. finalizer 예측할 수 없고, 상황에 따라 위험할 수 있어 보통 불필요하다. cleaner finalizer보다는 덜 위험하지만, 여전히 예측할 수 없고 느리다. 이 역시 보통은 불필요하다. 두 가지의 객체 소멸자는 예측할 수 없다. finalizer와 cleaner는 호출 후 즉시 수행된다는 보장이 없다. 즉, 호출된 후 언제 실행될지 알 수 없다는 의미이다. finalizer와 cleaner가 실행되기까지..
자바에서는 메모리 관리를 GC가 해주기 때문에 메모리 관리를 더 이상 신경 쓰지 않아도 된다고 생각할 수 있다. 하지만 GC의 영역에 벗어난 자원이 생성될 수 있으며, 이러한 자원이 생기지 않도록 주의해야 한다. 메모리 누수 케이스 1. 자기 자신의 메모리를 관리하는 클래스 이펙티브 자바에서는 Stack을 예로 든다. Stack은 객체 참조를 담는 저장소 풀을 만들어 원소들을 관리한다. 즉, 자기 자신의 메모리를 직접 관리한다. 문제는 저장소 풀에 등록된 원소들을 pop 할 때 생긴다. 일반적인 구현하는 pop의 로직은 Stack의 size를 -1 해주는 식으로 구현된다. 이때 Stack은 비활성화 영역에 다 쓴 참조를 가지게 되고, 메모리 누수의 원인이 된다. 다 쓴 참조(obselete referen..
1. 불필요한 객체 생성이란? 어떤 객체의 생성이 잦고 그 객체의 생성 비용이 높다면, 하나의 객체를 재사용하는 편이 좋다. 불필요한 객체 생성을 하지 않고 자원을 아낄 수 있기 때문이다. 특히, 불변 객체는 언제든 안심하고 재사용할 수 있다. 불변 객체는 인스턴스가 생성되면 그 상태를 변경할 수 없는 객체를 말한다. 객체의 상태가 변하지 않기 때문에 일관된 인스턴스를 공유해 재사용할 수 있다. 불변 객체에서 같은 값을 가지는 인스턴스가 한 개 이상 생성된다면, 그것은 불필요한 객체 생성이라 할 수 있다. 2. String을 통한 예시 대표적인 불변 클래스인 String을 통해 불필요한 객체 생성에 대한 예를 더욱 쉽게 이해할 수 있다. String a = new String("Effective"); 위..
Java의 객체지향은 많은 클래스가 하나 이상의 자원에 의존하게 된다. 이때 의존성을 코드에 명시할 경우 의존성이 클라이언트 코드에 강하게 결합되어 유연하지 않고 테스트하기 어려워진다. 자원을 코드에 직접 명시할 경우 문제점 다음과 같이 정적 유틸로 만들어진 결제 머신 클래스가 있다고 생각해보자. public class PaymentMachine { private static final Pay pay = new KakaoPay(); private PaymentMachine() { } // 생성자 방어 public static Payment payment() { /* 구현 */ } } 이 결제 머신은 카카오페이로 결제할 때 아주 잘 동작한다. 카카오페이를 직접적으로 의존하여 카카오 페이라는 자원을 가지고 ..
정적 메서드, 정적 필드를 가지는 유틸리티 클래스 개발을 하다 보면 유틸리티 클래스가 필요한 경우가 있다. 이러한 도구용 클래스는 단순히 정적 메서드와 정적 필드만을 담은 클래스로써 객체 지향과는 거리가 멀지만 분명 쓸모가 있다. 예시로는 java.lang.Math와 java.util.Arrays가 있다. Math 클래스는 계산과 관련된 정적 메서드와 상수들을 담고 있고, Arrays 클래스는 배열과 관련된 정적 메서드를 담고 있다. 이러한 유틸리티 클래스는 인스턴스화를 위해 설계된 클래스가 아니기 때문에 두 클래스 모두 private 생성자를 가지는 것을 볼 수 있다. private 기본 생성자를 명시하는 이유 그렇다면 왜 기본 생성자를 private으로 명시해주는 것일까? 그 이유는 생성자를 명시하지..
싱글턴? 싱글턴이란 인스턴스를 오직 하나만 생성할 수 있는 클래스를 말한다. 싱글턴을 만드는 두 가지 방법 1. 인스턴스를 public static final 필드로 만들고 private으로 생성자를 방어 private 생성자는 Singleton.INSTANCE가 초기화될 때 딱 한 번만 호출된다. public이나 protected 생성자가 없기 때문에 초기화될 때 만들어진 인스턴스가 전체 시스템에서 하나뿐임을 보장한다. 장점 : 간결하기 때문에 누가봐도 싱글턴 클래스임을 한눈에 알아볼 수 있다. 단, 리플렉션 API를 사용하면 private 생성자를 호출할 수 있어 새로운 인스턴스를 생성할 수 있다. 이를 해결하기 위해서는 생성자에서 두 번째 객체가 생성될 때 예외를 던지면 된다. 2. 정적 팩터리 메..
정적 팩터리 메서드와 생성자는 선택적 매개변수가 많을 때 적절히 대응하기 어렵다. 이러한 제약에 대안으로 프로그래머들은 다음과 같은 방법을 사용했다. 점층적 생성자 패턴 자바 빈즈 패턴 빌더 패턴 1. 점층적 생성자 패턴 필수 매개변수만 받는 생성자, 필수 매개변수와 선택 매개변수 1개를 받는 생성자, 선택 매개변수를 2개 받는 생성자 ....... 선택 매개변수를 전부 다 받는 생성자까지 점층적으로 늘려가는 방식이다. public class NutritionFacts { private final int servingSize; // (mL, 1회 제공량) 필수 private final int servings; // (회, 총 n회 제공량) 필수 private final int calories; // (1..