[아이템 6] 불필요한 객체 생성을 피하라

Jimin Lim·2022년 2월 28일
0

Effective Java

목록 보기
6/38
post-thumbnail

아이템 6

불필요한 객체 생성을 피하라

똑같은 기능의 객체를 매번 생성하기보다는 객체 하나를 재사용하는 편이 나을 때가 많다.

예를 들어,

String s = new String("haha"); // (1)
String s = "haha"; // (2)

(1) 방식을 사용한다면 계속해서 String 인스턴스를 생성하지만 (2) 방식을 사용한다면 모든 코드가 같은 객체를 재사용하는 것을 보장한다.

Static Factory Method

정적 팩터리 메서드를 제공하는 불변 클래스에서는 정적 팩터리 메서드를 사용해 불필요한 객체 생성을 피할 수 있다. 예를 들어 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가 더해질 때마다 인스턴스가 생성된다. 즉, 박싱된 기본 타입보다는 기본 타입을 사용하고, 의도치 않은 오토박싱이 숨어들지 않도록 주의해야한다.

profile
💻 ☕️ 🏝 🍑 🍹 🏊‍♀️

0개의 댓글