static关键字

 ̄綄美尐妖づ 提交于 2019-12-06 01:44:37

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

 

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!