美文网首页
Java中静态成员变量初始化时机

Java中静态成员变量初始化时机

作者: 勤劳的蚂蚁 | 来源:发表于2020-04-08 11:03 被阅读0次

静态变量只有在类使用的时候 才会初始化,比如new 对象,使用静态变量等

什么时候静态变量回收呢?

Java中静态成员变量,静态代码块,静态内部类何时被初始化?

关于这个问题,本文不扯理论,直接上代码,通过结果来验证结论,废话少说,测试代码如下:

public class StaticTest {

    public static StaticMember staticMember = new StaticMember();

    static {
        System.out.println("static code initializer ");
    }

    private static class InnerClass {
        private static StaticTest staticTest = new StaticTest("load from InnerClass");
    }

    public StaticTest() {
    }

    public StaticTest(String a) {
        System.out.println(a);
    }

    public static void f(){

    }

    public void d(){

    }

    public static void e(){
        InnerClass.staticTest.d();
    }
}

public class StaticMember {
    public StaticMember(){
        System.out.println("StaticMember");
    }
}

在StaticTest 测试类中我写了三种静态域分别是静态成员变量,静态代码块以及静态内部类,下面通过不同的case测试上面三种静态域何时被初始化。
测试case代码:

public class Main {
    static boolean flg;

    public static void main(String[] args) {

        /**case1**/
        //不会执行静态代码块, 静态成员变量不会初始化, 也不会加载静态内部类
        String simpleName = StaticTest.class.getSimpleName();

        /**case2**/
        //会执行静态代码块, 静态成员变量会初始化, 不会加载静态内部类
        //输出 StaticMember
        //    static code initializer
        StaticMember staticMember = StaticTest.staticMember;

        /**case3**/
        //会执行静态代码块, 静态成员变量会初始化, 不会加载静态内部类
        //输出 StaticMember
        //    static code initializer
        new StaticTest();

        /**case4**/
        //会执行静态代码块, 静态成员变量会初始化, 不会加载静态内部类
        //输出 StaticMember
        //    static code initializer
        StaticTest.f();

        /**case5**/
        //不会执行静态代码块, 静态成员变量不会初始化, 也不会加载静态内部类
        if (flg) {
            test();
        }

        /**case6**/
        //会执行静态代码块, 静态成员变量会初始化, 同时加载静态内部类
        // 输出:StaticMember
        //      static code initializer
        //      load from InnerClass
        StaticTest.e();
    }

    private static void test(){
        StaticTest.f();
        StaticTest.e();
    }

}


通过上面每一种代码测试case的输出结果,可以得出如下结论:

静态成员变量和静态代码块(static{})只有在类被调用的时候才会初始化。
这里是指在运行时真正被使用到才会被初始化,如果是在编译时被使用到,但在运行时没有使用到也不会被初始化,比如上面的case5。
静态内部类只有当被外部类调用到的时候才会初始化。
这里也是指在运行时,也就是说不在于你在编辑器中有没有写调用的代码,而是你写的这段调用代码运行时是否会被真正执行到。在只使用了外部类,但是没有使用内部类的情况下,内部类里面的东西不会被初始化。
关于case1的情况,直接引用StaticTest.class不会初始化静态变量和静态代码块,而直接new StaticTest()就会,为什么呢?因为JVM在加载类的过程中分为五个阶段:加载、验证、准备、解析、初始化,StaticTest.class的方式发生在第一个阶段,这个阶段会在Java堆中创建java.lang.Class的实例,而变量和静态块是发生在最后一个初始化的阶段,具体参考:Java虚拟机 类加载的过程, Chapter 5. Loading, Linking, and Initializing

原文链接:https://blog.csdn.net/lyabc123456/java/article/details/80262895
前几日做项目的时候使用了静态的成员变量Handler,如下所示:

public class MyUtility
{
    private static final int MSG_ADD = 0;
    private static final int MSG_DECREASE = 1;
    private static int cnt = 0;
    
    public static Handler handler = new Handler()
    {
        @Override
        public void handleMessage(Message msg)
        {
            switch(msg.what)
            {
            case MSG_ADD:
                cnt++;
                break;
            case MSG_DECREASE:
                cnt--;
                break;
            default:
                break;
            }
        }
    };
    
