다음 코드의 출력을 예상해보자.
class A{
A() {
f();
}
public void f(){
System.out.println("3");
}
}
class B extends A{
int x = 4;
public void f() {
System.out.println(x);
}
}
public class Main {
public static void main(String[] args) {
A a = new B();
}
}
답은 뭘까? 3 혹은 4중에 고민하고 있다면, 그렇지 않다. 답은 0이다.
java는 C++와 다르게 생성자에서도 virtual하고 순서상 A가 초기화된 후에 B가 초기화되기 때문에, x가 초기화되지 않은 상태로 출력되어 0이 출력된다.
그렇다면 생성자에서 필드를 초기화하는 클래스에 대해 객체의 참조가 저장된(a = new A()) 이후라면 항상 필드가 초기화되어있을까?
상상해보면 new A() 라는 표현식이 평가되고 난 후에 참조가 저장되었을 것이므로, 생성자가 호출되어 필드의 초기화를 보장할 것 같다. 다음 코드를 보자
// jls(java lang spec) 17의 17.5-1 예제를 약간 수정했다.
class A {
int x;
static A a;
public A() {
x = 4;
}
static void writer() {
a = new A();
}
static boolean reader() {
if (a != null) {
System.out.println(a.x);
}
}
}
멀티 스레드 환경에서 한 스레드는 writer()
를 호출하고, 다른 스레드는 reader()
를 호출할 때, 만약 reader()
의 a != null
이 참으로 평가되면, 4가 출력될까?
jls는 이를 보장하지 않는다. 오히려 0을 출력할 수 있다고 말한다.
즉, 객체의 참조가 저장된 시점에조차 construtor가 완료되었음을 보장하지 않는다. a에 대한 assign이 x에 대한 assign보다 선행될 수 있다.
the reader method is therefore not guaranteed to see the value 4 for it.
두 케이스 모두 x에 final을 지정하면 초기화된 값을 보여줌을 보장한다.
final은 단순히 수정하지 못하게 하는 역할을 수행하는 것 뿐만 아니라 안전하게 초기화됨이 보장된다.
'공부' 카테고리의 다른 글
Garbage Collection(4) - Weak Reference (0) | 2023.01.16 |
---|---|
Garbage Collection(3) - Java GC Tuning (0) | 2023.01.16 |
Garbage Collection(2) - Java GC (0) | 2023.01.16 |
Garbage Collection(1) - 탈출 분석과 참조 카운팅, 추적 (1) | 2023.01.16 |
JS - Tag Function (0) | 2022.11.02 |