전체 글
11. equals를 재정의하려거든 hashCode도 재정의하라
equals를 재정의한 클래스 모두에서 hashCode도 재정의해야 한다. 그렇지 않으면 hashCode 일반 규약을 어기게 되어 해당 클래스의 인스턴스를 HashMap이나 HashSet 같은 컬렉션의 원소로 사용할 때 문제를 일으킬 것이다. hashCode란? hashCode()는 자바의 Object 클래스에 정의된 메서드로, 객체의 해시 코드를 반환하는 역할을 한다. 해시 코드는 객체를 해시 테이블과 같은 자료구조에 저장하거나 검색하기 위해 사용된다. hashCode() 메서드는 객체의 내부 상태를 기반으로 해시 코드를 생성한다. 서로 다른 객체는 다른 해시 코드를 가지지만, 같은 내부 상태를 가진 객체는 동일한 해시 코드를 갖는다. 따라서 hashCode()는 객체의 동등성 비교를 위해 사용되는 메..
[Java] 해시 알고리즘이란?
해시 알고리즘(Hash Algorithm)은 임의의 길이를 가진 데이터를 고정된 길이의 해시 값으로 변환하는 알고리즘이다. 이 알고리즘은 입력 데이터에 대해 일관된 해시 값을 생성하고, 입력 데이터가 조금만 변경되어도 완전히 다른 해시 값을 반환하는 특징을 갖는다. 일반적으로 해시 함수라고도 불리며, 다양한 분야에서 데이터 무결성 검사, 데이터 식별, 암호화, 암호 인증, 자료구조 등에 활용된다. 해시 알고리즘은 다양한 종류가 있으며, 대표적인 예로 MD5(Message Digest Algorithm 5), SHA(Secure Hash Algorithm) 시리즈 등이 있다. 각 해시 알고리즘은 입력 데이터를 다양한 방법으로 변환하고 해시 값을 생성한다. 이러한 알고리즘은 충돌(collision)을 최소화..
10. equals는 일반 규약을 지켜 재정의하라
equals 메서드는 재정의하기 쉬워보이지만 곳곳에 함정이 도사리고 있어서 자칫하면 끔찍한 결과를 초래한다. 문제를 회피하는 가장 쉬운 길은 아예 재정의를 하지 않는 것이다. 다음 경우에 해당한다면 equals를 재정의 할 필요가 없다. 각 인스턴스가 본질적으로 고유하다. .값을 표현하는게 아니라 동작하는 개체를 표현하는 클래스가 여기에 해당한다. Thread가 좋은 예이다. 인스턴스의 논리적 통치성을 검사할 필요가 없다. .쉽게 설명해 500원 짜리 동전 두개가 있다. 500원 짜리 동전은 두 '개' 로 인스턴스가 두개 있는 것과 같지만 둘 다 동일한 500원의 가치를 지닌다. 물건을 계산을 해야할 때 어떤 500원 짜리인지 구분해야 할 필요는 없을 것이다. 이럴 때 equals를 재정의 할 필요가 없..
9.try-finally보다는 try-with-resources를 사용하라
자바 라이브러리에는 close 메서드를 호출해 직접 닫아줘야 하는 자원이 많다. InputStream , OutStream, java.sql.Connection 등이 그런 예이다. 자원 닫기는 클라이언트가 놓치기 쉬워서 예측할 수 없는 성능 문제로 이어지기도 한다. 이런 자원 중 상당수가 안전망으로 finalizer를 활용하고는 있지만 finalizer는 그리 믿을만하지 못하다. public class Copy { private static final int BUFFER_SIZE = 8 * 1024; // 코드 9-2 자원이 둘 이상이면 try-finally 방식은 너무 지저분하다! (47쪽) static void copy(String src, String dst) throws IOException { ..
8. finalizer와 cleaner 사용을 피하라
자바는 두 가지 객체 소멸자를 제공한다. 그 중 finalizer는 예측할 수 없고, 상황에 따라 위험할 수 있어 일반적으로 불필요하다. 오동작, 낮은 성능, 이식성 문제의 원인이 되기도 한다. finalizer는 나름의 쓰임새가 몇 가지 있지만 기본적으로 사용하지 말아야 한다. cleaner는 finalizer보다 덜 위험하지만 여전히 예측할 수 없고, 느리고, 일반적으로 불필요하다. 이 두가지는 즉시 수행된다는 보장이 없다. 객체에 접근 할 수 없게 된 후 실행되기까지 얼마나 걸릴지 알 수 없다. 즉, finalizer와 cleaner로는 저때 실행되어야 하는 작업은 절대 할 수 없다. 예를 들어 시스템이 동시에 열 수 있는 파일 개수에 한계가 있을 때, 파일 닫기를 맡기면 중대한 오류를 일으킬 수 ..
7. 다 쓴 객체 참조를 해제하라
C, C++ 과 같이 메모리를 직접 관리해야하는 언어와 다르게 자바는 GC가 메모리 관리를 해준다. 하지만 메모리 관리에 신경쓰지 않아도 되는 것은 아니다. 메모리 누수가 일어나는 코드 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() { // i..
6. 불필요한 객체 생성을 피하라
똑같은 기능의 객체를 매번 생성하기보다는 객체 하나를 재사용하는 편이 나을 때가 많다. 간단한 예로 String s1 = new String(" aaa " ) ; String s2 = " aaa " ; 위 문장은 실행될 때 마다 String 인스턴스를 새로 만든다. 이 문장을 반복문이나 자주 호출되는 메서드 안에 넣으면 s1을 사용할 때 마다 " aaa " 라는 String 인스턴스가 계속 생성된다. 아래 문장은 하나의 String 인스턴스를 사용한다. 생성자 대신 정적 팩터리 메서드를 제공하는 불변 클래스에서는 싱글턴을 보장해 불필요한 객체 생성을 피할 수 있다. public class RomanNumerals { // 코드 6-1 성능을 훨씬 더 끌어올릴 수 있다! static boolean isRo..
5. 자원을 직접 명시하지 말고 의존 객체 주입을 사용하라
많은 클래스가 하나 이상의 자원에 의존한다. 예를 들어 사전에 의존하는 맞춤법 검사기 어플리케이션이 있다. 정적 유틸리티 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 코드 return dictionary.closeWo..
4. 인스턴스화를 막으려면 private 생성자를 사용하라
이따금 단순히 정적 메서드와 정적 필드만 담은 클래스를 만들어야할 때가 있다. 정적 멤버만 담은 유틸리티 클래스는 인스턴스로 만들어 쓰려고 설계한게 아니다. 하지만 생성자를 명시하지 않으면 컴파일러가 자동으로 기본 생성자를 만든다. 즉, 매개변수를 받지 않는 public 생성자가 만들어지고, 사용자는 설계자가 생성자를 의도한건지, 아니면 실수로 자동생성된건지 구분 할 수 없다. 실제로 공개된 API에서 의도치않게 인스턴스화 할 수 있게 된 클래스들이 보일 때가 있다. 추상 클래스로 만드는 것으로는 인스턴스화를 막을 수 없다. 하위 클래스를 만들어서 인스턴스화 하면 그만이다. 이것은 사용자에게 상속해서 쓰라는 오해를 불러일으킬 수 있다. 이런 오해를 막기위해 private 생성자를 추가하면 클래스의 인스턴..
함수형 인터페이스
코드를 작성하다 보면 비슷한 로직의 중복 코드가 생길 수 있고, 그로 하여금 관리 지점이 늘어나기 때문에 대부분 리팩터링의 대상으로 취급한다. 함수형 인터페이스를 사용하면 효과적으로 코드의 중복을 줄일 수 있고, 가독성을 높일 수 있다. Consumer 매개값은 있고, 반환값은 없다. 매개값을 전달받아 사용하고 아무것도 반환하지 않을 때 사용된다. 이를 소비 (Consume) 한다고 표현한다. accept 추상 메소드를 가지고 있다. 이름 기능 메소드 Consumer 객체 T를 받아 소비한다. void accept(T t) BiConsumer 객체 T와 U 두가지를 받아 소비한다. void accept(T t, U u) DoubleConsumer double 값을 받아 소비한다. void accept(d..