有过java基础的同学肯定绕不开这个小山坡,静态代码块、非静态代码块、静态方法、构造方法、构造代码块,这些,哪些先执行,哪些后执行,为什么呢?
首先,先说下论点,再用code佐证,最后探讨机制。
java类加载顺序
1、虚拟机在首次加载Java类时,会对静态初始化块、静态成员变量、静态方法进行一次初始化 2、只有在调用new方法时才会创建类的实例 3、类实例创建过程:按照父子继承关系进行初始化,首先执行父类的初始化块部分,然后是父类的构造方法;再执行本类继承的子类的初始化块,最后是子类的构造方法 4、类实例销毁时候,首先销毁子类部分,再销毁父类部分
非静态代码块是在每次实例化对象时被调用的。new对象之后发生的事情:给对象的实例变量(非“常量”)分配内存空间,默认初始化成员变量;成员变量声明时的初始化;初始化块初始化(又称为构造代码块或非静态代码块);构造方法初始化。
示例代码
public class Test01 {
public static void main(String[] args) {
B b = new B();
}
}
class B extends A{
static{
System.out.println("子类的静态代码块");
}
C c = new C();
{
System.out.println("子类的构造代码块");
C.method();
}
B(){
System.out.println("子类的构造方法");
}
}
class A{
static{
System.out.println("父类的静态代码块");
}
C c = new C();
A(){
System.out.println("父类的构造方法");
}
}
class C{
static public void method(){
System.out.println("C的静态方法");
}
static{
System.out.println("C类的静态代码块");
}
C(){
System.out.println("C的构造方法");
}
}
代码运行的结果:
父类的静态代码块
子类的静态代码块
C类的静态代码块
C的构造方法
父类的构造方法
C的构造方法
子类的构造代码块
C的静态方法
子类的构造方法
解读:
首次加载Java类时,会对静态初始化块、静态成员变量、静态方法进行一次初始化,而B继承A,本来要先执行B的静态代码块,但按照父子关系初始化,A加了个塞,先执行A的静态代码块,打印“父类的静态代码块”;B的静态代码块也加载,打印“子类的静态代码块”;在父类中把C类也加载进来了,静态代码块执行,打印“C类的静态代码块”,C在new的时候,构造方法执行,打印“C的构造方法”;然后执行到A的构造方法,打印“父类的构造方法”;接着在B类继续向下执行,又new了个C,打印“C的构造方法”,然后向下执行非静态代码块,打印“子类的构造代码块”以及“C的静态方法”;最后B类的构造方法执行,打印“子类的构造方法”。
机制
具体内存中如何加载,暂时还没时间看,TODO,等看到java的类加载机制再详细探讨。
根据反编译的结果来看,整个运行过程是这样的:
public class Test01 {
public static void main(String[] args) {
B b = new B();
}
}
class B extends A{
static{
System.out.println("子类的静态");
}
C c ;
B(){
super();
c = new C();
System.out.println("子类的构造代码块");
C.method();
System.out.println("子类的构造方法");
}
public void method(){
System.out.println("子类的成员方法");
}
}
class C{
static public void method(){
System.out.println("C的静态方法");
}
static{
System.out.println("C类的静态");
}
C(){
super();
System.out.println("C的构造方法");
}
}
class A{
static{
System.out.println("父类的静态");
}
C c ;
A(){
super();
c = new C();
System.out.println("父类的构造方法");
}
}
构造方法里面,先super(),再成员变量(c=new C()),再其他代码,操作完毕,依次读取。这就是内存中真实的运行规则。
扩展
看下面的题,请问输出的是什么
public class Test03 {
public static void main(String[] args) {
Zi zi = new Zi();
}
}
class Zi extends Fu{
public int count=3;
public Zi(){
}
public void print(){
System.out.println("Zi..."+count);
}
}
class Fu{
public int count=10;
public Fu(){
print();
}
public void print(){
System.out.println("Fu..."+count);
}
}
同之前的逻辑,改造后代码如下:
public class Test03 {
public static void main(String[] args) {
Zi zi = new Zi();
}
}
class Zi extends Fu{
public int count=0;//默认初始值为0
public Zi(){
super();
count=3;
}
public void print(){
System.out.println("Zi..."+count);
}
}
class Fu{
public int count=0;
public Fu(){
super();
count=10;
print();
}
public void print(){
System.out.println("Fu..."+count);
}
}
Zi zi = new Zi(),过去找子类的构造方法,super(),此时子类的count还是0,找父类的构造方法,父类,的count改为10,print(),子类自己有重写,找子类自己的,count也是找子类自己的,此时还是0,结果Zi...0