JAVA/이펙티브 자바
16. public 클래스에서는 public 필드가 아닌 접근자 메서드를 사용하라
Getter&Setter로 흔히 불리는 접근자와 변경자 메서드 이다. public 클래스라면 패키지 바깥에서 접근할 수 있는 접근자를 제공함으로써 클래스 내부 표현 방식을 언제든 바꿀 수 있는 유연성을 얻을 수 있다. public 클래스가 필드를 공개하면 이를 사용하는 클라이언트가 생겨나고, 내부 표현 방식을 마음대로 바꿀 수 없게 된다. https://kimfk567.tistory.com/14 getter,setter를 사용하는 이유와 객체의 부품화 (in VO,DTO) 문득 getter,setter 제네레이터를 통해 VO,DTO를 만들고 왜 이렇게 사용할까?하는 생각이 들었다. getter ,setter를 만드는건 제네레이터가 해주기 때문에 쉽게 만들 수 있고 값을 받아오고 입력해줘서 DB kimfk..
15. 클래스와 멤버의 접근 권한을 최소화하라
어설프게 설계된 컴포넌트와 잘 설계된 컴포넌트의 가장 큰 차이는 바로 클래스 내부 데이터와 내부 구현 정보를 외부 컴포넌트로부터 얼마나 잘 숨겼느냐다. 잘 설계된 컴포넌트는 모든 내부 구현을 완벽히 숨겨, 구현과 API를 깔끔히 분리한다. 오직 API를 통해서만 다른 컴포넌트와 소통하며 서로의 내부 동작 방식에는 전혀 개의치 않는다. 이것이 바로 정보 은닉, 캡슐화이다. 캡슐화의 장점은 정말 많다. 그 중 대부분은 시스템을 구성하는 컴포넌트들을 서로 독립시켜서 개발, 테스트, 최적화, 적용, 분석, 수정을 개별적으로 할 수 있게 해주는 것과 연관되어 있다. 여러 컴포넌트를 병렬로 개발할 수 있기 때문에 시스템 개발 속도를 높인다. 시스템 관리 비용을 낮춘다. 각 컴포넌트를 더 빨리 파악하여 디버깅할 수 ..
14. Comparable을 구현할지 고려하라
이번에는 Comparable 인터페이스의 compareTo() 이다. compareTo() 함수에는 "문자열의 비교" 와 "숫자의 비교" 두 방식이 존재한다. 숫자의 비교 같은 경우는 단순히 크다(1), 같다(0), 작다(-1) 의 관한 결과값을 리턴해주는 반면 문자열의 비교 같은 경우는 같다(0) 외엔 문자열의 아스키 코드 값의 차이를 리턴한다. compareTo는 단순 동치성 비교에 더해 순서까지 비교할 수 있고 제네릭하다. Complarable을 구현했다는 것은 그 클래스의 인스턴스들에는 자연적은 순서가 있음을 뜻한다. 그래서 Comparable을 구현한 객체들의 배열은 다음처럼 손쉽게 정렬할 수 있다. Arrays.sort(a); 검색, 극단값 계산, 자동 정렬되는 컬렉션 관리도 역시 쉽게 할 수..
13. clone 재정의는 주의해서 진행하라
Cloneable은 복제해도 되는 클래스임을 명시하는 용도의 믹스인 인터페이지만, 아쉽게도 의도한 목적을 제대로 이루지 못했다. 가장 큰 문제는 clone 메서드가 선언된 곳이 Cloneable이 아닌 Object이고, 그마저도 protected 라는 데 있다. 그래서 Closeable을 구현하는 것만으로는 외부 객체에서 clone 메서드를 호출할 수 없다. 리플렉션을 사용하면 가능하지만, 100% 성공하는 것도 아니다. 해당 객체가 접근이 허용된 clone 메서드를 제공한다는 보장이 없기 때문이다. 하지만 이를 포함한 여러 문제점에도 불구하고 Cloneable 방식은 널리 쓰이고 있어서 잘 알아두는 것이 좋다. Cloneable 인터페이스는 Object의 protected 메서드인 clone의 동작 방..
12. toString을 항상 재정의하라
Object의 기본 toString 메서드가 우리가 작성한 클래스에 적합한 문자열을 반환하는 경우는 거의 없다. 이 메서드는 PhoneNumber@abbd처럼 단순히 클래스_이름@16진수로_표시한_해시코드 를 반환할 뿐이다. toString의 규약은 "모든 하위 클래스에서 이 메서드를 재정의하라" 라고 한다. toString을 잘 구현한 클래스는 사용하기에 훨씬 편하고 디버깅하기 편해진다. toString 메서드는 객체를 println, printf, 문자열 연결 등, 혹은 디버거가 객체를 출력할 때 자동으로 호출된다. 직접 호출하지 않아도 어디선가 쓰일 것이다. 그렇기에 실전에서 toString은 보안에 문제가 되지 않는 한 그 객체가 가진 주요 정보 모두를 반환하는게 좋다. toString을 구현할 ..
11. equals를 재정의하려거든 hashCode도 재정의하라
equals를 재정의한 클래스 모두에서 hashCode도 재정의해야 한다. 그렇지 않으면 hashCode 일반 규약을 어기게 되어 해당 클래스의 인스턴스를 HashMap이나 HashSet 같은 컬렉션의 원소로 사용할 때 문제를 일으킬 것이다. hashCode란? hashCode()는 자바의 Object 클래스에 정의된 메서드로, 객체의 해시 코드를 반환하는 역할을 한다. 해시 코드는 객체를 해시 테이블과 같은 자료구조에 저장하거나 검색하기 위해 사용된다. hashCode() 메서드는 객체의 내부 상태를 기반으로 해시 코드를 생성한다. 서로 다른 객체는 다른 해시 코드를 가지지만, 같은 내부 상태를 가진 객체는 동일한 해시 코드를 갖는다. 따라서 hashCode()는 객체의 동등성 비교를 위해 사용되는 메..
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..