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
Related
Initially I thought I would have a chicken and egg problem here, but exploring this in a unit test doesn't indicate any problems. I want to understand what is going on here. I would have thought that since enums are static and final the MyEnum constructor would run when the JVM loads MyClass. However in my test it prints "getValue" before "MyEnum constructor".
MyClass {
private enum MyEnum {
VALUE;
MyEnum() {
System.out.println("MyEnum constructor");
MyClass clazz = new MyClass();
}
}
public MyEnum getValue() {
System.out.println("getValue");
return MyEnum.VALUE;
}
}
public class MyClassTest {
#Test
public void testStuff() {
MyClass clazz = new MyClass();
clazz.getValue();
}
}
The fact that a class/interface/enum/annotation is nested within another class does not affect when its initialization will occur.
The rules for initialization apply regardless. They are defined in the JLS here.
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.
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).
The JLS also says the following about enum types
An enum declaration specifies a new enum type, a special kind of class type.
And about its members, it says
For each enum constant c declared in the body of the declaration of E,
E has an implicitly declared public static final field of type E that
has the same name as c. The field has a variable initializer which
instantiates E and passes any arguments of c to the constructor chosen
for E. The field has the same annotations as c (if any).
Put all this together and you get an explanation for the behavior you see.
Your code instantiates MyClass, then invokes its getValue() method. getValue() prints something to standard out and then tries to access a static field declared by MyEnum. This triggers initialization of the enum type, which initializes the public static static VALUE field, which invokes the corresponding MyEnum construct that again prints to standard out.
public class InnerTest {
public static void main(String arg[]) {
A.B.print();
}
}
class A {
static class B {
static void print() {
System.out.println("Hello");
}
}
}
How can i call static class B using class name A although class A is not static
This is not related to the class to be static or not, it is related the static keyword in the method.
take a look about How static keyword exactly works in Java? also read this article Java – Static Class, Block, Methods and Variables
One more aspect how to explain this:
class itself is not static or non static it is just a class.
You anyway can use static keyword only with class members. If you would try to declare InnerTest as static you would have an error that might look like this (so assuming it is not static nested class to some other class)
Illegal modifier for the class InnerTest; only public, abstract &
final are permitted
static nested class can be used as in question because it does not require access to any instance member of InnerTest. In other words it can access static members and only those.
If it needs access to non static members then it can not be static and the way to call would be like new InnerTest().new B().
The static keyword is used to modify a member of a class. When a member is modified with static, it can be accessed directly using the enclosing class' name, instead of using an instance of the enclosing class like you would with a non-static member.
The inner class Inner below is a member of the class Outer:
class Outer {
static class Inner {}
}
Therefore, Inner can be accessed like this:
Outer.Inner
Outer don't need to/cannot be modified by static because it is not a member of a class. It is a class existing in the global scope. To access it, you just write its name - Outer. It does not make sense for it to be non-static because it has no enclosing class. If it were non-static, how are you supposed to access it?
To use the correct terminology, class B is not an inner class; it is a static nested class. See https://docs.oracle.com/javase/tutorial/java/javaOO/nested.html for the definitions of the various types of nested class.
The important keyword is the static keyword in front of the definition of class B. It does not matter whether class A is static or not. In fact, it wouldn't make sense to put static in front of class A's definition.
Because class B is declared static, it doesn't keep a reference to an instance of class A.
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.
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.
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).