A class initialization rule states that:
If Class initialization is triggered due to access of static field, only Class which has declared static field is initialized and it doesn't trigger initialization of super class or sub class even if static field is referenced by Type of Sub Class, Sub Interface or by implementation class of interface.
Then in the following code, "Initializing Superclass" only should be printed.
Superclass.java :
public class Superclass {
public static long INIT_TIME = System.currentTimeMillis();
static {
System.out.println("Initializing Superclass");
}
}
Subclass.java:
public class Subclass extends Superclass {
static {
System.out.println("Initializing Subclass");
}
}
public static void main(String[] args) {
long time = Subclass.INIT_TIME;
}
}
When I run this, output :
Initializing Superclass
Initializing Subclass
Look at theJava Language Specification in chapter 12.1.3.
The rule is:
"If class Test has another class Super as its superclass, then Super must be initialized before Test."
Subclasses are not initialized, but of superclasses are.
Most likely your code is like this :
public class Temp extends SuperTemp { // subclass
static {
System.out.println("Initializing Temp");
}
}
class SuperTemp { // super class
static int i = 0;
static {
System.out.println("Initializing SuperTemp");
}
}
class SomeOtherClass {
public static void main(String[] args) {
System.out.println(Temp.i); // from some other class you are calling SubClass.superClassField
}
}
O/P :
Initializing SuperTemp
0
The reason : During Runtime Temp.i will be resolved to SuperTemp.i by the JVM. So, Temp will be loaded but not initialized. Looking at the way classes are loaded :
O/P of java -verbose:class
[Loaded SomeOtherClass from file XXXX]
[Loaded SuperTemp from file XXXX]
[Loaded Temp from file XXXX]
Initializing SuperTemp
0
As you stated :-
If Class initialization is triggered due to access of static field, only Class which has declared static field is initialized and it doesn't trigger initialization of super class or sub class even if static field is referenced by Type of Sub Class, Sub Interface or by implementation class of interface
This clearly states that the output should be Initializing Superclass only.
I ran your program and get the output as
Initializing Superclass
1414998665110
If you are getting output as you mentioned there is something else you are doing not stated in your question.
Note:- Referring to the answer provided by Jon Skeet on this question , it seems that one part of the aricle is wrong where it says:-
If Class initialization is triggered due to access of static field, only Class which has declared static field is initialized and it doesn't trigger initialization of super class
This is not related to your case , since INIT_TIME is part of Superclass, hence you are basically writing SuperClass_object.INIT_TIME when you say subclass.INIT_TIME. If you had INIT_TIME field in your Subclass instead of Superclass you would get output :-
Initializing Superclass
Initializing Subclass
1414998665110
To summarize this means Superclass gets intialized even if Class initialization is triggered due to access of static field, But Subclass is not initialized.
Related
Conditions to recreate (as far as I can tell):
nested enum references a parent static member
nested class
static member of parent class takes enum as an constructor argument to nested class
enum is referenced by an external class before anything else in the parent class
Run this code online:
https://repl.it/repls/PlushWorthlessNetworking
import java.util.ArrayList;
class Recreate {
private static ArrayList FEATURES = new ArrayList();
public enum Car {
TESLA(FEATURES);
Car(ArrayList l) { }
}
public static class Garage {
final Car car;
Garage(Car car) {
this.car = car;
}
}
public static Garage ONE_CAR_GARAGE = new Garage(Car.TESLA);
}
class Main {
public static void main(String[] args) {
// inclusion of this line causes the next line to NPE
System.out.println(Recreate.Car.TESLA);
System.out.println(Recreate.ONE_CAR_GARAGE.car.toString());
}
}
Here is what is happening:
The main method starts executing
You refer to Recreate.Car.TESLA
The classloader starts to load and initialize enum Car. As noted below, class Recreate is NOT yet loaded or initialized.
The initializer for TESLA refers to FEATURES
This causes class Recreate to be loaded and initialized
As part of static initialization of Recreate, Class Garage is loaded, intialized, and the instance ONE_CAR_GARAGE is created.
The problem here is that at this point, the construction of enum Car is not complete, and Car.TESLA has the value null.
Even though classes may be nested, it is not the case that nested classes are loaded and initialized as part of the outer class initialization. They may look nested in the source but each and every class is independent. Static nested classes are equivalent to top-level classes. Non-static classes are also the same but have the ability to refer to members in the containing class via a hidden reference.
You can see for yourself if you run this in a debugger, put breakpoints in several places, and examine the stack at each breakpoint.
I tested/debugged this in Eclipse with the following code, with breakpoints set where indicated. It's slightly different from your code but shouldn't behave differently:
public class Foo5
{
static class Recreate {
private static ArrayList FEATURES = new ArrayList();
public enum Car {
TESLA(FEATURES);
Car(ArrayList l) {
System.out.println("car"); // *** Breakpoint ***
}
}
public static Garage ONE_CAR_GARAGE = new Garage(Car.TESLA);
public static class Garage {
final Car car;
Garage(Car car) {
this.car = car; // *** Breakpoint ***
}
}
}
public static void main(String[] args) throws Exception {
Recreate.Car car = Recreate.Car.TESLA;
System.out.println(Recreate.Car.TESLA);
System.out.println(Recreate.ONE_CAR_GARAGE.car.toString());
}
}
The first breakpoint you will hit will be the one in the Garage(Car car) constructor. Examining the stack at that point you will see
Foo5$Recreate$Garage.<init>(Foo5$Recreate$Car) line: 23
Foo5$Recreate.<clinit>() line: 17
Foo5$Recreate$Car.<clinit>() line: 12
Foo5.main(String[]) line: 29
So when the Garage constructor is called, it has not yet returned from creating Car. This is dictated by the convoluted dependencies you have created between classes, so the solution is to untangle the dependencies. How you do that will depend on your ultimate goals.
You have a hidden circular dependency that's confusing the JVM. Let's take a look at your code.
class Recreate {
private static ArrayList FEATURES = new ArrayList();
public enum Car {
TESLA(FEATURES);
Car(ArrayList l) { }
}
public static class Garage {
final Car car;
Garage(Car car) {
this.car = car;
}
}
public static Garage ONE_CAR_GARAGE = new Garage(Car.TESLA);
}
We'll also need a few snippets from a page out of the JLS.
A class or interface type T will be initialized immediately before the first occurrence of any one of the following:
A static field declared by T is used and the field is not a constant variable (§4.12.4).
12.4.2. Detailed Initialization Procedure
...
Next, execute either the class variable initializers and static initializers of the
class, or the field initializers of the interface, in textual order, as though they
were a single block.
So our static data is being initialized when it's first referenced. Now, your Car.TESLA is implicitly static final, but it's not a constant, as per the definition.
A constant variable is a final variable of primitive type or type String that is initialized with a constant expression
So for our purposes, there are three static non-constant variables in play here: FEATURES, TESLA, and ONE_CAR_GARAGE.
Now, in your working case, you reference Recreate.ONE_CAR_GARAGE. This is a reference to a static field in Recreate, so FEATURES and then ONE_CAR_GARAGE get initialized. Then, during the initialization of ONE_CAR_GARAGE, TESLA gets initialized since its enumeration class is referenced. All's well.
However, if we reference the enum too early then we do things in the wrong order. Recreate.Car.TESLA gets referenced, so TESLA gets initialized. TESLA references FEATURES, so Recreate has to be initialized. This causes FEATURES and ONE_CAR_GARAGE to get initialized before TESLA finishes existing.
It's that hidden dependency that trips you up. Recreate.Car depends on Recreate which depends on Recreate.Car. Moving the ONE_CAR_GARAGE field into the Garage class will cause it to not get initialized with FEATURES and will fix your problem.
What does it mean when we say a class is "loaded"?
For example it is said that Static init blocks are executed at the time of class loading. When exactly class is loaded?
Please see this code from Kathy Sierra's book:
class Bird {
{
System.out.print("b1 ");
}
public Bird() {
System.out.print("b2 ");
}
}
class Raptor extends Bird {
static {
System.out.print("r1 ");
}
public Raptor() {
System.out.print("r2 ");
}
{
System.out.print("r3 ");
}
static {
System.out.print("r4 ");
}
}
class Hawk extends Raptor {
public static void main(String[] args) {
System.out.print("pre "); //1
new Hawk();//2
System.out.println("hawk ");
}
}
The output of above code is: r1 r4 pre b1 b2 r3 r2 hawk
I cant not understand howcome pre is printed after r1. Which part of the code loaded Raptor class?
One of the answer says :
"the class is initialized at the last possible moment, but before any of its members is accessed".
But by that logic, shouldn't pre be printed BEFORE r1? Since line 2 accesses constructor of class Raptor AFTER execution of line1.
Don't confuse class loading, which is implementation-specific, and class initialization, which is very strictly specified. Unfortunately these two terms are often used as if they meant the same thing, but usually the phrase "the time of class loading" refers to the time of class initialization.
As an executive summary of the detailed case-by-case rules, the class is initialized at the last possible moment, but before any of its members is accessed.
With that part cleared up, in the code of your example you have a class Hawk extends Raptor extends Bird. Hawk is the class containing the entry point of your program, the method main. This is the init order:
Start initializing Hawk. Is its parent class Raptor initialized? no.
Start initializing Raptor. Is its parent Bird initialized? no.
Start initializing Bird.
Complete initializing Bird.
Execute Raptor's static init blocks. This prints r1 r4.
Complete initializing Raptor.
Complete initializing Hawk.
Start executing Hawk.main(). This prints pre.
Usually when you first attempt to use the class, e.g. try to create an instance, use one of its static methods or store reference to the class' Class<?> object.
Note that this may depend on the classloader implementation.
Different JVMs load classes in different ways, but the basic rule is only loading classes when they are needed. If there are some other classes that are required by the loaded class, they will also be loaded. The loading process is recursive.
Below code is simple example of class and static block,variable laoding.
public class Foo {
//instance variable initializer
String s = "abc";
//constructor
public Foo() {
System.out.println("constructor called");
}
//static initializer
static {
System.out.println("static initializer called");
}
//instance initializer
{
System.out.println("instance initializer called");
}
public static void main(String[] args) {
new Foo();
new Foo();
}
}
Output for above program :
static initializer called
instance initializer called
constructor called
instance initializer called
constructor called
class A {
static int super_var = 1;
static {
System.out.println("super");
}
}
class B extends A {
static int sub_var = 2;
static {
System.out.println("sub");
}
}
public class Demo{
public static void main(String []args){
System.out.println(B.super_var);
}
}
outputs are :
super
1
this means that the child class not going to load or any other thing? how is it works?
When you access the static fields of a super class on subclass reference, only the class that declares the field will be loaded and initialized, in this case it is A. This is specified in JLS §12.4.1 - When Initialization Occurs:
A reference to a static field (§8.3.1.1) causes initialization of only
the class or interface that actually declares it, even though it might
be referred to through the name of a subclass, a subinterface, or a
class that implements an interface.
Emphasis mine.
So in your code, class B would not even be initialized, and hence its static block would not be executed.
Checkout the answer to this question: In what order do static initializer blocks in Java run?
The static block gets called only when a class is accessed (either creating an instance or accessing a member field or static method). However, you access a member of class A only, so there is no reason for class B to be initialized yet. The static initializer of B will be called as soon as you access a member from that class (either a field or static method, or create an instance from class B).
The reason is that class B doesn't need to be initialized until you access one of its members. Because A doesn't know about B (and cannot access it) there's really no reason for B to initialize at that stage.
You will find out that when you access B.sub_var the static initializer of B will be executed.
class A {
static {
System.out.println("A-SIB");
}
static void test(){
System.out.println("A-test");
}
}
class B extends A {
static {
System.out.println("B-SIB");
}
}
class C {
public static void main(String args []){
B.test();
}
}
When I ran class C, I thought A-SIB, B-SIB and A-test will be printed, but B-SIB was not there in the output. Can somebody explain why?
Here's what the JLS says about class initialization:
Initialization of a class consists of executing its static initializers and the initializers for static fields (class variables) declared in the class.
Initialization of an interface consists of executing the initializers for fields (constants) declared in the interface.
Before a class is initialized, its direct superclass must be initialized, but interfaces implemented by the class are not initialized. Similarly, the superinterfaces of an interface are not initialized before the interface is initialized.
A class or interface type T will be initialized immediately before the first occurrence of any one of the following:
T is a class and an instance of T is created.
T is a class and a static method declared by T is invoked.
A static field declared by T is assigned.
A static field declared by T is used and the field is not a constant variable (§4.12.4).
T is a top level class (§7.6), and an assert statement (§14.10) lexically nested within T (§8.1.3) is executed.
A reference to a static field (§8.3.1.1) causes initialization of only the class or interface that actually declares it, even though it might be referred to through the name of a subclass, a subinterface, or a class that implements an interface.
In this case, All you do in C with the class B is call the static method test(). But this method is declared in A, not in B. Thus, the JVM doesn't initialize the class B and thus doesn't invoke its static initializer block.
Note that the class B is referenced in C's byte-code and is loaded by the JVM. But it's not initialized. If you delete B.class and try to run C, you'll get an exception.
Class B does not implement (aka "hide") the static test method, so initial execution begins within Class A (hence A-SIB); and then with the test method in A (hence "A-test"). If you override test in Class B, you will get A-SIB B-SIB B-test
I am trying to understand this example from Thinking in Java:
package c07;
import com.bruceeckel.simpletest.*;
class Meal {
Meal() { System.out.println("Meal()"); }
}
class Bread {
Bread() { System.out.println("Bread()"); }
}
class Cheese {
Cheese() { System.out.println("Cheese()"); }
}
class Lettuce {
Lettuce() { System.out.println("Lettuce()"); }
}
class Lunch extends Meal {
Lunch() { System.out.println("Lunch()"); }
}
class PortableLunch extends Lunch {
PortableLunch() { System.out.println("PortableLunch()");}
}
public class Sandwich extends PortableLunch {
private static Test monitor = new Test();
private Bread b = new Bread();
private Cheese c = new Cheese();
private Lettuce l = new Lettuce();
public Sandwich() {
System.out.println("Sandwich()");
}
public static void main(String[] args) {
new Sandwich();
monitor.expect(new String[] {
"Meal()",
"Lunch()",
"PortableLunch()",
"Bread()",
"Cheese()",
"Lettuce()",
"Sandwich()"
});
}
}
As I understand from Java Language Specification, the order of execution begins by loading the class containing the main method. Then all the statics and member variables of this class must be initialized (before which all the member variables of the superclasses must be intialized, although there are none of those in this case).
So I thought b, c, l would be initialized before main starts executing. That does not seem to be the case from the output though. Am I missing something?
No, b and c are instance variables.
There's no automatic instantiation of the class containing main. Only static variables are initialized. It's just as if some outside caller wrote:
Sandwich.main(args);
So when you wrote:
Then all the statics and member variables of this class must be initialized
... that was wrong. Only the static variables are initialized - just as normal.
The example output is correct. Here are the important rules:
when the class is created, the constructor of the super class has to be called first. This bubbles up to Object class
before the constructor is called, member variable initialization is called.
No statics are involved in this example, except the technical monitor.
JLS # 12.4.1. When Initialization Occurs
A class or interface type T will be initialized immediately before the first occurrence of any one of the following:
T is a class and an instance of T is created.
T is a class and a static method declared by T is invoked.
A static field declared by T is assigned.
A static field declared by T is used and the field is not a constant
variable (§4.12.4).
T is a top level class (§7.6), and an assert statement (§14.10)
lexically nested within T (§8.1.3) is executed.
JLS # 12.5. Creation of New Class Instances
Whenever a new class instance is created, memory space is allocated for it with room for all the instance variables declared in the class type and all the instance variables declared in each superclass of the class type, including all the instance variables that may be hidden (§8.3).