불필요한 객체 생성을 피하라
똑같은 기능의 객체를 매번 생성하기보다는 객체 하나를 재사용하는 편이 나을 때가 많다.
예를 들어,
String s = new String("haha"); // (1)
String s = "haha"; // (2)
(1) 방식을 사용한다면 계속해서 String 인스턴스를 생성하지만 (2) 방식을 사용한다면 모든 코드가 같은 객체를 재사용하는 것을 보장한다.
정적 팩터리 메서드를 제공하는 불변 클래스에서는 정적 팩터리 메서드를 사용해 불필요한 객체 생성을 피할 수 있다. 예를 들어 Boolean(String)
생성자 대신 Boolean.valueOf(String)
과 같은 팩터리 메서드를 사용하는 것이 좋다.
생성 비용이 아주 비싼 객체도 더러 있다. 이런 비싼 객체가 반복해서 필요하다면 캐싱하여 재사용하기를 권장한다.
static boolean isRomanNumeralSlow(String s) {
return s.matches("^(?=.)M*(C[MD]|D?C{0,3})"
+ "(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$");
}
위 처럼 문자열이 유효한 로마 숫자인지를 확인하는 메서드를 작성한다고 할 때, 내부에서 만드는 정규표현식 용 Pattern 인스턴스는 한 번 쓰고 버려지는 가비지 컬렉션 대상이 된다.
public class RomanNumerals {
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();
}
}
따라서 클래스 초기화 과정에서 직접 인스턴스를 생성해 캐싱해두고 isRomanNumeral
메서드가 호출될 때마다 이 인스턴스를 재사용한다.
@Test
public void maptest() throws Exception{
Map<String, String> map = new HashMap<>();
map.put("hi","test");
Set<String> string1 = map.keySet();
Set<String> string2 = map.keySet();
Assertions.assertThat(string1).isEqualTo(string2); //success
}
Map 인터페이스의 KeySet메서드는 Set 인스턴스를 반환한다. KeySet 메서드는 같은 Map 인스턴스를 대변하므로 반환된 객체 중 하나를 수정하면 다른 모든 객체가 바뀐다. 따라서 여러 개를 만들어도 상관 없지만 그럴 필요도 없고 이득도 없다.
오토박싱은 프로그래머가 기본 타입과 박싱된 기본 타입을 섞어 쓸 때 자동으로 상호 변환해주는 기술이다.
private static long sum() {
Long sum = 0L;
for(long i=0; i<=Integer.MAX_VALUE; i++) {
sum += i; //Long 타입에 long 타입을 더하고 있다. 즉 오토박싱을 해준다.
}
return sum;
}
다음과 같은 경우, sum이 Long으로 선언되어 있으므로 long 타입인 i가 더해질 때마다 인스턴스가 생성된다. 즉, 박싱된 기본 타입보다는 기본 타입을 사용하고, 의도치 않은 오토박싱이 숨어들지 않도록 주의해야한다.