版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/luoyoub/article/details/82874993
————————————————
版权声明:本文为CSDN博主「忧伤的比目鱼」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/luoyoub/article/details/82874993
以下情况会触发类的初始化:
遇到new,getstatic,putstatic,invokestatic这4条指令;
使用java.lang.reflect包的方法对类进行反射调用;
初始化一个类的时候,如果发现其父类没有进行过初始化,则先初始化其父类(注意!如果其父类是接口的话,则不要求初始化父类);
当虚拟机启动时,用户需要指定一个要执行的主类(包含main方法的那个类),虚拟机会先初始化这个主类;
当使用jdk1.7的动态语言支持时,如果一个java.lang.invoke.MethodHandle实例最后的解析结果REF_getstatic,REF_putstatic,REF_invokeStatic的方法句柄,并且这个方法句柄所对应的类没有进行过初始化,则先触发其类初始化;
————————————————
版权声明:本文为CSDN博主「w893932747」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/w893932747/article/details/89067482
类的初始化
初始化顺序
首先来看下,当一个类从main入口方法,对于静态变量、静态初始化块、变量、初始化块、构造器,它们的初始化顺序:
1 public class Student { 2 public Student(){ 3 System.err.println("student initint===>"); 4 } 5 }
1 public class User { 2 public User() { 3 System.err.println("user initing===>"); 4 } 5 }
1 /** 2 * 初始化顺序:静态变量/静态代码块--》main方法--》非静态变量/代码块--》构造方法 3 */ 4 public class ClassLoadTest { 5 /** 6 * 静态变量 7 */ 8 private static User user = new User(); 9 10 /** 11 * 静态代码块 12 */ 13 static { 14 System.err.println("static code block"); 15 } 16 17 /** 18 * 代码块 19 */ 20 { 21 System.err.println("code block"); 22 } 23 24 /** 25 * 变量 26 */ 27 private Student student = new Student(); 28 29 /** 30 * 构造器 31 */ 32 public ClassLoadTest(){ 33 System.err.println("Constructor"); 34 } 35 public static void main(String[] args) { 36 System.err.println("mian ==>"); 37 new ClassLoadTest(); 38 } 39 }
1 执行结果 2 > Task :ClassLoadTest.main() 3 user initing===> 4 static code block 5 mian ==> 6 code block 7 student initint===> 8 Constructor
结论:
正常类的加载顺序:静态变量/静态代码块 -> main方法 -> 非静态变量/代码块 -> 构造方法
说明:静态代码块与静态变量的执行顺序同代码定义的顺序;非静态变量与代码块的执行顺序同代码执行顺序
继承的情况
1 public class Parent { 2 /* StaticVariable */ 3 public static String p_StaticField = "Parent--StaticVariable"; 4 /* Variable */ 5 public String p_Field = "Parent--Variable"; 6 protected int i = 9; 7 protected int j = 0; 8 /* Static initialization block */ 9 static { 10 System.out.println( p_StaticField ); 11 System.out.println( "Parent--Static initialization block" ); 12 } 13 /* initialization block */ 14 { 15 System.out.println( p_Field ); 16 System.out.println( "Parent--initialization block" ); 17 } 18 /* Constructor */ 19 public Parent() 20 { 21 System.out.println( "Parent--Constructor" ); 22 System.out.println( "i=" + i + ", j=" + j ); 23 j = 20; 24 } 25 }
1 public class SubClass extends Parent{ 2 /* StaticVariable */ 3 public static String s_StaticField = "SubClass--StaticVariable"; 4 /* Variable */ 5 public String s_Field = "SubClass--Variable"; 6 /* Static initialization block */ 7 static { 8 System.out.println( s_StaticField ); 9 System.out.println( "SubClass--Static initialization block" ); 10 } 11 /* initialization block */ 12 { 13 System.out.println( s_Field ); 14 System.out.println( "SubClass--initialization block" ); 15 } 16 /* Constructor */ 17 public SubClass() 18 { 19 System.out.println( "SubClass--Constructor" ); 20 System.out.println( "i=" + i + ",j=" + j ); 21 } 22 23 24 /* 程序入口 */ 25 public static void main( String[] args ) 26 { 27 System.out.println( "SubClassmain Method" ); 28 new SubClass(); 29 } 30 }
1 执行结果 2 > Task :SubClass.main() 3 Parent--StaticVariable 4 Parent--Static initialization block 5 SubClass--StaticVariable 6 SubClass--Static initialization block 7 SubClassmain Method 8 Parent--Variable 9 Parent--initialization block 10 Parent--Constructor 11 i=9, j=0 12 SubClass--Variable 13 SubClass--initialization block 14 SubClass--Constructor 15 i=9,j=20
初始化顺序:
父类–静态变量/父类–静态初始化块
子类–静态变量/子类–静态初始化块
父类–变量/父类–初始化块
父类–构造器
子类–变量/子类–初始化块
子类–构造器
结论:
子类的静态变量和静态初始化块的初始化是在父类的变量、初始化块和构造器初始化之前就完成了;
静态变量、静态初始化块顺序取决于它们在类中出现的先后顺序
变量、初始化块初始化顺序取决于它们在类中出现的先后顺序
分析
(1)访问SubClass.main()(这是一个static方法),于是装载器就会为你寻找已经编译的SubClass类的代码(也就是SubClass.class文件)。在装载的过程中,装载器注意到它有一个基类,于是再装载基类。不管你创不创建基类对象,这个过程总会发生。如果基类还有基类,那么第二个基类也会被装载,依此类推;
(2)执行基类的static初始化,然后是下一个派生类的static初始化,依此类推。这个顺序非常重要,因为派生类的“static初始化”有可能要依赖基类成员的正确初始化;
(3)当所有必要的类都已经装载结束,开始执行main()方法体,并用new SubClass()创建对象;
(4)类SubClass存在父类,则调用父类的构造函数,你可以使用super来指定调用哪个构造函数。基类的构造过程以及构造顺序,同派生类的相同。首先基类中各个变量按照字面顺序进行初始化,然后执行基类的构造函数的其余部分;
(5)对子类成员数据按照它们声明的顺序初始化,执行子类构造函数的其余部分;
static变量
1 public class Test { 2 static { 3 i = 0; // 给变量复制可以正常编译通过 4 // System.out.print(i); // 这句编译器会提示“非法向前引用” 5 } 6 static int i = 1; 7 8 static int j = 1; 9 10 static{ 11 j = 2; 12 } 13 14 public static void main(String[] args){ 15 System.out.println(Test.i); //1 16 System.out.println(Test.j); //2 17 } 18 }
不触发初始化实例
1 /** 2 * 被动使用类字段演示一: 3 * 通过子类引用父类的静态字段,不会导致子类初始化 4 **/ 5 public class SuperClass { 6 static { 7 System.out.println("SuperClass init!"); 8 } 9 10 public static int value = 123; 11 }
1 public class SubClass extends SuperClass { 2 static { 3 System.out.println("SubClass init!"); 4 } 5 }
1 public class NotInitialization { 2 public static void main(String[] args) { 3 System.out.println(SubClass.value); 4 //SuperClass init! 5 //123 6 7 /** 8 * 被动使用类字段演示二: 9 * 通过数组定义来引用类,不会触发此类的初始化 10 **/ 11 SuperClass[] sca = new SuperClass[10]; 12 } 13 }
被动使用类字段
1 public class ConstClass { 2 static { 3 System.out.println("ConstClass init!"); 4 } 5 6 public static final String HELLOWORLD = "hello world"; 7 }
1 public class Test { 2 public static void main(String[] args){ 3 System.out.println(ConstClass.HELLOWORLD); 4 } 5 }
1 输出 2 > Task :Test.main() 3 hello world
这里没有初始化ConstClass类,是因为在编译的时候,常量(static final 修饰的)会存入调用类的常量池【这里说的是main函数所在的类的常量池】,调用的时候本质上没有引用到定义常量的类,而是直接访问了自己的常量池
静态方法调用
当调用目标类的静态变量或静态方法时,不会触发该类的代码块或构造方法的执行,示例如下:
1 public class Handler { 2 public static User user = new User(); 3 static { 4 System.err.println("static code block"); 5 } 6 { 7 System.err.println("code block"); 8 } 9 public Handler(){ 10 System.err.println("Constructor"); 11 } 12 public static void print(){ 13 System.err.println("static method"); 14 } 15 }
1 public class User { 2 public User() { 3 System.err.println("user initing===>"); 4 } 5 static{ 6 System.err.println("user static method block===>"); 7 } 8 }
1 public class ClassLoadTest { 2 public static void main(String[] args) { 3 System.err.println(Handler.user); 4 Handler.print(); 5 } 6 }