느리더라도 꾸준히

[Java기본]생성자와 필드의 초기화 시점 및 순서 본문

Java

[Java기본]생성자와 필드의 초기화 시점 및 순서

테디규 2022. 11. 20. 10:56

정리

생성자와 필드 선언 시점 과 순서를 이해하여, 문제를 해결했다.

초기화 시점

  • 클래스변수의 초기화시점 : 클래스가 처음 로딩될 때 단 한번 초기화 된다.
  • 인스턴스변수의 초기화시점 : 인스턴스가 생성될 때마다 각 인스턴스별로 초기화가 이루어진다.

초기화 순서

  • 클래스변수의 초기화순서 : 기본값 -> 명시적초기화 -> 클래스 초기화 블럭
  • 인스턴스변수의 초기화순서 : 기본값 -> 명시적초기화 -> 인스턴스 초기화 블럭 -> 생성자

1.문제상황

저장소

public class Repository {
    ArrayList<Integer> al = new ArrayList<>(){{
        add(1);
        add(2);
        add(3);
        add(4);
        add(5);
    }};

    public int sumAll(ArrayList<Integer> al) {
        int sum = 0;
        for (int x : al) {
            sum += x;
        }
        return sum;
    }

    public ArrayList<Integer> getAl() {
        return al;
    }
}

간단한 list를 저장하고 출력하고 있는 Repository가 있다.

서비스로직

public class Service {
    Repository repository;

    public Service(Repository repository) {
        this.repository = repository;
    }

    int repoSum = repository.sumAll(repository.getAl());

//    기능2;

//    기능3;

//    기능4;

    public static void main(String[] args) {
        Service service = new Service(new Repository());
        System.out.println(service.repoSum);
    }
}

Service 클래스는 Repository 객체를 생성자를 통해 주입받고 있다.

repoSum 인스턴스 변수는 저장소에 존재하는 모든 값들을 더한 값을 가지며, 해당 값은 여러 기능(메서드)에서 사용할 목적으로 필드 선언을 해주었다.

출력결과

Exception in thread "main" java.lang.NullPointerException
    at Service.<init>(Service.java:8)
    at Service.main(Service.java:11)

repoSum 에 NPE가 발생하고 있다.

왜 이런 것일까?

왜 인지를 알기위해서는 Java 클래스 내부의 멤버 변수들의 초기화 순서를 알아야한다.

클래스 멤버 변수들의 초기화 순서

Java 클래스 멤버 변수들의 초기화 순서

  1. static 변수 선언부
  • 클래스가 로드 될 때 (메모리 모델상 Method area 에 올라감)
  • 그러므로 변수가 제일 먼저 초기화 됨
  1. 필드 변수 선언부
  • 객체 생성 될 때 (메모리 모델상 Heap area에 올라감)
  • 생성자 block 보다 앞서 초기화 함
  1. 생성자 block
  • 객체 생성 될 때 (메모리 모델상 Heap area에 올라감)
  • JVM이 내부적으로 locking (thread safe 영역임)
  • 필드 변수 중 final 변수의 가시화는 (다른 스레드에 공개하는 시점은) 생성자 block이 끝난 다음.
  • 필드 변수 선언부에서 이미 초기화 되었다면 그 값들은 덮어 씀

초기화 시점

  • 클래스변수의 초기화시점 : 클래스가 처음 로딩될 때 단 한번 초기화 된다.
  • 인스턴스변수의 초기화시점 : 인스턴스가 생성될 때마다 각 인스턴스별로 초기화가 이루어진다.

초기화 순서

  • 클래스변수의 초기화순서 : 기본값 -> 명시적초기화 -> 클래스 초기화 블럭
  • 인스턴스변수의 초기화순서 : 기본값 -> 명시적초기화 -> 인스턴스 초기화 블럭 -> 생성자

위의 코드는 생성자 초기화 순서보다 인스턴스 변수 초기화가 더 빠르기 때문에, repoSum을 선언할때의 repository 참조변수는 null을 담고 있는 저장소였기 때문에 NPE가 발생하는 것이었다.

2. 문제 해결

수정 코드

public class Service {
    Repository repository;

    public Service(Repository repository) {
        this.repository = repository;
    }

//    int repoSum = repository.sumAll(repository.getAl());

//    기능2;

//    기능3;

//    기능4;

    public static void main(String[] args) {
        Service service = new Service(new Repository());
        int repoSum = service.repository.sumAll(service.repository.getAl());
        System.out.println(repoSum);
    }
}

출처

https://eyevsky.tistory.com/entry/Java-클래스-멤버-변수들의-초기화-순서

Comments