《Thinking in Java》——静态、非静态成员变量、子句的初始化顺序(含继承)及对象创建过程

本文详细探讨了Java中的初始化顺序,包括静态和非静态成员变量、子句的初始化,对象创建步骤以及继承关系下的初始化流程。内容涵盖静态成员的唯一初始化、构造器的执行时机、对象内存分配和默认值设定,以及在继承链中的类加载和初始化顺序。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

初始化顺序(详细过程可看继承与初始化部分,相当于没有基类的子类)

1.静态成员变量或子句只有在首次new该类对象或者首次访问该类成员变量或方法时被执行一次;
2.非静态成员变量或子句在每次new该类对象时都会初始化;
3.构造器在成员变量和子句初始化之后才执行,而且需要new语句才会被执行;
4.成员变量和子句之间顺序执行;
5.包含main()方法的类,也需要先初始化静态成员变量,再执行main()方法。

所有的static对象和static代码段都会在加载时依程序中的顺序(即,定义类时的书写顺序)而依次初始化。当然,定义为static的东西只会在被初始化一次。

  • 当通过new 类名()产生一个新对象时,顺序为:
    1所有.静态成员变量或子句(仅一次)
    2.所有非静态成员变量或子句(new几个对象,执行几次)
    3.构造器

  • 当通过类名直接访问某类的静态成员变量或者方法时,顺序为:
    1.所有静态成员变量或子句(仅一次)
    2.访问变量或方法

对象创建过程

假设有一个Dog类

  1. 构造器实际上是静态方法,因此,首次创建类型为Dog的对象时,或者Dog类的静态方法、静态域首次被访问时,Java解释器必须查找类的路径,以定位Dog.class文件;
  2. 然后载入Dog.class文件,有关静态初始化的所有动作被执行。因此,静态初始化只在Class对象首次加载的时候进行一次;
  3. 当用new Dog()创建对象的时候,首先将在堆上为Dog对象分配足够的存储空间;
  4. 这块存储空间会被清零,这就自动地将Dog对象中的所有基本类型数据都设置成了默认值(对于数字来说是0,对于布尔类型和字符类型也相同),而引用则被设置成了null;
  5. 执行所有出现在成员变量定义处的初始化动作;
  6. 执行构造器。

继承与初始化

package thinkinginjava;

class Insect {
    private int i = 9;
    protected int j;
    Insect() {
        System.out.println("i = " + i + ", j = " + j);
        j = 39;
    }
    private static int x1 = printInit("static Insect.x1 initialized");
    static int printInit(String s) {
        System.out.println(s);
        return 47;
    }
}

public class Beetle extends Insect {
    private int k = printInit("Beetle.k initialized");
    public Beetle() {
        System.out.println("k = " + k);
        System.out.println("j = " + j);
    }
    private static int x2 = printInit("static Beetle.x2 initialized");
    public static void main(String[] args) {
        System.out.println("Beetle constructor");
        Beetle b = new Beetle();
    }
}

结果:

static Insect.x1 initialized
static Beetle.x2 initialized
Beetle constructor
i = 9, j = 0
Beetle.k initialized
k = 47
j = 39
  • 加载类(子类→基类→基类的基类……)
    在Beetle上运行Java时,所发生的第一件事就是试图访问Beetle.main()(一个static方法),因为类在其static成员首次被访问时加载,于是加载器开始启动并找出Beetle类的编译代码(在名为Beetle.class的文件中)。在对它进行加载的过程中,编译器注意它有一个基类(这是由关键字extends得知的),于是它继续加载。不管你是否打算产生一个该基类的对象,这都要发生。
    如果该基类还有其自身的基类,那么第二个基类将会被加载,以此类推。
  • static初始化(根基类→根基类的子类→根基类子类的子类……)
    根基类的static初始化先进行,然后是它的子类,以此类推,这种顺序很重要,因为导出类的static初始化可能会依赖于基类成员能否被正确的初始化。
  • 设置默认值
    类加载完毕后,对象就可以被创建了。对象中所有的基本类型被设置成0,引用被设置为null——这是通过将对象内存设置为二进制零值而一举生成的(在加载类时,我们已经能够得知该对象占有多少内存了,这其中包括基类的非静态成员变量,子类的非静态成员变量,它们都在这块内存中,会被同时设置为默认值)。
  • 调用子类构造器时先调用基类构造器
    显示或者隐式调用基类的构造器
  • 基类构造器完成之后,实例变量按其次序被初始化
  • 构造器的其余部分被执行
    (当基类还有基类时,会以相同的顺序执行上述三个过程,直至根基类)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值