静态块真的是类加载的时候执行么?

最近在读java深度历险这本书,书中第二章有个关于静态块执行时机的讨论,这一改我以前的认知啊。

很多博客或是书籍都认为静态块是类加载的时候执行的,真相真的是这样么?

我们先来看两个例子:

example 1: 

public class Test {

    public static void main(String[] args) throws Exception {
        System.out.println("initialize...");
        Class c = Class.forName("TestStatic");
        System.out.println("instance...");
        Object obj1 = c.newInstance();
        Object obj2 = c.newInstance();
        System.out.println("end ...");
    }
}
public class TestStatic {
    static {
        System.out.println("*******************");
    }
}

输出结果:

这次结果是TestStatic的静态块是在类加载的时候执行的,接下来我们看看另一个例子

example 2:

public class Test {

    public static void main(String[] args) throws Exception {
        System.out.println("initialize...");
        Class c = Class.forName("TestStatic", false, Test.class.getClassLoader());
        System.out.println("instance...");
        Object obj1 = c.newInstance();
        Object obj2 = c.newInstance();
        System.out.println("end ...");
    }
}

输出结果:

这次我们看到静态块是在实例化的时候执行的,为何会造成这两种截然不同的结果呢?

原因在于两次使用了不同的类加载方法

Class c = Class.forName("TestStatic");
Class c = Class.forName("TestStatic", false, Test.class.getClassLoader());

看看源码可以发现两种类加载方式最终调用的都是一个native方法forName0()

第三个参数ClassLoader都是代码执行者Test类的ClassLoader,由此可以看出两次静态块执行时机的不同就是一个参数initialize值决定的。同时,我们看到即使是后面再实例化了一个obj2,static块也没有再次执行,这表明静态块仅仅在第一次实例化才会执行。

===============================这段测试有问题======================================

关于参数initialize的含义我看到网上有博客说就是否对加载的类进行初始化,设置为true时会类进行初始化,代表会执行类中的静态代码块,以及对静态变量的赋值等操作联系类加载的过程相关知识,我们知道给静态变量赋初始值是在“准备”阶段,真正赋上代码中的值“9”在“初始化”阶段(final修饰的静态变量赋值“9”在“准备”阶段),开始我以为设置为false就可以跳过类加载的“初始化”步骤,但测试过后发现这个参数initialize并不可以控制对静态变量的赋值。

测试:

TestStatic类中新增static变量i,赋值9

public class TestStatic {
    static int i = 9;
    static {
        System.out.println("*******************");
    }
}

Test类设置initialize为false的加载方式,同时在加载过后以及实例化过后新增打印语句:

public class Test {

    public static void main(String[] args) throws Exception {
        System.out.println("initialize...");
        Class c = Class.forName("TestStatic", false, Test.class.getClassLoader());
        System.out.println("1. i = " + TestStatic.i);
        System.out.println("instance...");
        Object obj = c.newInstance();
        System.out.println("2. i = " + TestStatic.i);
        System.out.println("end ...");
    }
}

然而,结果是加载过后实例化之前静态变量i的赋值动作已经完成,initialize参数为false并没有控制住静态变量的赋值动作。

不知道是不是我测试的方式有问题,如果哪位朋友知道请告知下,感激不尽!

===============================================================================================

综上,静态块实际是在第一次实例化的时候才会执行,只不过我们在日常写代码时使用的都是initialize为true的类加载方式,所以会产生静态块是类加载时执行的错误认知。

以上如果有理解不对的地方还望大家能够指正和包容,谢谢!

********************************************************************************************************************

已经有大神指出这个测试方式有问题,对于类名.静态变量这种取值方式,JVM会立即对该类做类加载的初始化动作的,所以此时测试出来的“9”并不能推导出initialize参数为false没有控制住静态变量的赋值动作。

这段话摘自《深入理解Java虚拟机第2版》第七章中的类加载的时机的内容:
虚拟机规范规定了,当遇到new、getstatic、putstatic或invokestatic这4条字节码指令是,如果没有进行过初始化则需要先触发初始化。生成这4条指令的最常见的Java代码场景是:使用new关键字实例化对象的时候、读取或者设置一个类的静态字段(被final修饰、已在编译期把结果放入常量池的静态字段除外)的时候,以及调用一个类的静态方法的时候。

非常感谢指出错误的朋友,以免误导大家!

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值