一、初始化顺序:父类的静态变量-->父类的静态代码块-->子类的静态变量-->子类的静态代码快-->父类的非静态变量(父类的非静态代码块)-->父类的构造函数-->子类的非静态变量(子类的非静态代码块)-->子类的构造函数java
package com.test; public class Test { public static void main(String[] args) { Child c=new Child(); } } class Parent { public static PrintMessage a=new PrintMessage("父类静态成员被初始化"); private PrintMessage b=new PrintMessage("父类非静态成员被初始化"); static{ System.out.println("父类的静态代码块被执行"); } { System.out.println("父类的非静态代码块被执行"); } public Parent(){ System.out.println("父类的构造方法被执行"); } } class Child extends Parent{ public static PrintMessage a1=new PrintMessage("子类静态成员被初始化"); private PrintMessage b1=new PrintMessage("子类非静态成员被初始化"); static { System.out.println("子类的静态代码块被执行"); } { System.out.println("子类的非静态代码块被执行"); } public Child(){ System.out.println("子类的构造函数被执行"); } } class PrintMessage{ public PrintMessage(String mes){ System.out.println(mes); } }
执行结果:数据结构
父类静态成员被初始化
父类的静态代码块被执行
子类静态成员被初始化
子类的静态代码块被执行
父类非静态成员被初始化
父类的非静态代码块被执行
父类的构造方法被执行
子类非静态成员被初始化
子类的非静态代码块被执行
子类的构造函数被执行函数
二、JAVA 类的加载机制spa
Java类加载分为5个过程,分别为:加载,链接(验证,准备,解析),初始化,使用,卸载。设计
1. 加载
加载主要是将.class文件(也能够是zip包)经过二进制字节流读入到JVM中。 在加载阶段,JVM须要完成3件事:
1)经过classloader在classpath中获取XXX.class文件,将其以二进制流的形式读入内存。
2)将字节流所表明的静态存储结构转化为方法区的运行时数据结构;
3)在内存中生成一个该类的java.lang.Class对象,做为方法区这个类的各类数据的访问入口。code
2.链接对象
2.1. 验证
主要确保加载进来的字节流符合JVM规范。验证阶段会完成如下4个阶段的检验动做:
1)文件格式验证
2)元数据验证(是否符合Java语言规范)
3)字节码验证(肯定程序语义合法,符合逻辑)
4)符号引用验证(确保下一步的解析能正常执行)
2.2. 准备
准备是链接阶段的第二步,主要为静态变量在方法区分配内存,并设置默认初始值。
2.3. 解析
解析是链接阶段的第三步,是虚拟机将常量池内的符号引用替换为直接引用的过程。继承
3. 初始化ip
初始化阶段是类加载过程的最后一步,主要是根据程序中的赋值语句主动为类变量赋值。
当有继承关系时,先初始化父类再初始化子类,因此建立一个子类时其实内存中存在两个对象实例。
注:若是类的继承关系过长,单从类初始化角度考虑,这种设计不太可取。缘由我想你已经猜到了。
一般建议的类继承关系最多不超过三层,即父-子-孙。某些特殊的应用场景中可能会加到4层,但就此打住,第4层已经有代码设计上的弊端了。内存
4. 使用
程序之间的相互调用。
5. 卸载
即销毁一个对象,通常状况下中有JVM垃圾回收器完成。代码层面的销毁只是将引用置为null。
经过上面的总体介绍后,再来看Singleton2.getInstance()的执行分析:
1)类的加载。运行Singleton2.getInstance(),JVM在首次并无发现Singleton类的相关信息。因此经过classloader将Singleton.class文件加载到内存中。
2)类的验证。略
3)类的准备。将Singleton2中的静态资源转化到方法区。value1,value2,singleton在方法区被声明分别初始为0,0,null。
4)类的解析。略(将常量池内的符号引用替换为直接引用的过程)
5)类的初始化。执行静态属性的赋值操做。按照顺序先是value1 = 5,value2 = 3,接下来是private static Singleton2 singleton2 = new Singleton2();
这是个建立对象操做,根据 结论1 在执行Singleton2的构造方法以前,先去执行static资源和非static资源。但因为value1,value2已经被初始化过,因此接下来执行的是非static的资源,最后是Singleton2的构造方法:value1++;value2++。
因此Singleton2结果是6和4。
以上除了搞清楚执行顺序外,还有一个重点->结论2:静态资源在类的初始化中只会执行一次。不要与第3个步骤混淆。
有了以上的这个结论,再来看Singleton.getInstance()的执行分析:
1)类的加载。将Singleton类加载到内存中。
2)类的验证。略
3)类的准备。将Singleton2的静态资源转化到方法区。
4)类的解析。略(将常量池内的符号引用替换为直接引用的过程)
5)类的初始化。执行静态属性的赋值操做。按照顺序先是private static Singleton singleton = new Singleton(),根据 结论1 和结论2,value1和value2不会在此层执行赋值操做。因此singleton对象中的value1,value2只是在0的基础上进行了++操做。此时singleton对象中的value1=1,value2=1。 而后, public static int value1 = 5; public static int value2 = 3; 这两行代码才是真的执行了赋值操做。因此最后的结果:5和3。 若是执行的是public static int value1; public static int value2;结果又会是多少?结果: 1和1。