2024. 1. 19. 19:18ㆍJava
[ 작성 이유 ]
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
'Java' 카테고리의 다른 글
Spring JDBC로 알아보는 예외 덩어리 처리 방법 (0) | 2024.02.22 |
---|---|
자바의 람다와 스트림 - 1(TIL) (0) | 2024.01.20 |
static 키워드 (0) | 2024.01.17 |
자바의 캡슐화 - Private, Protected, Default, Public (0) | 2024.01.10 |
Java의 메모리 구조 (0) | 2023.12.28 |