深入class文件了解静态代码块、代码块、构造器顺序

本文详细解析了Java中类的加载过程、静态与非静态代码块的执行时机,以及构造器的调用顺序。通过具体示例展示了不同情况下代码的执行流程。

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

面试题 1

class Root{
static {
        System.out.println("Root的静态初始化快");
        }
        {
        System.out.println("Root的普通初始化快");
        }

public Root() {
        System.out.println("Root的无参数的构造器");
        }
        }
class Mid extends Root{
    static {
        System.out.println("Mid静态代码块");
    }
    {
        System.out.println("MId的普通初始代块");
    }

    public Mid() {
        System.out.println("Mid的无参数的构造器");
    }

    public Mid(String msg) {
        //通过this调用同一类中重载的构造器
        this();
        System.out.println("Mid的带参数的构造器,其参数值:"+msg);
        System.out.println("Mid的无参构造器");
    }

}
class Leaf extends Mid{
    static int age;
    static {
        System.out.println("Left的静态初始化快");
    }
    {
        System.out.println("Left的普通初始化快");
    }

    public Leaf() {
        //通过super调用父类中带有一个字符串参数逇构造器
        super("郭京伟");
        System.out.println("Left的构造器");
    }
}
public class LeafTest {
    public static void main(String[] args) {
//        System.out.println(Leaf.age);
        new Leaf();
    }

}
Root的静态初始化快
Mid静态代码块
Left的静态初始化快
Root的普通初始化快
Root的无参数的构造器
MId的普通初始代块
Mid的无参数的构造器
Mid的带参数的构造器,其参数值:郭京伟
Mid的无参构造器
Left的普通初始化快
Left的构造器

Process finished with exit code 0

总结:由父及子

  1. 当你调用main方法时,其实是通过类名.main调用的
  2. 然后类需要先加载,加载当前类的时候会先把依赖的类先加载
  3. 加载类的时候会执行其中的静态代码块
  4. 调用构造器创建对象的时候会先执行普通代码块中的方法,其实你看字节码文件的时候你会发现,普通代码块的内容会出现在构造器中
//class字节码文件
class Root {
    public Root() {
        System.out.println("Root的普通初始化快");
        System.out.println("Root的无参数的构造器");
    }

    static {
        System.out.println("Root的静态初始化快");
    }
}
  1. 在构造器的首行,没有显式的声明this(形参列表) 或 super(形参列表) 则默认调用的是父类的构造器

面试题 2

class Father{
    static {
        System.out.println("111父亲的静态代码块");
    }
    {
        System.out.println("222父亲的非静态代码块");
    }

    public Father() {
        System.out.println("333父亲的无参构造");
    }
}
public class Son extends Father{
    static {
        System.out.println("444Son中静态代码块");
    }
    {
        System.out.println("555Son中非静态代码块");
    }

    public Son() {
        System.out.println("666Son中的无参构造");
    }
    //虽然作为方法的入口,但是也是个静态方法,通过类去调,类去调之前要先
    //加载
    public static void main(String[] args) {
        System.out.println("777Son中静态main方法");
        System.out.println("******************");
        new Son();
        System.out.println("------------------");
        new Son();
        System.out.println("++++++++++++++++++");
        new Father();
    }
}
111父亲的静态代码块
444Son中静态代码块
777Son中静态main方法
******************
222父亲的非静态代码块
333父亲的无参构造
555Son中非静态代码块
666Son中的无参构造
------------------
222父亲的非静态代码块
333父亲的无参构造
555Son中非静态代码块
666Son中的无参构造
++++++++++++++++++
222父亲的非静态代码块
333父亲的无参构造

Process finished with exit code 0

面试题 3

public class OrderTest {
    public static void main(String[] args) {
        Order order = new Order();
        System.out.println(order.orderId);
    }
}
class Order{
    {
        orderId=4;
        //       System.out.println(orderId);  // 非法前向引用
        System.out.println("执行到这");
    }
    int orderId=3;



}
执行到这
3

结果: 这两个执行顺序是看是谁在前就先执行谁,谁在后就后执行谁. 但是不能非法向前引用

可以看看对应的class文件,编译器会进行优化整理的

class Order {
    int orderId = 4;

    Order() {
        System.out.println("执行到这");
        this.orderId = 3;
    }
}

面试题 4

public class Test {
    public static Test t1=new Test();{
        System.out.println("blockA");}
    static {
        System.out.println("blockB");
    }
    public static void main(String[] args)
    {
        Test t2=new Test();
    }
}

结果:

blockA
blockB
blockA    

那我们通过断点和class文件看看为什么

  1. 通过类名.main调用main方法,那么就会去加载这个类
  2. 然后到达① 执行,这时候又会重新进入这个类,这是会认为这个类是已经加载完成的,就会直接执行普通代码块方法② 输出:blockA,然后构造器方法,最后出出栈回到上次的地方
  3. 接着执行静态代码块的方法,静态代码块优先于普通代码块,输出blockB,最后在执行普通代码块中的方法。

在这里插入图片描述

变形:我加个无参构造器会怎么样

public class Test {
    public static Test t1=new Test();
    {
        System.out.println("blockA");
    }
    static {
        System.out.println("blockB");
    }

    public Test() {
        System.out.println("AA");
    }

    public static void main(String[] args)
    {
        Test t2=new Test();
    }
}
结果:
blockA
AA
blockB
blockA
AA

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值