版权声明:本文为博主原创文章,遵循 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 }