Java的类加载机制

类从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期包括:加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)和卸载(Unloading)7个阶段。其中准备、验证、解析3个部分统称为连接(Linking)。

Java中赋值顺序:

  1. 父类的静态变量赋值

  2. 自身的静态变量赋值

  3. 父类成员变量赋值和父类块赋值

  4. 父类构造函数赋值

  5. 自身成员变量赋值和自身块赋值

  6. 自身构造函数赋值

给出一段代码,请问输出结果是什么。

public class Test {

    public static void main(String[] args) {
        staticFunction();
    }

    static Test test = new Test();

    static {
        System.out.println("1");
    }

    {
        System.out.println("2");
    }

    Test() {
        System.out.println("3");
        System.out.println("a=" + a + ",b=" + b);
    }

    public static void staticFunction() {
        System.out.println("4");
    }

    int a = 110;
    static int b = 112;
}

正常来说,如果套用java的赋值顺序来思考的话,输出结果应该是1(静态块)和4(主函数)

但是实际的输出结果却是2,3,a=110,b=0,1,4。怎么输出的结果跟预想的不一样呢?

这里就涉及到了一个关键点:实例的初始化不一定是在类初始化结束之后才开始的。

在类的生命周期内,只有准备阶段和初始化阶段才会涉及到类变量的初始化和赋值。在准备阶段,jvm会为类变量分配内存并赋默认值,类变量test默认为null,静态变量b默认为0。如果b是由final修饰的静态变量,那么b的值在初始阶段便为112。当进行到类的初始化阶段,此时jvm需要执行类构造器,包括类的构造方法和对象的构造方法。因此在此阶段首先执行的是静态赋值语句(按顺序从上至下执行)——static Test test = new Test(),此时实例化对象test,初始化对象的成员变量a=110,然后会调用方法内的构造块,输出2,再调用方法内的构造方法Test(),输出3,a=110,b=0。然后再往下执行静态方法输出1,最后再运行主函数输出4。

看到这里,大家都可能会有疑问,为什么实例的初始化竟然在静态的初始化之前呢?其实实例的初始化并没有出现在静态初始化之前,认真看看流程我们可以发现,在静态初始化Test的时候遇到的test成员是本类的实例,因此在实例化test变量时,实际上是把实例初始化嵌入到静态初始化的流程中。这才是关键所在。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值