이펙티브 자바 #item6 불필요한 객체 생성을 피하라

임현규·2023년 1월 3일
0

이펙티브 자바

목록 보기
6/47
post-thumbnail
post-custom-banner

primitive의 객체 타입이나 String 타입을 생성할 때 new를 사용하지 말자

Java의 String은 String Constant Pool 영역에 저장된다. 그래서 동일한 String을 호출하는 경우 매번 인스턴스를 생성하는 것이 아니라 pool에 저장된 문자열을 가져온다. Integer나 Long의 경우에도 constant pool에 저장한다. 그렇기에 new 대신 직접 입력하면 java compiler는 알아서 Integer.valueOf(123); 과 같은 형태로 바꾸어서 contant pool 영역에 값이 있다면 그 값을 호출한다.

class SolutionTest {

    @Test
    void test() {
        String s1 = "abc123123";
        String s2 = "abc123123";
        System.out.println(s1.hashCode());
        System.out.println(s2.hashCode());
    }
}

hashCode가 동일함을 알 수 있다.

비싼 인스턴스 비용을 정적으로 처리해라

Pattern matching의 경우 Pattern 인스턴스를 매 번 생성하고 Matcher를 이용해 정규식을 처리한다. 문제는 Pattern 인스턴스 비용이 생각보다 비싸다는 것이다.

Value Object나 문자열 입력의 유효성 검사를 활용할 때 특히 정규식을 많이 활용할 수 있는데 이럴 때 어떤 방법이 좋은 지 알아보자

public class Student {

    private final String name;

    public Student(String name) {
        validateName(name);
        this.name = name;
    }

    private static void validateName(String name) {
        Pattern pattern = Pattern.compile("^[가-힣]{2,5}$");
        if (!pattern.matcher(name).matches()) {
            throw new IllegalArgumentException("옳바르지 않은 이름 입력.");
        }
    }
}

해당 코드의 경우 인스턴스 생성시 name 필드에 들어갈 문자열의 유효성 검사를 한다. 문제는 매번 Pattern 인스턴스를 생성한다는 것이다. 곰곰히 생각하면 Student의 인스턴스를 생성할 때 매 번 생성하더라도 Pattern은 항상 동일하기 때문에 매 번 생성할 이유가 없다. 이런 경우 static을 활용한다.

public class Student {

    private static final Pattern NAME = Pattern.compile("^[가-힣]{2,5}$");
 
    private final String name;

    public Student(String name) {
        validateName(name);
        this.name = name;
    }
    
    private static void validateName(String name) {
        if (!NAME.matcher(name).matches()) {
            throw new IllegalArgumentException("옳바르지 않은 이름 입력.");
        }
    }
}

매번 호출하지 않아도 되기 때문에 코드가 간결해지고 보기 좋아졌다. 훨씬 좋아진 코드라 볼 수 있다.

의도치 않은 오토박싱에 유의해라

    @Test
    void test() {
        Long sum = 0L;
        for (long i =0; i < 1_000_000; ++i) {
            sum += i;
        }
    }

위의 코드를 보면 Sum은 0 ~ 100만 까지 더한 누적합이다. 그러나 sum의 타입은 boxing 타입인 Long인데 이 경우 문제를 일으킬 수 있다. Long은 연산 타입을 지원하지 않는다. 그렇기 때문에 JVM은 자동으로 Long은 long으로 unBoxing을 하고 연산한 후 다시 boxing을 한다. 매번 연산할 때마다 언박싱과 박싱이 일어난다는 것은 100만번의 연산마다 인스턴스를 새로 생성한다는 것이다. 이 방식은 굉장히 느리다. 이런 실수를 하지 않도록 한다.

무분별하게 객체 생성을 피하고자 pool을 사용하지 말자

Pool을 이용한 방법은 코드의 가독성을 떨어뜨리고 복잡해진다. 인스턴스 생성이 무겁거나, 성능 하락의 요인이 된다면 그 때 pool을 활용해도 늦지 않다. 대부분 pool을 활용하는 경우는 thread나 db connection 경우가 많은데 이 경우는 라이브러리에서 잘 지원하므로 굳이 구현할 필요가 없다.

profile
엘 프사이 콩그루
post-custom-banner

0개의 댓글