• Java中的对象构造与初始化顺序


    参考文献:

    Java核心技术卷一
    Java程序的初始化顺序
    Java 代码块和类的执行调用顺序
    类的加载与初始化

    域初始化

    如果在构造器中没有显式地给域赋予初值,那么就会被自动地赋为默认值: 数值为 0、
    布尔值为 false、 对象引用为 null。

    也可以直接在实例域中设置一个有意义的初值,在执行构造器之前,先执行赋值操作,将所有类对象的该域设置为一样的值。比如:

    class Employee
    {
    private String name ="JXZ";
    }
    
    • 1
    • 2
    • 3
    • 4

    构造器

    如果在编写一个类时没有编写构造器, 那么系统就会提供一个无参数构造器。这个构造
    器将所有的实例域设置为默认值。

    仅当类没有提供任何构造器的时候, 系统才会提供一个默认的构造器。如果类中提供了至少一个构造器, 但是没有提供无参数的构造器, 则在构造对象时如果没有提供参数就会被视为不合法。比如在编写类的时候,已经给出了一个简单的构造器,但是想用new ClassName()构造实例,就必须提供一个默认的构造器(即不带参数的构造器),如果希望所有域被赋予默认值, 可以采用下列格式:

    public ClassName(){}
    
    • 1

    关键字 this 代表引用方法的隐式参数。但其还有一层用法,如果构造器的第一条语句形如 this(...)(如果这样使用,也只能放在第一条语句), 这个构造器将调用同一个类的另一个构造器。比如:

    public Employee(double s)
    {
    // 这里就会调用Employee(String, double)
    this("Employee #" + nextld, s);
    nextld++;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    初始化块

    在一个类的声明中,可以包含多个代码块,只要构造类的对象(实例化),这些块就会被执行,在下面有个例子中可以看到,装载类但没有实例化类的时候,这些普通代码块并没有被执行,与之相对的是静态代码块执行了。

    class Employee
    {
    	private static int nextld;
    	private int id;
    	private String name;
    	private double salary;
    	
    	// object initialization block
    	{
    	id = nextld;
    	nextld++;
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    《Java核心技术卷一》中对调用构造器的相关步骤总结如下:

    1. 所有数据域被初始化为默认值(0、false 或 null)
    2. 按照在类声明中出现的次序, 依次执行所有域初始化语句和初始化块
    3. 如果构造器第一行调用了第二个构造器,则执行第二个构造器主体
    4. 执行这个构造器的主体

    Java程序的初始化顺序

    在加入父子类以及静态代码块后,初始化顺序就复杂一点了,参考Java程序的初始化顺序总结如下:

    静态优于非静态,先父后子,变量域声明优于块,最后构造器

    1. 父类静态变量
    2. 父类静态代码块
    3. 子类静态变量
    4. 子类静态代码块
    5. 父类非静态变量
    6. 父类非静态代码块
    7. 父类构造函数
    8. 子类非静态变量
    9. 子类非静态代码块
    10. 子类构造函数

    实例代码:

    public class InitOrderDemo {
    
        public InitOrderDemo() {
            System.out.println("父类构造方法");
        }
    
        String b = "父类非静态变量";
    
        {
            System.out.println(b);
            System.out.println("父类非静态代码块");
        }
    
        static String a = "父类静态变量";
    
        static {
            System.out.println(a);
            System.out.println("父类静态代码块");
        }
    
        public static void superMethod() {
            System.out.println("父类普通静态方法");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    public class Derived extends InitOrderDemo {
    
        public Derived() {
            System.out.println("子类构造方法");
        }
    
        String b = "子类非静态变量";
    
        {
            System.out.println(b);
            System.out.println("子类非静态代码块");
        }
    
        static String a = "子类静态变量";
    
        static {
            System.out.println(a);
            System.out.println("子类静态代码块");
        }
    
        public static void derivedMethod() {
            System.out.println("子类普通静态方法");
        }
    
        public static void main(String[] args) {
    //        InitOrderDemo.superMethod();
            Derived.derivedMethod();
    //        new Derived();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    执行输出如下:

    InitOrderDemo.superMethod()
    父类静态变量
    父类静态代码块
    子类静态变量
    子类静态代码块
    父类普通静态方法

    Derived.derivedMethod():
    父类静态变量
    父类静态代码块
    子类静态变量
    子类静态代码块
    子类普通静态方法

    new Derived():
    父类静态变量
    父类静态代码块
    子类静态变量
    子类静态代码块
    父类非静态变量
    父类非静态代码块
    父类构造方法
    子类非静态变量
    子类非静态代码块
    子类构造方法

    其中InitOrderDemo.superMethod() Derived.derivedMethod()的打印只打印出了静态变量和静态代码块,和装载时域的初始化有关。参考类的加载与初始化

    同时参考代码块的两个注意事项:

    1. static代码块即静态代码块,随着类的加载而执行,是对类进行初始化,且只会执行一次,当之后再创建对象时,静态代码块不会再执行。普通代码块每创建一次对象便会执行一次。
    2. 普通代码块,在创建对象实例时,会被默认调用,每当创建一次,便会调用一次。如果只是使用类的静态成员时,普通代码块不会被调用执行
  • 相关阅读:
    Note——torch.size() & umr_maximum() array.max() & itertools.product()
    华为机试 - 最大股票收益
    众和策略:天天基金网怎么开户?
    大学毕业1年,从监工转行软件测试,我拿到了人生第一份8k的offer
    mysql-mysql的安装和一些基本设置
    【分析笔记】全志平台 TWI 上拉电压异常的问题
    凯撒密码用c语言实现
    Linux使用脚本关闭数据库
    Nacos安装指南以及集群搭建
    SpringBoot常用注解(详解)
  • 原文地址:https://blog.csdn.net/qq_44036439/article/details/128176527