    public static void bench()
    {
        Message msg = handler.obtainMessage(MSG_ADD);
        handler.sendMessage(msg);
        new Thread(){
            @Override
            public void run()
            {
                Message msg = handler.obtainMessage(MSG_DECREASE);
                handler.sendMessage(msg);
            }
        }.start();
    }
}



并在主线程中一个按钮的onClick事件中调用这个类中的静态函数:

public void onClick(View vw)
    {
        new Thread(){
            @Override
            public void run()
            {
                MyUtility.bench();
            }
        }.start();
    }

注意是在子线程中调用的MyUtility中的函数,结果运行中出错,调试发现如下错误:
java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()

意思是说handler是在子线程中创建的,而子线程没有调用Looper.perpare()。



原先以为static成员变量是在程序加载的时候初始化,怎么会在子线程中创建呢?网上搜索也没发现有价值的线索,最后只有自己总结了。网上说静态变量是在类加载的时候初始化,难道是MyUtility是在第一次使用的时候才加载?如果程序运行过程中没有使用这个类,那么这个类是不会被程序加载的?带着这样的疑问,进行了下面的调试:

如果类第一次使用是在子线程中,那么类加载是在子线程中进行的,static变量的初始化也是在子线程中进行。把Handler改成如下声明方式(这样可以打断点进行调试):

public static Handler handler = null;

static{

handler = new Handler()
    {
        @Override
        public void handleMessage(Message msg)
        {
            switch(msg.what)
            {
            case MSG_ADD:
                cnt++;
                break;
            case MSG_DECREASE:
                cnt--;
                break;
            default:
                break;
            }
        }
    };

}

经调试发现,果然如我总结的一样。看来java中static成员变量初始化时机和c++中static成员变量不一样啊。

发现问题的原因就好解决问题了,第一次使用MyUtility类要在主线程中(比如先在主线程中随便调用一个MyUtility函数或者变量),那么就不会出现java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()异常了。



后续:在一篇文章中看到,Java和C#语言都支持类的动态加载,而C++不支持!
————————————————

原文链接:https://blog.csdn.net/lcfeng1982/java/article/details/7460039

相关文章

  • Java中静态成员变量初始化时机

    静态变量只有在类使用的时候 才会初始化,比如new 对象,使用静态变量等 什么时候静态变量回收呢?

  • java学习4/11

    局部变量和成员变量的区别 Java中的static使用 Java中的静态内部类 Java中继承的初始化顺序 Jav...

  • 设计模式——工厂,策略,建造者

    Java 类初始化顺序基本分为以下几种:静态块,初始化块,静态方法,构造函数块,静态变量,成员变量对于块顺序,静态...

  • java相关零碎知识

    java初始化,加载顺序: 父类的静态成员变量,父类静态代码块,子类的静态成员变量,子类的静态代码块,父类非静态成...

  • C++11:构造函数及类的初始化

    C++11:初始化 C++11中成员变量的初始化主要包括三类变量: 普通成员变量 常量 静态变量 静态常量 普通成...

  • kotlin属性初始化和懒加载之lateinit、by lazy

    java属性的初始化 在说kotlin属性初始化之前我们先来看下java属性的初始化 大体分为成员变量初始化、静态...

  • 四、JNI-成员和方法

    成员 方法 #1. 成员 Java中成员变量分为普通成员变量和静态成员变量。普通成员变量也称为类实例成员变量,所有...

  • kotlin真的没有静态成员变量的吗?

    kotlin是如何改造静态成员变量与静态方法的? 在java中,定义一个静态成员变量用static关键字即可。静态...

  • 变量

    变量可分为局部变量、成员变量(也叫实例变量)、静态变量 1.与C语言不同,Java中的局部变量必须先初始化再使用 ...

  • java类初始化机制

    初始化顺序 无继承类初始化 初始化顺序静态变量/静态代码块 -> 成员变量/代码块 -> 构造函数其中,静态变量与...

网友评论

      本文标题:Java中静态成员变量初始化时机

      本文链接:https://www.haomeiwen.com/subject/kwbzphtx.html