Java中的前向引用与类初始化顺序

一.什么是向前引用?
  有过C++编程经验的就会知道,一个变量或者方法总是需要先声明再使用。那么Java里面如下的代码是可以的吗?

public class GoFirst{
    int m = n;//illegal forward reference,无法编译,报错
    int n = 1;
    }

也许我们可以做一些迷惑编译器的代码以达到前向引用的目的。

public class GoFirst{
    int m = method();;//可以编译,此时n已被初始化为0
    int n = 1;
    int method() {return n; }
    }
    public static void main(String[] args) {
        GoFirst goFirst=new GoFirst();
        System.out.println(goFirst.m);//0,首先按照成员声明顺序装载成员字段,m初始化时n未被初始化,为默认值0
        System.out.println(goFirst.method());//1,此时所有字段初始化已经完毕
        System.out.println(goFirst.n);//1
    }

  在GoFirst类被初始化的时候,第一次的初始化:此时的GoFirst的所有成员变量均被初始化为各种数据类型的初始值,此时的成员变量已经为默认值(int类型的默认值为0,此次初始值均为编译器给定的默认值),第二次的初始化:按照成员变量声明的顺序设置我们想要初始值。如m先被设置为method()的返回值,再初始化n的值为1。

下面是《Java编程思想第四版》中对象创建过程的描述,假设有一个Dog类:

  1.首次创建Dog的对象时或者Dog类的静态方法/静态域首次被访问时,Java解释器必须查找类路径,以定位Dog.class文件
  2.载入Dog.class(这将会创建一个Class对象)文件,有关静态初始化的所有动作都会执行,静态初始化只在Class对象首次加载的时候进行一次
  3.当用new Dog()创建对象的时候,首次将在堆上为Dog对象分配足够的存储空间
  4.将这片存储空间清零,这就自动的将Dog对象的基本数据类型都设置为了默认值,而引用则被设置为了null
  5.执行所有出现于字段定义处的初始化动作
  6.执行构造器

二.类初始化顺序
  Ⅰ.单个类加载顺序

/**
 * 〈类的初始化顺序测试〉
 *
 * @author 龙
 * @create 2018/9/10 15:40
 * @since 1.0.0
 */
public class InitOrder {
    public  int number=initNumber();
    {
        System.out.println("初始化代码块!!!");
    }

    public static int staticA=initA();
    static {
        System.out.println("静态初始化代码块!!!");
    }
    public static int initA(){
        System.out.println("初始化静态字段!!!");
        return 5;
    }
    public int initNumber(){
        System.out.println("初始化成员字段!!!");
        return 5;
    }
    public static void main(String[] args) {
        System.out.println("main函数开始执行!!!");
        InitOrder initOrder=new InitOrder();
    }
}

  运行结果如下:

初始化静态字段!!!
静态初始化代码块!!!
main函数开始执行!!!
初始化成员字段!!!
初始化代码块!!!

  静态字段和静态代码块,初始化顺序只是按照代码顺序执行,初始化成员字段和初始化代码块同理。由于main函数为这个类的静态成员,所以在main函数执行前依然需要先初始化该类的其它静态字段和静态代码块。如果在main方法里面不新建InitOreder对象,则成员字段和代码块将不会被初始化。即不会打印后面两句

  Ⅰ.多个类加载顺序

public class Parent {
    public  int numberP=initNumberP();

    public static int staticP=initStaticP();
    public Parent(){
        System.out.println("父类无参构造函数!!!");
    }
    public Parent(String s){
        System.out.println("父类有参构造函数!!!");
    }
    public static int initStaticP(){
        System.out.println("父类初始化静态字段!!!");
        return 5;
    }
    public int initNumberP(){
        System.out.println("父类初始化成员字段!!!");
        return 5;
    }


}
class Son extends Parent{
    public  int numberS=initNumberS();

    public static int staticS=initStaticS();
    public Son(){
        super("string");//没有该句则默认调用父类的无参构造函数
        System.out.println("子类构造函数!!!");
    }
    public static int initStaticS(){
        System.out.println("子类初始化静态字段!!!");
        return 5;
    }
    public int initNumberS(){
        System.out.println("子类初始化成员字段!!!");
        return 5;
    }

    public static void main(String[] args) {
        System.out.println("main函数开始执行!!!");
        Son son=new Son();
    }
}

  运行结果如下:

父类初始化静态字段!!!
子类初始化静态字段!!!
main函数开始执行!!!
父类初始化成员字段!!!
父类有参构造函数!!!
子类初始化成员字段!!!
子类构造函数!!!

  就继承而言,父类静态成员>子类静态成员>父类实例成员>子类实例成员


参考资料:
  《Java编程思想第四版》

posted @ 2018-05-26 17:32  李子君啊  阅读(241)  评论(0编辑  收藏  举报