Preface
In Java, an object must be correctly initialized before it can be used, which is stipulated by the Java specification. Recently I discovered an interesting question, and the answer to this question tricked my eyes at first glance. Take a look at these three categories:
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 can be obtained by running the Lower class? In this minimal example, the whole situation can be seen more easily, but this situation happens in reality and there will be a lot of code distracting a person.
Anyway, the output is like this:
Upper: upperInitedLower: null;
Although the String type is used in the small example, the actual code of the Initializer class has a delegate object for registration, which is the same as Lower class's function - at least Lower class is the intention. But for some reason it doesn't work when running the application. Instead, the default path is used and the delegate object is not set (null).
Now change Lower 's code a little:
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 now looks like this:
Upper: upperInitedLower: lowerInited
Have you found the difference in the code?
Yes, this lowerString field is no longer explicitly set to empty. Why does this make it different? Anyway, isn't the default value of the reference type field (such as String here) empty? Of course it is empty. It turns out that while this slight change obviously does not change the behavior of the code in any way, it makes the result different.
So, what exactly happened? Everything becomes clear when checking the initialization order:
1. main() function calls the Lower constructor.
2. An instance of Lower is ready. Meaning that all fields are created and filled with default values, for example, the default value of the reference type is empty and the default value of the Boolean type is false . At this time, no inline assignment to the field occurs.
3. The parent class constructor is called. This is enforced by the characteristics of the language. So before anything else happens, Upper's constructor is called.
4.Upper This constructor runs and specifies a reference to the newly created instance of Initializer.initialize() method.
5. Initializer class attaches a new string to two fields ( upperString and lowerString ). Assigning values to those two fields by using a bit dirty instanceof of instance checking is not a particularly good design pattern, but it also works, don't worry about that much. Once that happens, the references to upperString and lowerString are no longer empty.
6. The call to Initializer.initialize() is completed, and Upper constructor is also completed.
7. Now it's getting interesting: The construction of Lower instance continues. Assuming that the =null assignment is not explicitly specified in the lowerString field declaration, the Lower constructor resumes execution and prints out two strings connected to the field.
However, if there is an operation that explicitly assigns null, the execution process will be slightly different: when the parent class constructor is completed, any variable initialization will be performed before the rest of the constructors run (see Section 12.5 of the Java Language Specification). In this case, the string reference assigned to lowerString previously is not assigned null again. Then continue to execute the remaining function construction, and now print lowerString 's value as: null .
This is a great example, not only to facilitate our attention to some details about creating objects (or to know where to check Java coding specifications, printed or online), but also to show why writing initializations like this is bad. We shouldn't care about Upper's subclass at all. Conversely, if for some reason initialization of certain fields cannot be done in the subclass itself, it will only require some variant of its own initialization helper class. In this case, if you use String lowString or String lowerString = null there is really no difference, what it should be will be.
Summarize
The above is the entire content of this article. I hope the content of this article will be of some help to everyone’s study or work. If you have any questions, you can leave a message to communicate.