静态内部类为何能够实现单例?

public class DeviceHelper {

    private DeviceHelper() {

    }

    private static class DeviceHelperHolder {
        /**
         * jvm 虚拟机规定当主动 new 一个对象时会触发类的初始化,初始化阶段也就是执行类构造器 <clinit>()方法的过程;
         * <clinit>() 方法对于类来说不是必须的,如果一个类中既没有静态语句块也没有静态变量赋值动作,
         * 那么编译器都不会为类生成<clinit>()方法。
         *
         * 虚拟机会保证一个类的 <clinit>() 方法在多线程环境中能被正确的加锁、同步。如果多个线程初始化一个类,
         * 那么只有一个线程会去执行<clinit>()方法,其它线程都需要等待。
         *
         *
         * 下面的 instance 是属于静态变量赋值,那么会执行类构造器 <clinit>() 方法,从而保证线程安全。
         *
         */
        private static DeviceHelper instance = new DeviceHelper();
    }

    public static DeviceHelper getInstance() {
        // jvm 规定读取一个类的静态属性(非静态常量 final static int a =1 这种),会触发类的初始化。
       // 如下会触发 DeviceHelperHolder 类的初始化
        return DeviceHelperHolder.instance;
    }
}

上面解释了原因,静态内部类的方式有个不足之处就是不能传递参数,比如你需要在 DeviceHelperHolder 中使用 Context,那么无法通过构造函数传递。

优势也很明显:

  1. 类似于饿汉模式,用到才初始化。
  2. 由JVM保证并发安全,性能比使用同步锁要高。

触发类的初始化的5种场景:

  1. new 一个类
  2. 读取或设置一个类的静态字段(static int a = 10)且该字段不是 final修饰的(因为 static final 字段在类的准备阶段已经赋值)或者调用静态方法
  3. 使用反射的方法对类进行反射调用时,如果该类未初始化则进行初始化
  4. 当初始化一个类时,如果其父类还未初始化,则先初始化其父类
  5. 当Java虚拟机启动时,初始化包含 main 方法的主类

对象的初始化顺序如下:

静态变量/静态代码块 -> 普通变量/普通代码块 -> 构造函数

  1. 父类静态变量和静态代码块;
  2. 子类静态变量和静态代码块;
  3. 父类普通成员变量和普通代码块;
  4. 父类的构造函数;
  5. 子类普通成员变量和普通代码块;
  6. 子类的构造函数。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值