Java Object Initialization Order - Know your JLS!
6047 단어 object
Recently I came across an interesting problem whose solution eluded me at first glance. Consider these three classes:
package com.ds.test;
public class Upper {
String upperString;
public Upper() {
Initializer.initialize(this);
}
}
package com.ds.test;
public class Lower extends Upper {
String lowerString = null;
public Lower() {
super();
System.out.println("Upper: " + upperString);
System.out.println("Lower: " + lowerString);
}
public static void main(final String[] args) {
new Lower();
}
}
package com.ds.test;
public class Initializer {
static void initialize(final Upper anUpper) {
if (anUpper instanceof Lower) {
Lower lower = (Lower) anUpper;
lower.lowerString = "lowerInited";
}
anUpper.upperString = "upperInited";
}
}
What output is to be expected from running the
Lower
class?In this very reduced example it is much easier to get a view of the whole situation - in reality where this occurred there was a lot more code to distract one's attention...Anyway, this is what the output looks like: Upper: upperInited
Lower: null;
While the little example uses Strings, the real code of
Initializer
had a delegate object registered with the equivalent of the Lower
class - at least that was the intention. For some reason however did this not work when running the application. Instead, the default path was taken - the one for the delegate object being not set ( null
).Now, change the code of Lower
slightly: package com.ds.test;
public class Lower extends Upper {
String lowerString;
public Lower() {
super();
System.out.println("Upper: " + upperString);
System.out.println("Lower: " + lowerString);
}
public static void main(final String[] args) {
new Lower();
}
}
The output is now:
Upper: upperInited
Lower: lowerInited
Notice the difference in the code?Yes, the
lowerString
field is no longer explicitly set to null
. Why would this make a difference? Isn't the default value for reference type fields (such as String
here) null
anyway? Of course, it is. However it turns out that this tiny little change - which apparently would not change the code's behavior in any way - makes this thing fly or not fly.So what is going on? It becomes clear when looking at the initialization order: main()
calls the Lower
constructor. Lower
is prepared. That means, all fields are created and populated with default values, i. e. null
for reference types, false
for boolean
s and so on. At this time, any inline assignments to the fields have not taken place! Upper
's constructor is called. Upper
constructor runs and hands a reference to the freshly created instance to the Initializer.initialize()
method. Initializer
attaches new String
s to both fields. It does so by using a somewhat dirty instanceof
check - not a particularly good design pattern, but possible, nevertheless. Once that has happened, both the upperString
lowerString
references are no longer null
. Initializer.initialize()
call finishes, as does the Upper
constructor. Lower
instance continues. Assuming there is no explicit =null
assignment in the lowerString
field declaration, the Lower
constructor resumes execution and prints out the two Strings that are attached to the fields.However, if there is an explicit assignment to null
, execution has a slightly different flow: Just after the super constructor is done, any variable initializers are executed (see section 12.5 of the Java Language Spec ), before the rest of the constructor is run. In this case the String
reference that was previously assigned to lowerString
is now overwritten with null
again! Only then does the rest of the constructor continue execution, now printing lowerString: null
. Apart from being a nice example for why it is handy to be aware of some of the minutiae of object creation (or knowing where to look in the JLS, printed or online) this shows why it is a bad idea to write the
Initializer
like this. It should not be aware of Upper
's subclasses at all! Instead, if for some reason initialization of certain fields cannot be done in the Lower
class itself, it will just require its own variant of some sort of initialization helper. In that case, it would really make no difference if you used String lowerString;
or String lowerString = null;
- just as it should be.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
localStorage에 객체를 추가하는 방법은 무엇입니까?이 노트에서는 localStorage에 객체를 삽입하는 방법을 보여드리겠습니다. 경우에 따라 로컬 스토리지 또는 세션 스토리지에 데이터를 개체로 저장해야 할 수 있습니다. 어떻게 이것을 달성할 수 있습니까? 객체가 ...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.