• 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编程思想第四版》

  • 相关阅读:
    键盘事件
    冒泡事件-捕获事件-阻止事件
    Date()常用属性
    dom树节点的增删改插
    boost/c++11 变量类型打印、相等判断以及成员函数指针相关
    c++ std:call_once 与 单例模式实现
    c++11 异步编程 std::async
    c++ STL中一些常用函数/模板
    c++11 std::atomic 原子操作
    C++ std::atomic_flag 实现spinLock
  • 原文地址:https://www.cnblogs.com/lizijuna/p/11907403.html
Copyright © 2020-2023  润新知