clone 메서드가 선언된 곳이 Cloneable이 아닌 Object이며, 접근 제한자가 protected 이기 때문에 Cloneable을 구현하는 것만으로는 외부 객체에서 clone 메서드를 호출할 수 없다. 이런 문제점에도 불구하고 Cloneable 방식은 널리 쓰인다. 이에 대해 알아보자. Cloneable 인터페이스 Object의 protected 메서드인 clone의 동작 방식을 결정 Cloneable을 구현한 클래스의 인스턴스에서 clone을 호출하면 그 객체의 필드들을 하나하나 복사한 객체를 반환, 그렇지 않은 클래스의 인스턴스에서 호출하면 CloneNotSupportedException 을 던짐 clone 메서드의 허술한 일반 규약 x.clone() != x 는 참이어야한다. → 복사한 객..
Object의 기본 toString 메서드가 우리가 작성한 클래스에서 적합한 문자열을 반환하는 경우는 거의 없다. 단순히 클래스 이름@16진수로_표시한_해시코드 를 반환한다. toString은 유익한 정보를 반환할 수 있도록 하위 클래스에서 재정의해야한다. (디버깅에도 용이) toString은 그 객체가 가진 주요 정보 모두를 반환하는게 좋다. /* * 전화번호의 문자열 표현을 반환합니다. * 이 문자열은 XXX-YYYY-ZZZZ 형태의 11글자로 구성됩니다. * XXX는 지역코드, YYYY는 접두사, ZZZZ는 가입자 번호입니다. */ @Override public String toString() { return String.format("%03d-%04d-%04d", areaCode, prefix, ..
equals를 재정의한 클래스 모두에서 hashCode도 재정의해야한다. 그렇지 않으면 HashMap이나 HashSet 같은 컬렉션 원소로 사용할때 문제가 발생한다. Object 명세 규약 equals 비교에 사용되는 정보가 변경되지 않았으면, hashCode 를 호출할때 항상 같은 값을 반환해야한다. equals(Object)가 두 객체가 같다고 판단했다면, 두 객체의 hashCode 반환 값이 같아야한다. 즉, 논리적으로 같은 객체는 같은 해시코드를 반환해야한다. equals(Object)가 두 객체를 다르다고 판단했더라도, 두 객체의 hasCode 반환 값이 다를 필요는 없다. 단, 다른 객체에 대해서는 다른 값을 반환해야 해시테이블 성능이 좋아진다. Map m = new HashMap(); m.p..
Object 클래스의 equals 메서드는 재정의하지않고 그냥두면 그 클래스는 오직 자기 자신과만 같게 된다. equals 재정의를 추천하지 않는 상황 1) 각 인스턴스가 본질적으로 고유할때 값을 표현하는게 아니라 동작하는 개체를 표현하는 클래스일때 ex)Thread 클래스 2) 인스턴스의 논리적 동치성을 검사할 일이 없을때 클라이언트가 논리적 동치성 검사 방식을 원하지 않거나 필요하지 않으면 재정의할 필요가 없다. 3) 상위 클래스에서 재정의한 equals가 하위 클래스에도 들어맞을때 ex) Set 구현체는 AbstractSet로부터, List 구현체들은 AbstractList로부터, Map 구현체들은 AbstractMap으로부터 equals를 상속받아 그대로 사용 4) 클래스가 private이거나 p..
자바 라이브러리에는 close 메서드로 직접 닫아줘야하는 자원이 많다. ex) InputStream, OutputStream, java.sql.Connection 자원 닫기는 예측할 수 없는 성능 문제로 이어질수있다. try-finally 전통적으로 자원을 닫는 수단으로 사용하였다. 예외가 발생하거나 메서드에서 반한되는 경우를 포함해서 말이다. //자원이 하나인 경우 static String firstLineOfFile(String path) throws IOException { BufferedReader br = new BufferedReader(new FileReader(path)); try { return br.readLine(); } finally { br.close(); } } //자원이 둘 이..
finalizer와 cleaner 자바가 제공하는 두가지 객체 소멸자다. finalizer는 예측할 수없고 위험할 수 있어 일반적으로 불필요하여 자바9 이후 deprecataed 되었다. 그 대안인 cleaner는 finalizer보단 덜하지만 여전히 예측할 수 없고 느려서 불필요하다. c++의 파괴자(destructor) 와는 다른 개념이다. c++에서 파괴자는 비메모리 자원을 회수하는 용도르 쓰이지만, 자바에서는 try-with-resources나 try-finally를 사용하여 해결한다. finalizer와 cleaner의 부작용 finalizer와 cleaner는 즉시 수행된다는 보장이 없어 제때 실행되어야 하는 작업은 절대 할 수 없다. ex) 파일 닫기를 finalizer와 cleaner에 맡..
자바의 가비지 컬렉터는 알아서 다 쓴 객체를 회수해간다. 그럼에도 메모리 관리에 신경써야한다. public class Stack { private static final int DEFAULT_INITAL_CAPACITY = 16; private Obejct[] elements; private int size = 0; public Stack() { elements = new Object[DEFAULT_INITAL_CAPACITY]; } public void push(Object e) { ensureCapacity(); elements[size++]; } public Object pop() { if (size == 0) { throw new EmptyStackException(); } return eleme..
객체의 재사용 똑같은 기능의 객체를 매번 생성하는 것보다 객체 하나를 재사용하는 것이 나을 때가 많음 생성자 대신 정적 팩터리 메서드를 사용하여 불필요한 객체 생성을 피할 수 있음 ex) Boolean.valueOf(String) 그런데 생성 비용이 아주 비싼 객체도 있다. 이런 객체를 반복해서 필요할 때는 캐싱해서 재사용을 권장한다. static boolean isRomanNumeral(String s) { return s.matches("^(?=.)M*(C[MD]|D?C{0,3})" + "(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$"); } 위의 코드는 String.matches 로 정규표현식을 통해 문자열 형태를 확인하는 코드이다. 그런데, 이 메서드가 내부에서 만드는 Pattern..