똑같은 기능의 객체를 매번 생성하기보다는 객체 하나를 재사용하는 편이 나을 때가 많다.
재사용하면 빠르고 세련된다?
하면 안되는 코드
String s = new String("bikini");
이 코드는 실행될 때마다 String 인스턴스를 새로 만든다. 그래서 반복문을 하면 인스턴스가 수백만 개가 만들어질 수 있다.
개선된 코드
String s = "bikini";
하나의 인스턴스를 사용한다., JVM에 동일한 문자열 리터럴이 존재한다면 그 리터럴을 재사용한다.
자바 9에서 deprecated 된 Boolean(String) 대신 Boolean.valueOf(String) 같은 static 팩토리 메소드(아이템1)를 사용할 수 있다. 생성자는 반드시 새로운 객체를 만들어야 하지만 팩토리 메소드는 그렇지 않다.
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
는 정규표현식으로 문자열 형태를 확인하는 가장 쉬운 방법이지만, 성능이 중요한 상황에서 반복해 사용하기엔 적합하지 않다.
String.matches
는 내부적으로 Pattern
객체를 만들어 쓰는데 그 객체를 만들려면 정규 표현식으로 유한 상태 기계로 컴파일 하는 과정이 필요하다. 즉 비싼 객체다.
Pattern
객체를 만들어 재사용하는 것이 좋다.
public class RomanNumber {
private static final Pattern ROMAN = Pattern.compile("^(?=.)M*(C[MD]|D?C{0,3})(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$");
static boolean isRomanNumeral(String s) {
return ROMAN.matcher(s).matches();
}
}
게으른 초기화(lazily initializing)(아이템83)를 사용해서 최적화 할 수 있지만 추천하진 않는다. 보통 지연 초기화는 측정 가능한 성능 개선 없이 구현을 복잡하게 만든다.(아이템67)
실제 작업은 뒷단 객체에 위임하고, 자신은 제2의 인터페이스 역할을 해주는 객체다.
뒷단 객체 외에는 관리할 상태가 없으므로 뒷단 객체 하나당 어댑터 하나씩만 만들어지면 충분하다.
Map
인터페이스가 제공하는 KeySet
메서드는 Map
객체 안의 키 전부를 담은 Set
뷰를 반환한다. keySet
을 호출할 때마다 새로운 객체가 나올거 같지만 사실 같은 객체를 리턴하기 때문에 리턴 받은 Set
타입의 객체를 변경하면, 결국에 그 뒤에 있는 Map
객체를 변경하게 된다.
public class UsingKeySet {
public static void main(String[] args) {
Map<String, Integer> menu = new HashMap<>();
menu.put("Burger", 8);
menu.put("Pizza", 9);
Set<String> names1 = menu.keySet();
Set<String> names2 = menu.keySet();
names1.remove("Burger");
System.out.println(names2.size()); // 1
System.out.println(menu.size()); // 1
}
}
프로그래머가 기본 타입과 박싱된 기본 타입을 섞어 쓸때 자동으로 상호 변환해주는 기술
오토방식은 기본 타입과 박스 타입의 경계가 안보이게 해주지만 그렇다고 그 경계를 없애주진 않는다.
public class AutoBoxingExample {
public static void main(String[] args) {
long start = System.currentTimeMillis();
Long sum = 0l;
for (long i = 0 ; i <= Integer.MAX_VALUE ; i++) {
sum += i;
}
System.out.println(sum);
System.out.println(System.currentTimeMillis() - start);
}
}
sum
의 변수를 Long
으로 만들었기 때문에 불필요한 Long
객체를 2^31만큼 만들어 6초 넘게 걸린다. 그래서 long
으로 바꾸면 10배이상 줄일 수 있다.
불필요한 오토박싱을 피하려면 박스 타입 보다는 프리미티브 타입을 사용해야 한다
객체 생성은 비싸니 피해야 한다.로 오해해서는 안된다. 방어적 복사가 필요한 상황에서 객체를 재사용했을 때의 피해가 필요 없는 객체를 반복 생성했을 때의 피해보다 훨씬 크다. 실패하면 버그와 보안 구멍으로 이어진다. 불필요한 객체 생성은 그저 코드 형태와 성능에만 영향을 끼친다.