티스토리 뷰

JAVA

불필요한 객체를 만들지 말자

세댕댕이 2022. 7. 17. 00:16

5. 불필요한 객체는 만들지 말라

1) 기능적으로 동일한 객체는 필요할 때마다 만드는 것보다 재사용하는 것이 더 낫다.

- 변경 불가능한(immutable) 객체는 언제든지 재사용할 수 있다.

- 변경 불가능한 클래스(final class. 대표적으로 String, Math 등...) 의 경우, 생성자보다는 정적 팩토리 메서드를 이용하면 불필요한 객체 생성을 막을 수 있다.

 

메서드를 호출할 때마다 생성되는 객체가 많아 비용이 높다면 정적 초기화 블록(Static initializer)를 사용해보자

 

===============

* 필드를 초기화 하는데에는 3가지 방법이 있다

1. 명시적 초기화 (= 선언과 동시에 초기화하는 것)

2. 생성자 초기화

3. 초기화 블록을 통한 초기화

 

초기화 블록은 또 다시 인스턴스 초기화 블록과 클래스 초기화 블록으로 나뉜다.

인스턴스 초기화 블록은 중괄호 { } 를 이용해서 정의.

- 인스턴스가 생성될 때마다 실행된다 (생성자보다 먼저 실행된다!!)

- 생성자가 여러개 있을 경우, 모든 생성자에서 공통으로 수행되는 부분을 인스턴스 초기화 블록으로 분리시켜서 코드의 중복을 쪼금 막는데 사용될 수 있다.

 

클래스 초기화 블록은 인스턴스 초기화 블록 앞에 static을 붙여서 정의한다.

- 클래스가 처음으로 메모리에 로딩되는 시점에 딱 한번 실행된다.

-> 객체 생성 이전에 수행되므로 인스턴스 변수에 접근할 수 없음.

- 생성자로는 불가능한 클래스(static) 변수의 초기화를 진행할 때 사용할 수 있음 -> 명시적 초기화로는 복잡할 때 사용

(생성자에서 클래스 변수에 접근하는 것은 초기화가 아니라.. 그냥 기존 값을 변경하는 것)

 

* 클래스 변수는 객체 생성 없이도 사용 가능해야 하므로 생성자 초기화를 할 수 없다

(+ 내부에 인스턴스 변수/메서드도 사용할 수 없고, this 키워드도 사용 불가)

 

* 필드의 초기화 순서(우선순위. 저 --> 고)

클래스 변수: 기본값(0 or null) -> 명시적 초기화 -> 클래스 초기화 블록

인스턴스 변수: 기본값 -> 명시적 초기화 -> 인스턴스 초기화 블록 -> 생성자

 

public class User {

    private static final Date BOOM_START;
    private static final Date BOOM_END;

    static { // 정적 초기화(클래스 초기화) 블록
        Calendar cal = Calendar.getInstance(TimeZone.getDefault());
        cal.set(1950, Calendar.JANUARY, 1, 0, 0, 0);
        BOOM_START = cal.getTime();
        cal.set(1955, Calendar.DECEMBER, 31, 24, 59, 59);
        BOOM_END = cal.getTime();
    }

    ...
}

===============

 

2) 생각지 못한, 의도치 않은 자동 객체화(AutoBoxing)가 발생하지 않도록 유의해라

- 딱히 이유가 없다면 primitive 타입을 사용하는 것이 속도나 메모리 면에서 좋다

-> primitive 타입 데이터는 스택 영역만 사용하는데 반해, Wrapper 타입 객체는 힙 + 스택 영역 둘 다 사용함

 

* AutoBoxing: primitive 타입 자료형을 Wrapper 클래스의 객체로 자동으로 바꿔주는 것 

<-> Unboxing: Wrapper 클래스 타입을 primitive 타입으로 자동으로 바꿔주는 것

 

* 토막상식: Wrapper 클래스는 산술연산을 위해 정의된 클래스가 아니므로, 인스턴스에 저장된 값을 변경할 수 없다.

- 산술 연산이 되는 것은 사실 내부에서 Unboxing 후 primitive 타입으로 변환 및 연산하고 다시 Autoboxing 해주는 것.

- 값이 변경되는 것이 아니라, 새로운 객체를 만들어서 통으로 갈아끼워지는 것이다. (객체는 불변해야 해)

- "Wrapper 클래스끼리" == 비교하는 것은 내부의 값을 비교하는 것이 아니라 주소값을 비교하는 것.

-> 내부 값을 비교하기 위해서는 equals() 메서드를 이용해야 함.

    // time = 9923ms
    public static void main(String[] args) {
        long start = System.currentTimeMillis();
        Long sum = 0L;
        for(long i = 0; i < Integer.MAX_VALUE; i++) {
            sum += i; // AutoBoxing 발생 - 신규 객체 생성
        }
        long end = System.currentTimeMillis();
        System.out.println("sum = " + sum);
        System.out.println("time = " + (end - start) + "ms");
    }
    
    //////////////////////////////
    
    // time = 1559ms
    public static void main(String[] args) {
        long start = System.currentTimeMillis();
        long sum = 0L;
        for(long i = 0; i < Integer.MAX_VALUE; i++) {
            sum += i;
        }
        long end = System.currentTimeMillis();
        System.out.println("sum = " + sum);
        System.out.println("time = " + (end - start) + "ms");
    }

꽤 성능차이가 유의미하게 발생한다

 

 

불필요한 객체를 만들지 마라고 해서 무조건 객체 생성을 피하라는 것은 아님. 알잘딱깔센

= "재사용이 가능하다면" 굳이 새로운 객체를 만들지 마라.

(근데 새로운 객체를 만들어야 한다면, 기존 객체는 재사용하지 마라. (추후 다룸))

 

  

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/01   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함