변수의 초기화

양성빈·2022년 6월 15일

참고
자바의 정석

변수의 초기화

변수의 초기화

앞서 이야기를 했지만 변수를 선언하고 처음으로 값을 저장하는 것을 변수의 초기화라고 한다. 왠만하면 변수를 선언과 동시에 적절한 값으로 초기화하는게 좋다. 하지만, 멤버변수같은 경우는 초기화를 진행하지 않아도 자동으로 변수의 자료형의 기본값으로 초기화를 해주어 초기화를 따로 할 필요는 없지만, 지역변수는 사용하기 전에 반드시 초기화를 해야한다.

class InitTest {
	int x; // 인스턴스 변수
    int y = x; // 인스턴스 변수
    
    void method() {
    	int i; // 지역변수
        int j = i; // ERROR: 지역변수를 초기화를 안하고 사용
    }
}

멤버 변수 (클래스 변수, 인스턴스 변수)와 배열의 초기화는 선택적이지만, 지역변수의 초기화는 필수적이다.

멤버변수의 초기화 방법
1. 명시적 초기화
2. 생성자
3. 초기화 블럭
      - 인스턴스 초기화 블럭: 인스턴스 변수를 초기화 하는데 사용
      - 클래스 초기화 블럭: 클래스변수를 초기화 하는데 사용

명시적 초기화

변수를 선언과 동시에 초기화하는 것을 명시적 초기화라고 한다. 가장 기본적인 방법으로 여러 초기화 방법중에 가장 우선순위로 생각해야하는 초기화 방법이다. 하지만, 초기화 작업을 할때 좀 더 복잡한 로직으로 초기화 해야할 것 같으면 초기화 블럭 또는 생성자를 이용해야 한다.

class Car {
	int door = 4;
    Engine e = new Engine();
}

초기화 블럭

초기화 블럭에는 클래스 '초기화 블럭'과 '인스턴스 초기화 블럭' 2가지 종류가 있다.

클래스 초기화 블럭: 클래스변수의 복잡한 초기화에 사용된다. 단 1번만 호출된다.
인스턴스 초기화 블럭: 인스턴스 변수의 복잡한 초기화에 사용된다. 인스턴스 생성마다 호출된다.

인스턴스 초기화 블럭은 클래스 내에 단순히 {} 블럭을 만들고 안에 초기화 작업을 해주면 되며, 클래스 초기화 블럭은 {} 앞에 static 키워드를 붙여주면 된다.

초기화 블럭 내에서 초기화 작업이라 해서 변수 초기화만 생각을 하는데 그렇지 않고 조건문, 반복문, 예외처리를 자유롭게 사용하면 된다. 즉, 명시적 초기화로 도저히 초기화가 힘들 때 2순위로 생각하면 좋을 것 같다.

class InitBlock {
	static {
    	// 클래스 초기화 블럭 
    }
    
    {
    	// 인스턴스 초기화 블럭
    }
}

클래스 초기화 블럭은 클래스가 메모리에 처음 로딩될 때, 한번만 수행되며, 인스턴스 초기화 블럭은 생성자와 같이 인스턴스를 생성할 때마다 수행된다. 그리고 생성자보다 인스턴스 초기화 블럭이 먼저 수행된다.

💡 참고
클래스가 처음 로딩될 때, 클래스 변수들이 자동적으로 메모리에 만들어지고, 곧바로 클래스 초기화 블럭이 클래스 변수들을 초기화하게 된다.

여기서 잠시 헷갈리는 부분이 생길 것이다. 인스턴스 변수는 생성자에서 초기화하면 안될까?
정답은 초기화 해도 된다. 그러면 인스턴스 변수는 인스턴스 초기화 블럭이든 생성자든 아무것을 사용해도 될까?
그것은 아니다. 인스턴스 변수의 초기화는 주로 생성자를 사용하고, 인스턴스 초기화 블럭은 모든 생성자에 공통으로 수행돼야 하는 코드를 넣는데 사용한다.

// 인스턴스 초기화 블럭을 사용 안한 경우
Car() {
	++count;
    serialNo = count;
	color = "white";
    gearType = "Auto";
}

Car (String color, String gearType) {
	++count;
    serialNo = count;
	this.color = color;
    this.gearType = gearType;
}
// 인스턴스 초기화 블럭을 사용 한 경우
{
	++count;
    serialNo = count;
}

Car() {
	color = "white";
    gearType = "Auto";
}

Car (String color, String gearType) {
	this.color = color;
    this.gearType = gearType;
}

위의 코드를 보면 알겠지만, 공통적인 초기화 작업을 인스턴스 변수로 빼니 중복된 코드가 제거됨으로 코드의 신뢰성을 높여주고, 오류의 발생가능성을 줄여준다. 즉, 재사용성을 높이는것. 이것이 객체지향의 지향점이다.

멤버변수의 초기화 시기와 순서

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

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

⚠️ 참고
클래스 로딩 시기는 JVM의 종류애 따라 다를 수 있는데, 클래스가 필요할 때 바로 메모리에 로딩되도록 설계가 되어 있는 것도 있고, 실행효율을 높이기 위해서 사용될 클래스들을 프로그램이 시작될 때 미리 로딩하도록 되어있는 것도 있다.

그러면 좀 더 자세히 초기화 시기와 순서를 알아보자.

class InitTest {
	static int cv = 1;
    int iv = 1;
    
    static {
    	cv = 2;
    }
    
    {
    	iv = 2;
    }
    
    InitTest() {
    	iv = 3;
    }
}

위의 코드가 존재하고, new InitTest();를 호출 할 때 어떻게 초기화가 진행되는지 살펴보자.

  1. cv가 메모리 (메소드 영역)에 생성되고, cv에는 int형의 기본값인 0이 cv에 저장된다.
  2. 그 다음에 명시적 초기화에 의해서 cv가 1에 저장된다.
  3. 마지막으로 클래스 초기화 블럭이 수행되면서 cv에 2가 저장된다.
  4. InitTest 클래스의 인스턴스가 생성되면서 iv가 메모리 (heap)에 존재한다.
    iv 역시 int형 변수이므로 기본값 0에 저장된다.
  5. 명시적 초기화에 의해 iv에 1이 저장된다.
  6. 인스턴스 초기화 블럭이 수행되어 iv에 2가 저장된다.
  7. 마지막으로 생성자가 수행되어 iv에는 3이 저장된다.
profile
모든 것을 즐길줄 아는 개발자입니다!

0개의 댓글