从字节码角度来解释:如果子类继承父类,当创建子类对象的时候,子类和父类的静态变量、静态代码块、实例变量、构造代码块、构造方法的执行顺序

代码

class A {
    public static String a1 = "静态属性";
    String a2 = "实例变量";

    static {
        System.out.println("A---》" + a1);
        System.out.println("A---》静态代码块");
    }

    {
        System.out.println("A---》" + a2);
        System.out.println("A---》构造代码块");
    }

    public A() {
        System.out.println("A---》构造方法");
    }
}

class B extends A {
    public static String b1 = "静态属性";
    String b2 = "实例变量";

    static {
        System.out.println("B---》" + b1);
        System.out.println("B---》静态代码块");
    }

    {
        System.out.println("B---》" + b2);
        System.out.println("B---》构造代码块");
    }

    public B() {
        System.out.println("B---》构造方法");
    }
}

public class Test {
    public static void main(String[] args) {
        System.out.println("第一次:");
        new B();
        System.out.println("--------------隔离横线-----------------");
        System.out.println("第二次:");
        new B();
    }
}

结果

第一次:
A---》静态属性
A---》静态代码块
B---》静态属性
B---》静态代码块
A---》实例变量
A---》构造代码块
A---》构造方法
B---》实例变量
B---》构造代码块
B---》构造方法
--------------隔离横线-----------------
第二次:
A---》实例变量
A---》构造代码块
A---》构造方法
B---》实例变量
B---》构造代码块
B---》构造方法

解释

我们来看Test类中的main方法,里面有一行代码是:new B(),此时B类还没有加载,那我们需要先来加载B类,这个过程在类加载子系统中完成,主要过程如下:
在这里插入图片描述
其中加载阶段没有什么重要内容,主要看链接阶段,具体内容如下:
在这里插入图片描述
根据上面可以知道在本阶段类静态变量会进行初始化、类静态常量会进行显示赋值,所以B类中的b1变量就会初始化为"",这就是链接阶段的主要内容

现在来看初始化阶段,具体内容如下:
在这里插入图片描述
可以看出该阶段会执行<client>()方法,该方法主要作用是进行类变量的显示赋值以及执行static静态代码块中的内容,这两个过程的执行顺序取决于他们的代码执行顺序,另外该方法执行之前需要先加载父类,由于B类的父类A类,所以A类需要先完成加载、链接、初始化之后才会轮到B类的初始化方法执行

A类的加载过程不再说了,链接过程会将类静态变量a1初始化为"",初始化阶段会执行<client>()方法,所以类静态变量a1会被显示赋值为"静态属性",然后执行static静态代码块中的内容,因此会先执行如下代码:

System.out.println("A---》" + a1);
System.out.println("A---》静态代码块");

因此会先输出:

A---》静态属性
A---》静态代码块

到此为止A类已经加载完了,现在来执行B类初始化中的<client>()方法,所以类静态变量b1会被显示赋值为"静态属性",然后执行static静态代码块中的内容,因此会先执行如下代码:

System.out.println("B---》" + b1);
System.out.println("B---》静态代码块");

因此会先输出:

B---》静态属性
B---》静态代码块

现在B类和A类都已经加载完毕了,那么就需要创建对象了,我们来看一下Test类的字节码文件:

在这里插入图片描述
然后看B类的<init>方法,如下:
在这里插入图片描述
现在我们知道了在子类对象初始化之前需要先初始化其父类对象<init>方法中执行如下操作:

  1. 默认初始化
  2. 显式初始化 / 代码块中初始化(这两个的执行顺序取决于代码顺序)
  3. 构造器中初始化

所以A类对象中的a2变量将会默认初始化为"",然后显示初始化为"实例变量",之后执行代码块,代码如下:

{
    System.out.println("A---》" + a2);
    System.out.println("A---》构造代码块");
}

因此会输出:

A---》实例变量
A---》构造代码块

A类对象执行<init>方法之后,就该执行B类对象<init>方法了,那么B类对象中的b2变量将会默认初始化为"",然后显示初始化为"实例变量",之后执行代码块,代码如下:

{
    System.out.println("B---》" + b2);
    System.out.println("B---》构造代码块");
}

因此会输出:

B---》实例变量
B---》构造代码块

现在已经执行了Test类main()方法中的第一行new B()代码

现在来看Test类main()方法,里面的其他代码不在看了,我们直接来看该方法中的第一行new B()代码

由于B类B类的父类A类都已经加载过了,所以不用在经过类加载子系统中的加载、链接、初始化阶段,所以只会执行B类的<init>方法,然后会先执行A类中的<init>方法,具体过程上面已经描述的很清晰了,这里不在赘述,所以会输出如下内容:

A---》实例变量
A---》构造代码块
A---》构造方法
B---》实例变量
B---》构造代码块
B---》构造方法

到此就得出了所有结果

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值