From: http://www.linuxdiyf.com/viewarticle.php? Id = 18987
We can think that each class has a method named initialize (). This name implies that it must be called before use. Unfortunately, in this case, you have to remember to call this method. Java class library designers can use a special method called constructor to ensure that every object can be initialized. if there is a constructor in the class, Java will automatically call the constructor when the object is just created and the user is too late to obtain it, so that initialization is guaranteed.
I don't know how much the difference between the original author's description and the translator's understanding is. Combined with the entire chapter, I didn't find two key words "" and "". At least the author and the translator did not really explain what the JVM did during initialization, or did not understand the JVM initialization details. Otherwise, the two methods are clearly available, but why do you think there is a "initialize ()" method that actually does not exist?
Where are the "and" methods? These two methods actually exist and you cannot find them. This may make some masters confused. Coupled with some bugs in JDK implementation, if you do not have a deep understanding, it is really hard to understand.
Now there is a strange phenomenon in the scientific system, so a huge system was initially built on the basis of a hypothesis: Suppose 1 is correct, then 2 is derived, and then continue to export 10000000000. Unfortunately, too many people do not care about systems like 2-00000000000, which are based on the assumption that 1 is correct. I will not use the assumption that "we can think like this". I want to prove that "and" "methods actually exist:
Code: Package debug; Public class mytest { Static int I = 100/0; Public static void main (string [] ARGs ){ Ssytem. Out. println ("Hello, world! "); } } |
Run the following command to check the output of jdk1.5:
Code: Java. Lang. exceptionininitializererror Caused by: Java. Lang. arithmeticexception:/by zero At Debug. mytest. (test. Java: 3) Exception in thread "Main" |
Please note that the exception is located in debug. mytest like the exception generated when calling other methods.
Let's take a look:
Code: Package debug; Public class test { Test (){ Int I = 100/0; } Public static void main (string [] ARGs ){ New Test (); } }Jdk1.5 input: Exception in thread "Main" Java. Lang. arithmeticexception:/by zero At Debug. Test. (test. Java: 4) At Debug. Test. Main (test. Java: 7) |
The JVM does not locate the exception in the test () constructor, but in debug. Test ..
When we see these two methods, we will discuss these two "built-in initialization methods" in detail (I do not like to create some non-standard terms, but I do not know how to name them in a standard way ).
The built-in initialization method is a special method specifically used for initialization by JVM internally, rather than a method provided to the programmer for calling. In fact, you cannot even compile the syntax like "<>" in the source program. This shows that initialization is controlled by JVM rather than programmers.
Class initialization method:
I didn't know from any point whether Cl is short for class, but this method is indeed used to initialize "class. In other words, it is used to initialize the static context.
During load, JVM calls the built-in method to initialize class members and static initialization blocks. Their order is in the original order of the source file.
We will add a few static statements:
Code: Package debug; Public class test { Static int x = 0; Static string S = "123 "; Static { String S1 = "456 "; If (1 = 1) Throw new runtimeexception (); } Public static void main (string [] ARGs ){ New Test (); } } |
Then decompile:
Code: javap-C Debug. Test Compiled from "test. Java" Public class Debug. Test extends java. Lang. object { Static int X; Static java. Lang. String S; Public Debug. Test (); Code: 0: aload_0 1: invokespecial #1; // method Java/lang/object. "" :() V 4: Return Public static void main (Java. Lang. String []); Code: 0: New #2; // class debug/test 3: DUP 4: invokespecial #3; // method "" :() V 7: Pop 8: Return Static {}; Code: 0: iconst_0 1: putstatic #4; // field X: I 4: LDC #5; // string 123 6: putstatic #6; // field S: ljava/lang/string; 9: LDC #7; // string 456 11: astore_0 12: New #8; // class Java/lang/runtimeexception 15: DUP 16: invokespecial #9; // method Java/lang/runtimeexception. "" :() V 19: athrow } |
We can see that class initialization is based on the original sequence defined in the source file. First declare
Code: static int X; Static java. Lang. String S; |
Then assign values to int X and string s:
Code: 0: iconst_0 1: putstatic #4; // field X: I 4: LDC #5; // string 123 6: putstatic #6; // field S: ljava/lang/string; |
Execute the string S1 = "456" of the initialization block; generate a runtimeexception and throw
Code: 9: LDC #7; // string 456 11: astore_0 12: New #8; // class Java/lang/runtimeexception 15: DUP 16: invokespecial #9; // method Java/lang/runtimeexception. "" :() V 19: athrow |
You must understand that the "" method is not only a class initialization method, but also an interface initialization method. This is not the interface
All attributes are inline. Only interface constants directly assigned constant values can be inline. While
[Public static Final] Double D = math. Random () * 100;
Such expressions need to be calculated and initialized by the "" method in the interface.
Next let's take a look at the instance Initialization Method ""
"" Is used to initialize an object when it is created. when an object is created in heap, a space is allocated in heap. The "" method is called first. This method includes instance variable assignment (declaration is not included), initialization block, and constructor call. If there are multiple overloaded constructor methods, each constructor will have a corresponding "" method. The constructor always executes instance variable initialization and block initialization before calling the constructor of the parent class. similarly, the instance variables and initialization blocks are executed in the original source file order. The code in the constructor is executed at the end:
Code: Package debug; Public class test { Int x = 0; String S = "123 "; { String S1 = "456 "; // If (1 = 1) // Throw new runtimeexception (); } Public test (){ String Ss = "789 "; } Public static void main (string [] ARGs ){ New Test (); } } Result of javap-C Debug. test: Compiled from "test. Java" Public class Debug. Test extends java. Lang. object { Int X; Java. Lang. String S; Public Debug. Test (); Code: 0: aload_0 1: invokespecial #1; // method Java/lang/object. "" :() V 4: aload_0 5: iconst_0 6: putfield #2; // field X: I 9: aload_0 10: LDC #3; // string 123 12: putfield #4; // field S: ljava/lang/string; 15: LDC #5; // string 456 17: astore_1 18: LDC #6; // string 789 20: astore_1 21: Return Public static void main (Java. Lang. String []); Code: 0: New #7; // class debug/test 3: DUP 4: invokespecial #8; // method "" :() V 7: Pop 8: Return } |
If one constructor calls another constructor in the same class, the corresponding "" method calls another ", but the instance variables and initialization blocks are ignored, otherwise, they will be executed multiple times.
Code: Package debug; Public class test { String S1 = RT ("S1 "); String S2 = "S2 "; Public test (){ S1 = "S1 "; } Public test (string s ){ This (); If (1 = 1) throw new Runtime (); } String RT (string s ){ Return S; } Public static void main (string [] ARGs ){ New Test (""); } } |
Decompilation result:
Code: Compiled from "test. Java" Public class Debug. Test extends java. Lang. object { Java. Lang. String S1; Java. Lang. String S2; Public Debug. Test (); Code: 0: aload_0 1: invokespecial #1; // method Java/lang/object. "" :() V 4: aload_0 5: aload_0 6: LDC #2; // string S1 8: invokevirtual #3; // method RT :( ljava/lang/string;) ljava/lang/string; 11: putfield #4; // field S1: ljava/lang/string; 14: aload_0 15: LDC #5; // string S2 17: putfield #6; // field S2: ljava/lang/string; 20: aload_0 21: LDC #2; // string S1 23: putfield #4; // field S1: ljava/lang/string; 26: Return Public Debug. Test (Java. Lang. String ); Code: 0: aload_0 1: invokespecial #7; // method "" :() V 4: New #8; // class Java/lang/runtimeexception 7: DUP 8: invokespecial #9; // method Java/lang/runtimeexception. "" :() V 11: athrow Java. Lang. String RT (Java. Lang. String ); Code: 0: aload_1 1: areturn Public static void main (Java. Lang. String []); Code: 0: New #10; // class debug/test 3: DUP 4: LDC #11; // string 6: invokespecial #12; // method "" :( ljava/lang/string;) V 9: Pop 10: Return } |
We can see that test (string s) calls test (); so "" :( ljava/lang/string;) V no longer initializes instance variables and initialization blocks:
Code: Public Debug. Test (Java. Lang. String ); Code: 0: aload_0 1: invokespecial #7; // method "" :() V 4: New #8; // class Java/lang/runtimeexception 7: DUP 8: invokespecial #9; // method Java/lang/runtimeexception. "" :() V 11: athrow |
If the two constructor methods are independent of each other, the instance variables and initialization blocks will be called before each constructor is called:
Code: Package debug; Public class test { String S1 = RT ("S1 "); String S2 = "S2 "; { String S3 = "S3 "; } Public test (){ S1 = "S1 "; } Public test (string s ){ If (1 = 1) Throw new runtimeexception (); } String RT (string s ){ Return S; } Public static void main (string [] ARGs ){ New Test (""); } } |
Decompilation result:
Code: Compiled from "test. Java" Public class Debug. Test extends java. Lang. object { Java. Lang. String S1; Java. Lang. String S2; Public Debug. Test (); Code: 0: aload_0 1: invokespecial #1; // method Java/lang/object. "" :() V 4: aload_0 5: aload_0 6: LDC #2; // string S1 8: invokevirtual #3; // method RT :( ljava/lang/string;) ljava/lang/string; 11: putfield #4; // field S1: ljava/lang/string; 14: aload_0 15: LDC #5; // string S2 17: putfield #6; // field S2: ljava/lang/string; 20: LDC #7; // string S3 22: astore_1 23: aload_0 24: LDC #2; // string S1 26: putfield #4; // field S1: ljava/lang/string; 29: Return Public Debug. Test (Java. Lang. String ); Code: 0: aload_0 1: invokespecial #1; // method Java/lang/object. "" :() V 4: aload_0 5: aload_0 6: LDC #2; // string S1 8: invokevirtual #3; // method RT :( ljava/lang/string;) ljava/lang/string; 11: putfield #4; // field S1: ljava/lang/string; 14: aload_0 15: LDC #5; // string S2 17: putfield #6; // field S2: ljava/lang/string; 20: LDC #7; // string S3 22: astore_2 23: New #8; // class Java/lang/runtimeexception 26: DUP 27: invokespecial #9; // method Java/lang/runtimeexception. "" :() V 30: athrow Java. Lang. String RT (Java. Lang. String ); Code: 0: aload_1 1: areturn Public static void main (Java. Lang. String []); Code: 0: New #10; // class debug/test 3: DUP 4: LDC #11; // string 6: invokespecial #12; // method "" :( ljava/lang/string;) V 9: Pop 10: Return } |
After understanding the above knowledge, let's do a small test:
Code: public class Test2 extends test1 { System. Out. Print ("1 "); } Test2 (){ System. Out. Print ("2 "); } Static { System. Out. Print ("3 "); } { System. Out. Print ("4 "); } Public static void main (string [] ARGs ){ New Test2 (); } } Class test1 { Test1 (){ System. Out. Print ("5 "); } Static { System. Out. Print ("6 "); } } |
Can you see the print order clearly? What will be printed without new Test2?