java--关于new对象的时候有继承关系的类各种成员的加载顺序的超级详细解释


如果在网上搜索“类初始化顺序”,一般的答案为:

父类静态->子类静态->父类代码块->父类构造->子类代码块->子类构造

但这种简单的回答适合死记硬背但不适合理解
下面给出最详细的解释:

一、无继承关系(直接继承Object)的类加载过程:

1.静态成员是由类加载器加载的

静态成员是由类加载器加载的,而且类加载器只加载静态成员,不会加载实例成员与构造器

class Supper {
    {
        System.out.println("supper加载成员代码块");
    }
    String noStaticVar1 = "supper加载成员变量";
    static String staticVar = "supper加载静态变量";
    static{
        System.out.println(staticVar);
        System.out.println("supper加载静态代码块");
    }
    public Supper() {
        System.out.println("supper加载空参构造器");
    }
}
class TestClassLoad {
    public static void main(String[] args) throws ClassNotFoundException {
        ClassLoader supperClassLoader = Supper.class.getClassLoader();  // 通过反射获取类加载器
        // supperClassLoader.loadClass("Supper");  这种方法不会对类成员进行初始化
        Class.forName("Supper",true,supperClassLoader); // 加载Supper类并对静态成员,true表示进行初始化
    }
}
// 输出:
supper加载静态变量
supper加载静态代码块
supper加载静态代码块
2.代码块与变量按代码中出现的顺序进行初始化

这种说法可以看为main函数中的一个变量定义与一行语句,new 对象时先由加载器加载【静态变量/静态代码块】,然后实例化过程中加载【成员变量/成员代码块】

class Supper {
    {
        // System.out.println(noStaticVar); 编译报错:变量未定义
        System.out.println("supper加载成员代码块");
    }
    String noStaticVar = "supper加载成员变量";
    {
        System.out.println(noStaticVar);
        System.out.println("supper加载成员代码块1");
    }
    String noStaticVar1 = "supper加载成员变量1";
    static String staticVar = "supper加载静态变量";
    static{
        System.out.println(staticVar);
        // System.out.println(staticVar1); 编译报错:变量未定义
        System.out.println("supper加载静态代码块");
    }
    static String staticVar1 = "supper加载静态变量";
    static{

        System.out.println("supper加载静态代码块1");
    }
}
class TestClassLoad {
    public static void main(String[] args) throws ClassNotFoundException {
        new Supper();
    }
}
// 输出
supper加载静态变量
supper加载静态代码块
supper加载静态代码块1
supper加载成员代码块
supper加载成员变量
supper加载成员代码块1
3.new对象时只会进入一个相应的构造器

而且静态成员只加载一次。
下面,变量与代码块我们只保留一个,然后我们new两个Supper,一个带餐一个不带参:

class Supper {
    public String noStaticVar = "成员变量";
    {
        System.out.println("supper加载成员代码块");
    }
    static{
        System.out.println("supper加载静态代码块");
    }
    public Supper() {
        System.out.println("supper加载空参构造器");
    }
    public Supper(String noStaticVar) {
        this.noStaticVar = noStaticVar;
        System.out.println("supper加载有参构造器");
    }
}
class TestClassLoad {
    public static void main(String[] args) throws ClassNotFoundException {
        new Supper();
        System.out.println("*********");
        new Supper("supper");
    }
}
// 输出:
supper加载静态代码块
supper加载成员代码块
supper加载空参构造器
*********
supper加载成员代码块
supper加载有参构造器

二、继承关系下的类加载过程:

1.先加载父类静态成员,后加载本类静态成员

先只保留静态成员,只看类加载器做的事情:

class Supper {
    static{
        System.out.println("supper加载静态代码块");
    }
}

class Sub extends Supper {
    static {
        System.out.println("Sub加载静态代码块");
    }
}

class TestClassLoad {
    public static void main(String[] args) throws ClassNotFoundException {
        ClassLoader supperClassLoader = Sub.class.getClassLoader();  
        Class.forName("Sub",true,supperClassLoader); 
    }
}

// 输出:
supper加载静态代码块
Sub加载静态代码块
2.终极测试

import org.junit.Test;

public class JavaStudy {
    static {
        System.out.println("sdfbjahhdbojfasd");
    }
    @Test
    public void lambda1() {
        String[] strings = new String[0];
        System.out.println(strings);
        for (String string : strings) {
            System.out.println(string);
        }
    }
}


class Supper {
    public String noStaticVar = "supper成员变量";
    {
        System.out.println("supper加载非静态成员");
    }
    static{
        System.out.println("supper加载成员");
    }
    public Supper() {
        System.out.println("supper加载空参构造器");
    }
    public Supper(String noStaticVar) {
        this.noStaticVar = noStaticVar;
        System.out.println("supper加载有参构造器");
    }
}

class Sub extends Supper {
    public String noStaticVar = "Sub成员变量";
    {
        System.out.println("Sub加载非静态成员");
    }
    static {
        System.out.println("Sub加载静态成员");
    }
    public Sub() {
        System.out.println("Sub加载构造器");
    }
    public Sub(String noStaticVar) {
        this.noStaticVar = noStaticVar;
        System.out.println("Sub加载有参构造器");
    }
}

class TestClassLoad {
    public static void main(String[] args) throws ClassNotFoundException {
        new Sub();
        System.out.println("***********");
        new Sub("sub");
    }
}
// 输出
supper加载静态成员
Sub加载静态成员
supper加载非静态成员
supper加载空参构造器
Sub加载非静态成员
Sub加载空參构造器
***********
supper加载非静态成员
supper加载空参构造器
Sub加载非静态成员
Sub加载有参构造器

从输出结果看出:

1.new Sub()时,加载顺序为

  • 父类静态成员 ->子类静态成员->父类非静态成员->父类 【空参】/ 【显式调用的】构造器 ->子类非静态成员 ->子类【】参构造器

2.new Sub(“带参数”)时,加载顺序为,默认调用空参构造器

  • 父类静态成员 ->子类静态成员->父类非静态成员->父类 【空参】/ 【显式调用的】构造器 ->子类非静态成员 ->子类【】参构造器

也就是说,除了子类构造器不一样之外,所有内容完全一样
而且如果debug可以发现执行顺序如下(注意第5部):
在这里插入图片描述
注意:父类成员变量没有被加载

3.最后的结论

所以可以得出一个结论,各类成员的加载过程为:

1.类加载器

  • 父类静态变量/静态代码块(定义的先后顺序)
  • 子类静态变量/静态代码块(定义的先后顺序)
    .

2.子类构造器

  • 父类成员代码块
  • 父类构造器(默认调用空参构造器,可显示调用)
  • 子类成员变量/成员代码块(定义的先后顺序)
  • 子类构造器下的语句
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

运维小菜

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

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

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

打赏作者

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

抵扣说明:

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

余额充值