When class initialization is considered, we all know that when subclasses are initialized, subclasses are initialized if the parent class is not initialized. But there is no such thing as a simple word.
First look at the conditions in Java that initialize the trigger:
(1) When the object is instantiated using new, the static data and methods are accessed, that is, when the instruction is encountered: New,getstatic/putstatic and invokestatic;
(2) When the class is invoked using reflection;
(3) When a class is started, the parent class is initialized first, if it is not initialized;
(4) The class where the main method of the entry is executed;
(5) JDK1.7 The class of the method handle in the dynamic language support, if initialization is not triggered;
After being compiled into a <clinit> method, the initialization of the class is performed in this method, which is performed only by the JVM to guarantee this and to synchronize control;
where the condition (3), from the point of view of the method invocation, is the <clinit> of the subclass that the <clinit> will call the parent class recursively at the beginning, similar to the constructor that we must first call the parent class in the subclass constructor;
Note, however, that "triggering" is not a complete initialization, which means it is possible that the initialization of subclasses will end in advance of the initialization of the parent class, which is where "danger" is.
1. Example of a class initialization:
In this example I use a perimeter class that contains 2 static member classes that have inheritance relationships, because the initialization of the outer class and the static member class have no causal relationship, so it is safe and convenient to display it;
The parent Class A and subclass B contain the main function, which is known by the trigger condition (4), which triggers a different class initialization path by calling the two main function, respectively;
The problem with this example is that the parent class contains a static reference to the subclass and initializes the definition at the same point:
public class Wrapperclass {
private static Class A {
static {
System.out.println ("Class A initialization starts ...");
The parent class contains a static reference to the subclass
private static B = new B ();
protected static int aint = 9;
static {
System.out.println ("Class A initialization end ...");
}
public static void Main (string[] args {
}
}
private static class B extends A {
static {
SYSTEM.O Ut.println ("Class B initialization begins ...");
The domain of the subclass relies on the domain
private static int bInt = 9 + A.aint of the parent class;
The static domain System.out.println of public B () {
//constructor-dependent classes
("constructor Invocation of Class B +" BInt value + bInt);
}
static {
SYSTEM.OUT.PRINTLN (class B initialization end ...) "+" Aint value: "+ bInt";
}
public static void Main (string[] args) {
}
}
}
Scenario One: entrance is the output result of the main function of Class B:
/**
* Class A initialization begins ...
* Class B constructor calls the value of bint 0
* Class A initialization end ...
* Class B initialization begins ...
* Class B initialization end ... aint value:
Analysis: You can see that the invocation of the main function triggers the initialization of Class B, the <clinit> method of Class B, and Class A, as its parent class, first initializes into the <clinit> method of a, which has a statement new B (); In this case, B is instantiated, which is already in the <clinit> of Class B, the main thread has acquired the lock to begin executing Class B's <clinit> We said at the beginning that the JVM would guarantee that the initialization method of a class was executed only once, When the JVM receives the new instruction, it does not enter the <clinit> method of Class B but directly instantiates it, but at this point Class B has not completed class initialization, so you can see that the value of Bint is 0 (this 0 is the initialization of the method area after the preparation phase allocates the memory in the class load);
Therefore, it can be concluded that the parent class contains a static field of the subclass type and an assignment action, which may cause the subclass instantiation to occur before the class initialization completes;
Scenario Two: when the entrance is the main function of Class A, the result is output:
/**
* Class A initialization begins ...
* Class B initialization begins ...
* Class B initialization end ... aint value: 9
* Class B constructor calls bint value 9
* Class A initialization end
... */
Analysis: After the analysis of scene one, we know that initializing class A by initialization of Class B causes the instantiation of Class A in class A to be instantiated before Class B initialization completes, so if Class A is initialized first, the initialization of Class B can be triggered when the class variable is instantiated. So that the initialization is in front of the instantiation? The answer is yes, but it still has a problem.
Based on the output, it can be seen that Class B initialization is performed before class A initialization completes. This causes a variable like the class variable aint to initialize after Class B initialization is completed, so the domain Bint in class B obtains the aint value "0" rather than the "18" we expect;
Conclusion: In a comprehensive, it can be concluded that a class variable containing a subclass type in the parent class, and that it is very risky to define an instantiation, may not be as straightforward as the example, and that invoking the method at the definition is as dangerous as possible, even if you want to include the static field of the subclass type. You should also assign values through the static method, because the JVM can guarantee that all initialization actions are complete before the static method call (and you should not include static B = new B (); such initialization behavior);
2. An example of instantiation:
First you need to know the process of object creation:
(1) encountered the new instruction, check that the class has completed loading, validation, preparation, parsing, initialization (parsing is a symbolic reference that is parsed into a direct reference, such as a method name is a symbolic reference that can be used when initialization is done using this symbolic reference to support dynamic binding). These processes are not completed first;
(2) Allocates the memory, uses the idle list or the pointer collision method, and assigns the newly allocated memory "the 0", therefore all instance variables in this link all have the default initialization to 0 (refers to null) the process;
(3) Execute <init> method, including checking the <init> method (constructor) that invokes the parent class, the assignment action defined by the instance variable, the execution of the instantiation order, and the last call to the action in the constructor.
This example may be more familiar, that is, it violates the "do not invoke overridden methods in constructors, clone methods, and ReadObject methods." The reason is the polymorphism in Java, which is dynamic binding.
The constructor of parent class A contains a protected method, and Class B is its subclass.
public class Wronginstantiation {
private static Class A {public
a () {
dosomething ()
}
protected void DoSomething () {
System.out.println ("A ' s DoSomething");
}
private static class B extends A {
private int bInt = 9;
@Override
protected void dosomething () {
System.out.println ("B ' s dosomething, bInt:" + bInt);
}
Public
static void Main (string[] args) {
b = new B ();
}
}
Output results:
/**
* B ' s dosomething, bint:0 * *
Analysis: First, you need to know that the Java compiler generates a default constructor when the supplied constructor is not displayed, and calls the constructor of the parent class at the beginning, so the constructor for Class B begins by invoking the constructor of Class A.
Class A calls the protected method dosomething, and from the output we see a method implementation in which the subclass is actually called, while the instantiation of the subclass has not yet started, so bint is not 9, as expected, but 0;
This is because of the dynamic binding, dosomething is a protected method, so it is invoked through the invokevirtual instruction, which finds the corresponding method implementation based on the type of object instance (here is the instance object of B, the corresponding method is the method implementation of Class B). , and this is the result.
Conclusion: As mentioned earlier, "Do not invoke overridden methods in constructors, clone methods, and ReadObject methods."
The above is for you to introduce Java class initialization and instantiation of the 2 "mined area", I hope to help you learn.