📝个人主页:哈__
期待您的关注
在日常使用Java的时候,我们都接触过new这个关键字,那你是否知道在我们的对象真正创建出来之前都做了哪些事情呢?
实际上要去判断一个类的初始化的顺序,需要分一下情况,一种是没有继承关系的类(不考虑Object类),一种是有继承关系的类。
一、无继承关系类的初始化
先看下边的代码。有着这样的一个类InitializeDemo ,我们直接运行main方法,你知道会打印出来什么吗?
public class InitializeDemo {private static int k = 1;private static InitializeDemo t1 = new InitializeDemo("t1");private static InitializeDemo t2 = new InitializeDemo("t2");private static int i = print("i");private static int n = 99;{print("初始化块");j=100;}public InitializeDemo(String str){System.out.println((k++)+":" + str + " i="+i+". n="+n);++i;++n;}static{print("静态块");n=100;}private int j = print("j");public static int print(String str){System.out.println((k++)+":" + str + " i="+i+". n="+n);++n;return ++i;}public static void main(String[] args) {InitializeDemo i = new InitializeDemo("test");}
}
打印结果如下所示。
想要弄明白这个,我就得先了解一下类的生命周期了。我们通过主方法运行Java程序,而这个主方法中创建了i对象并且属于InitializeDemo 类,所以在main方法执行时会将我们的类进行初始化。
在我们的类第一次进行初始化的时候,我们类中的静态块和静态属性会被优先构建。之后才轮得到非静态块和非静态属性。
当你知道了上边的规则之后,我们来看代码,类中有五个静态属性,一个非静态属性,一个静态块,一个非静态块还有一个静态方法。我们优先构建静态方法、属性和静态块,之后在构建非静态块。
这里我总结出第一条规则。
类的一个对象进行初始化,按照代码顺序从上往下先构建静态的属性、方法和静态块,之后在构建非静态块和非静态属性。(类中的静态块和静态变量只会初始化一次,也就是类第一次被用到的时候)
1、静态变量k被初始化
private static int k = 1;
2、静态变量t1初始化
private static InitializeDemo t1 = new InitializeDemo("t1");
这里还要说一下,这个静态变量是创建自己的类的对象,代码执行到这一行就要进行一个对象的初始化过程了。遇到这种创建本类对象的,我们按照以下的第二条规则。
一个类中有静态的本类对象,创建的过程从上到下先执行本类的非静态块和非静态属性,最后在执行构造方法。
那么我们初始化t1的时候,就要从上向下找非静态块和非静态属性(如果有的话 ),最后在执行构造方法。
{print("初始化块");j=100;}
我们调用print函数并且将j的值修改为100,这样就出现了我们的第一行。
之后往下走,走到了这一行代码。这行代码定义了j变量,并且调用了print方法,所以也就有了我们的第二行。
private int j = print("j");
最后调用构造方法。打印我们的第三行。
现在你是否清楚了前三行输出结果是怎么来的了吗?这前三行的结果都是t1进行初始化时非静态块和非静态变量的执行结果。
3、静态变量 t2初始化
搞明白了t1是如何初始化的t2就和t1一模一样了,这里不讲解。
4、静态变量i初始化
静态变量i的初始化用到了print函数,这也就是第七行为什么会打印的原因。
private static int i = print("i");
5、静态变量n初始化
private static int n = 99;
6、静态块初始化
这里调用print函数,也就是第八行的由来。
static{print("静态块");n=100;}
ok,到了这一步我们的静态属性和静态块已经从上到下全部初始化完成了。但你要知道,我们在上边初始化的非静态块和非静态属性所打印的语句是我们的t1和t2对象引起的,并非我们主方法内构建的i对象引起的,接下来还要初始化我们的非静态块和属性。
7、非静态块初始化
第九行的由来。
{print("初始化块");j=100;}
8、非静态属性初始化
第十行的由来。
private int j = print("j");
9、执行构造方法
第十一行的由来。
public InitializeDemo(String str){System.out.println((k++)+":" + str + " i="+i+". n="+n);++i;++n;}
以上就是我们的一个类的构建过程,这里我给大家一张图(自己画的)。
二、有父类继承的初始化
和没有父类继承初始化大同小异,我定义了这样的一个类让InitializeDemo,去继承。为了便于还是懵懂的同学理解,我把静态变量t1和t2删掉。
public class InitializeDemoFather {{System.out.println("父类初始化块");}static{System.out.println("父类静态块");}public InitializeDemoFather(){System.out.println("父类构造函数");}
}
来看看我们现在的打印结果。
我给大家讲一下初始化的顺序大家就明白了,这里用一张图来表示。
对应着上边的图,是不是一看就有点思路了呢?
如果你可以完整的走出以上的输出顺序,你就可以把t1和t2对象加上去了,不过不要被输出顺序所迷惑,就比如说,你可能会看到父类静态块和属性初始化完成后,初始化的不是子类的静态块和静态属性,而是父类的非静态块和非静态属性。你应该认真的思考一下,这个 输出的父类的非静态块和非静态属性初始化的语句到底是从何而来?