static initializer block와 initializer block

2024. 1. 19. 19:18Java

[ 작성 이유 ]

 static 키워드와 관련된 공부를 하다가 static{} 이렇게 된 코드를 보게 되었고 이 코드가 무엇을 의미하는 건지 몰라 찾아보니 정적 초기화 블록이라는 걸 알게 되었다. 그래서 모르는 김에 초기화 블록에 대해서 정리를 해보고자 한다.

[ 초기화 블록 ]

 초기화 블록은 실행 시점에 따라 static initializer block(정적 초기화 블록), initializer block(인스턴스 초기화 블록) 이렇게 두 가지로 나뉜다.

 

○ 정적 초기화 블록

 정적 초기화 블록이 실행되는 시점은 클래스가 로딩되는 시점에 실행된다. static이라는 키워드가 붙어있듯이 인스턴스와 관련된 변수를 설정하는 공간은 아니고 static 변수만을 초기화할 수 있다. 특징으로는 해당 클래스에서 딱 한 번만 실행된다.

 

○ 초기화 블록

 초기화 블록이 실행되는 시점은 인스턴스가 생성되는 시점에 실행된다. 이 경우에는 정적 초기화 블록과 다르게 일반 변수, static 변수 상관없이 접근이 가능하다. 특징으로는 매번 인스턴스가 호출될 때마다 실행된다.

 

그렇다면 그런 궁금함이 이제 들 것이다. 만약에 객체를 생성하고 내부 메서드를 실행하면 실행되어야 하는 것이 총 4가지가 되는 것이다. 정적 초기화 블록, 초기화 블록, 생성자, 메서드 그럼 이것들의 실행 순서는 어떻게 될까? 그건 다음 코드를 통해서 확인할 수 있다. 

public class SequenceOfInitializerBlock {

    static {
        System.out.println("hello static initializer block");
    }

    {
        System.out.println("hello initializer block");
    }

    SequenceOfInitializerBlock(){
        System.out.println("hello constructor");
    }

    private void hello(){
        System.out.println("hello hello method");
    }

    public static void main(String args[]){
        // 결과는 다음과 같다.
        // hello static initializer block
        // hello initializer block
        // hello constructor
        // hello hello method
        SequenceOfInitializerBlock si = new SequenceOfInitializerBlock();
        si.hello();
    }
}

 

 main에 주석으로 달아놓은 것처럼 정적 초기화 블록, 초기화 블록, 생성자, 메서드 순서대로 실행이 된다.

 

그렇다면 초기화 블록은 어디다가 쓰는 걸까? 약간 처음엔 의아했다. 그냥 미리 값을 세팅해놓으면 되는거 아닌가? 그게 더 직관적이고 보기 좋은데? 더군다나 static 키워드가 들어간 변수는 공유되기 때문에 변경될 일도 없어서 굳이 거기다가 작성할 이유가 있나 싶다. 검색을 해보니, 이 물음에 대한 답은 2가지 정도가 있는 것 같다.

 

○ 첫 번째 상황

 

 초기화 블록은 반드시 필요한 것을 확인할 때 사용하기에 좋다. 만약에 DB를 사용하기 위해서 jdbcDriver를 호출해야 하는데 우선적으로 초기화될 것들이 초기화되지 않은 경우, 예외 처리를 하건 프로그램이 시작되자마자 죽여버려서 에러를 확인할 수 있게 해줘야 한다. 이런 예외적인 상황을 처리해주기에 좋다.

static {
        try {
        Class.forName("com.example.jdbc.Driver");
        } catch (ClassNotFoundException e) {
        throw new ExceptionInInitializerError("Cannot load JDBC driver.", e);
        }
}

 

 위의 코드가 stackoverflow에서 예시로 들어준 코드이다. 잘 공감이 가지 않는 코드지만 이런 식으로도 사용이 가능하지 않을까? 재활용해야하는 객체를 미리 생성해두는데 개수 제한을 해야 하는 경우 미리 실행시점에 수천 개를 만들어놓고 특정 static final 값을 false로 만들어버리는거다. 그 이상으로 만드려고 했을 때 예외를 던져버리는 식으로 말이다. 그런 상황까진 아니지만 operation을 기준으로 false냐 true냐에 따라서 객체를 생성할 수 있고 없고를 보여주는 코드를 짜봤다.

public class ExampleException {

    private static boolean operation;
    private int value;

    static {
        operation = true;
    }

    {
        try{
            if(operation == false)
                throw new Exception();
        } catch(Exception e){
            System.out.println("This instance cannot make");
            throw new IllegalStateException();
        }
    }

    public ExampleException(int value){
        this.value = value;
        System.out.println("Instance is made! It has value : " + value);
    }

    public static void main(String[] args){
        ExampleException ex = new ExampleException(100);
        ExampleException.operation = false;
        ExampleException ex2 = new ExampleException(100000);
    }
}

 

○ 두번째 상황

 

 아마 내가 공감을 못한 이유가 많은 블로그에서 String이나 int 같은 필드 내에서 초기화해도 상관 없는 값을 사용하는 걸 보여줘서 그런 것 같다. Map 같은 자료구조의 경우는 그게 쉽지 않다. 그런 경우에 다음과 같이 사용이 가능할 것 같다. 또한 아래와 같은 방식으로 코드를 짜는 경우, 많이 쓰일 객체를 미리 생성해둔 뒤 재활용도 가능하다고 한다. 아마 String 같은 게 예시가 될 것 같다.

public class ExampleComplicateInitialization {

    private static Map<String, Integer> legs = new HashMap<>();

    static {
        legs.put("octopus", 8);
        legs.put("squid", 10);
    }
}

 

 추가적으로 두 번째 상황의 경우 오해가 있을 수 있는데 static 초기화 블록이라고 반드시  static 값만 사용이 가능한 건 아니다. static 메서드에서 변수로 오는 경우는 상관이 없듯이 초기화 블록 내부에서 객체를 새로 생성하는 경우에는 인스턴스 값을 사용할 수 있고 초기화도 가능하다. 이건 static 키워드가 붙은 메서드에서도 동일하다.

public class ExampleStaticInitializerUseInstance {

    public static long value;

    static{
        Date date = new Date();
        value = date.getTime();
    }
}

 

[ 참고 목록 ]

https://www.baeldung.com/java-static-instance-initializer-blocks

https://stackoverflow.com/questions/2420389/static-initialization-blocks