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

参考文献:

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

域初始化

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

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

class Employee
{
private String name ="JXZ";
}

构造器

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

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

public ClassName(){}

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

public Employee(double s)
{
// 这里就会调用Employee(String, double)
this("Employee #" + nextld, s);
nextld++;
}

初始化块

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

class Employee
{
	private static int nextld;
	private int id;
	private String name;
	private double salary;
	
	// object initialization block
	{
	id = nextld;
	nextld++;
	}
}

《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("父类普通静态方法");
    }
}
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();
    }
}

执行输出如下:

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

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

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

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

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

  1. static代码块即静态代码块,随着类的加载而执行,是对类进行初始化,且只会执行一次,当之后再创建对象时,静态代码块不会再执行。普通代码块每创建一次对象便会执行一次。
  2. 普通代码块,在创建对象实例时,会被默认调用,每当创建一次,便会调用一次。如果只是使用类的静态成员时,普通代码块不会被调用执行
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

互联网民工蒋大钊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值