1 | public class Test2 implements InitializingBean { |
初始化结果
1 | test2 |
初始化顺序
- 构造函数
- 成员变量构造
- xml中的入参摄入
- 构造函数后PostConstruct
- afterPropertiesSet
- xml中的初始化调用函数
普通类加载
1 | public class ClassLoadTest { |
准备过程
这个过程相当于给类变量分配内存并设置变量初始值的阶段,这些变量所使用的内存都将在方法区中进行分配。
针对上述例子:1
2
3test=null;
x=0;
y=0;
注意:这里有个特殊情况,如果该字段被final修饰,那么在准备阶段改字段就会被设置成咱们自定义的值。public static final int value = 11,在准备阶段就会直接赋值11,并不是该变量的初始值。
解析过程
将符号引用转换成直接引用的过程。这里有两个名词符号引用和直接引用。1
2* 符号引用:符号引用与虚拟机的布局无关,甚至引用的目标不一定加载到了内存中。符号可以是任何形式的字面量,只要使用时能够准确的定位到目标即可。
* 直接引用:直接引用可以直接指向目标的指针、相对偏移量或是一个能间接定位到目标的句柄。直接引用与虚拟机布局有关,如果有了直接引用,那么引用的目标必定已经在内存中存在。
初始化
在准备阶段,变量已经赋值过系统要求的默认值,在初始化阶段,则会根据程序制定的主观计划去初始化类变量和其他资源。这句话听起来有些绕口,根据上述例子,实际上就是:1
2test = new ClassLoadTest();// x = 1;y =1
y = 0;
这个过程,由于x咱们自己并没有去设定一个值,所以初始化阶段它不会发生任何改变,但是y咱们有设定一个值0,所以最后造成最终结果为x = 1;y = 0。
ps:在同一个类加载器下,一个类只会初始化一次。多个线程同时初始化一个类,只有一个线程能正常初始化,其他线程都会进行阻塞等待,直到活动线程执行初始化方法完毕。
如果将代码改为
1 | public class ClassLoadTest { |
就会得到不一样的结果
1 | y = 0; |
summary
- 先是准备阶段,将成员变量初始化,基础类型为0,对象用null。
- 然后是解析阶段,将对象一一赋值,从上到下,先从x,y开始,然后test。