성장일기

초기화 블럭(initialization block)

자바에서 초기화 블럭에는 클래스 초기화 블럭과 인스턴스 초기화 블럭 두 가지 종류가 있다. 클래스 초기화 블럭은 클래스 변수의 초기화 블럭에 사용되고, 인스턴스 초기화 블럭은 인스턴스 변수의 초기화 블럭에 사용된다.

 

  • 클래스 초기화 블럭 : 클래스 변수의 복잡한 초기화에 사용
  • 인스턴스 초기화 블럭 : 인스턴스 변수의 복잡한 초기화에 사용

 

초기화 블럭을 이용하지 않고도 클래스, 인스턴스 변수들을 초기화 할 수 있지만, 초기화 블럭을 사용하는 이유는 초기화 작업이 복잡하여 명시적 초기화만으로는 부족한 경우를 위해서 제공한다.

 

 초기화 블럭을 작성하는 방법의 경우 인스턴스 초기화 블럭은 단순히 클래스 내에 블럭{}을 만들고 그안에 코드를 작성하기만 하면 되고, 클래스 초기화 블럭은 인스턴스 초기화 블럭 앞에 단순히 static을 덧붙이기만 하면 된다. 또한 초기화 블럭 내에는 메소드 내에서와 같이 조건문, 반복문, 예외처리구문 등을 자유롭게 사용할 수 있다.

class InitBlock{
    static{/*클래스 초기화 블록*/};
    
    {/*인스턴스 초기화 블록*/}
    
    //..
}

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

 

class Car{
    static int count = 0;
    int serialNo;
    String color;
    String gearType;

    Car() {
        count++;
        serialNo = count;
        color = "white";
        gearType = "auto";
    }

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

public class TestFree {
    public static void main(String[] args) throws IOException {
        Car car1 = new Car();
        Car car2 = new Car("black", "auto");
        Car car3 = new Car();

        System.out.println(car1.serialNo);  //1
        System.out.println(car2.serialNo);  //2
        System.out.println(car3.serialNo);  //3
    }
}

 Car클래스의 멤버로 클래스 변수 count를 선언하여 인스턴스가 생성 될 때마다 생성자에서 count를 증가시켜 seialNo를 count값으로 초기화 해주는 과정이다. 코드를 살펴보면 두 생성자 내에 count를 증가시키고 serialNo을 초기화 하는 과정이 중복된 것을 알 수 있다.

 

 물론 생성자 오버로딩과 this()를 이용하면 중복을 줄일 수 있긴 하지만 위와 같이 클래스의 모든 생성자에서 공통으로 수행되어야 하는 문장들이 있을때, 인스턴스 블럭을 이용하면 코드가 보다 간결해지고 직관적이다.

 

 

인스턴스 초기화 블럭 사용

class Car{
    static int count = 0;
    int serialNo;
    String color;
    String gearType;

    {
        count++;
        serialNo = count;
    }

    Car() {
        this("white", "auto");
    }

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

public class TestFree {
    public static void main(String[] args) throws IOException {
        Car car1 = new Car();
        Car car2 = new Car("black", "auto");
        Car car3 = new Car();

        System.out.println(car1.serialNo);  //1
        System.out.println(car2.serialNo);  //2
        System.out.println(car3.serialNo);  //3
    }
}

이처럼 코드의 중복을 제거하는 것은 코드의 신뢰성을 높여주고, 오류의 발생가능성을 줄여 준다는 장점이 있다.

 

 

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

 멤버 변수의 초기화 방법 중 초기화 블럭에 대해 알아보았다. 하지만 멤버 변수를 초기화 하는 방법은 기본값 초기화, 명시적 초기화, 초기화 블럭(클래스, 인스턴스) 초기화, 생성자 초기화와 같이 다양한 방법이 존재하는데 시기와 순서를 알아보자.

class InitTest{
    static int cv = 1;
    int iv = 1;

    static {
        cv++;
        System.out.println("클래스 초기화 블럭 초기화: " + cv);
    }

    {
        iv++;
        System.out.println("인스턴스 초기화 블럭 초기화: " + iv);
    }

    InitTest() {
        iv++;
        System.out.println("생성자 호출: " + iv);
    }

}

public class TestFree {
    public static void main(String[] args) throws IOException {
        InitTest it = new InitTest();
        InitTest it2 = new InitTest();
    }
}

다음과 같이 실행 순서를 알아보기 위하여 print문으로 데이터 정보를 찍고 단순히 클래스의 인스턴스를 생성만 해보았다. 실행결과는 아래와 같다.

 결과를 살펴보면 클래스 초기화 블럭 -> 인스턴스 초기화 블럭 -> 생성자순으로 호출 순서를 보이고 있고, 인스턴스를 두번째 생성할 때는 클래스 초기화 블럭은 생략되었다.

 

 클래스에 대한 정보는 프로그램 실행도중 클래스에 대한 정보가 요구될 때, 클래스는 메모리에 로딩되고, 해당 클래스가 이미 메모리에 로딩되어 있다면 또다시 로딩되지 않고 저장되어 있는 정보를 참조하기 때문에 초기화도 다시 수행되지 않는다.

 

클래스 멤버의 초기화 과정

기본값 : cv = 0  ->  명시적 초기화 : cv = 1  ->  클래스 초기화 블럭 : cv = 2

 

인스턴스 멤버의 초기화 과정

기본값 : iv = 0  ->  명시적 초기화 : iv = 1  ->  인스턴스 초기화 블럭 : iv = 2  ->  생성자 : iv = 3

'Java' 카테고리의 다른 글

[Java] 제어자(modifier) - static, final, abstract  (0) 2022.02.06
[Java] super와 super()  (0) 2022.01.29
[Java] this()와 this  (0) 2022.01.23
[Java] 가변인자(varargs)와 오버로딩  (0) 2022.01.07
[Java] 가변 배열  (0) 2022.01.06

공유하기

facebook twitter kakaoTalk kakaostory naver band