Java实例初始化过程

转载自http://mp.weixin.qq.com/s?__biz=MjM5Mzk3MzA4MA==&mid=2651366281&idx=3&sn=fe98210a7115afff01b35da044a71ea9&scene=0#wechat_redirect
文章通过Java字节码从下面几个场景来分析Java实例的初始化过程:

  1. 成员+构造函数
  2. 成员+代码块+构造函数
  3. 静态变量+静态代码块
  4. 继承和多态

成员+构造函数

  1. 有赋值的类成员属性是按声明的位置先后进行初始化(与访问标志符无关)
  2. 成员属性的初始化会优先于构造函数的初始化
  3. 初始化动作都是在构造函数中完成的, 如果没有显示构造函数,那么编译器会产生一个无入参构造函数来完成初始工作
  4. 建议声明成员属性时没有必要赋于null,等到真实需要使用成成员时再初始化或传递值。(在声明时赋于null,所以我们可以看到指令也会进行aconst_null的操作,但是在(6)时再次对myText2进行了赋值并再次产生了指令操作。注:null本身不是一种对象,在JVM中没有明确的指明采用什么类型,不同的JVM实现可能不一样,我们可以简单理解为null是一个标志,告诉虚拟机对应的类型还不明确,并还未为其分配空间。)

成员+代码块+构造函数

  1. 非静态代码块的执行也是被放到构造函数中
  2. 非静态代码块并不影响代码顺序的初始化工作
  3. 尽量不要有非静态的代码块,可读性不好,需要在非静态代码块解决的问题完全可以移到构造函数中

静态变量+静态代码块

因为静态变量和静态代码块与所在的类被实例化个数无关,而是所在类被虚拟机加载时会执行对应的静态代码块的字节码,这是类被加载事件触发的,并会因为类实例才会有,比如第一次执行下面的非实例化的代码同样会触发该类的静态代码块执行。

System.out.println(StaticFieldInitialize.class);
//或
Object obj = new Object();
if(obj instanceof StaticFieldInitialize){

}

继承和多态

  1. 一性次:优先加载类时会初始化静态成员/静态代码块 ,顺序为父类 ->子类,每个类在JVM只会被初始化一次,除非类被卸载再加载;
  2. 接着会按继承关系执行:父类成员属性/非静态代码块->父类构造函数 ->成员属性/非静态代码 -> 构造函数
public class Parent {
    private String name = "parent";
    public Parent(){
        printName();
    }

    public void printName() {
        Log.e("Test", "parent = " + name);
    }
}

public class Child extends Parent {
    private String name = "child";
    public Child(){

    }

    public void printName() {
        Log.e("Test", "child = " + name);
    }
}

Parent parent = new Child();

这是一个非常简单继承关系,Chlid继承了Parent类并重写了printName方法,是个典型的多态特性。
分析:

  1. 在第一个场景中我们知道在Child类初始化自己构造函数时,第一步会优先调用它的父类构造函数,而这时Child类的name属性还未被赋值,即它还是null;
  2. 在执行父类构造函数时,调用Object构造函数后,先执行Parent类的name成员赋值的字节码,紧张着会调用printName方法,但从源代码层面看这似乎就是调用Parent的printName方法,但是事实非如此。在Parent类中的this并不是代码Parent实例本身,而是代表Child类的实例引用,所以此时运行调用时会优先到Child实例上去寻找printName方法,如果有该方法就执行,没有则执行父类的。
  3. 当通过运行时动态调用Child实例printName方法时,Child类的name属性还未初始化,所以看到输出 child = null的结果。

结果:child = null
但是如果printName()变成私有的,即private void printName(),那结果就是:parent = parent

你可能感兴趣的:(Java实例初始化过程)