单例实现的几种方式

单例是一个只实例化一次的类,即类仅存在一个对象。

私有化构造器 + 静态成员变量

显示声明构造器,并使用 private 关键字将其私有化,避免使用 new Object 来创建对象。需要使用该实例,只需要通过成员变量 INSTANCE 访问即可。

public class MySingleton {
    public static final MySingleton INSTANCE = new MySingleton();  
    private MySingleton(){}
}

缺点:会被 AccessibleObject.setAccessible 方法利用反射调用私有构造函数,实例化新的对象。

私有化构造器 + 静态工厂方法

该单例实现方法与上面基本一致,只需要将成员变量私有化,添加工厂方法来访问该成员变量即可。获取实例只需要调用 getInstance 即可。

public class MySingleton {
    private static final MySingleton INSTANCE = new MySingleton();  
    private MySingleton(){}
    
    public static MySingleton getInstance(){
        return INSTANCE;
    }
}

缺点:同上。

实现序列化需要注意的问题

使用上述两种方法实现单例,且需要实现可序列化时,仅仅是实现 Serializable 接口是不够的,要维护单例保证,应声明所有实例字段为 transient,并提供 readResolve 方法(Item-89)。

否则,每次反序列化实例时,都会创建一个新实例,在我们的示例中,这会导致出现虚假的 MySingleton。为了防止这种情况发生,将这个 readResolve 方法添加到 MySingleton 类中。

详细的解决方法可以查看《Effective Java》第三版 Item 89。

单元素枚举

优点:默认提供了序列化机制,提供了对多个实例化的严格保证,即使面对复杂的序列化或反射攻击也是如此。

public enum MySingleton{
    INSTANCE;
}

缺点:当类需要扩展超类时,enum 便不在适合。

延迟加载单例(双重检查不成立?)

延迟加载即只有第一次调用时,才进行初始化。

public class MySingleton {
    private static MySingleton INSTANCE = null;  
    private MySingleton(){}
    
    public static MySingleton getInstance(){
        if (INSTANCE == null) {
            INSTANCE = new MySingleton();
        }
        return INSTANCE;
    }
}

当程序第一次调用 getInstance 方法时,MySingleton 实例才被初始化。

该延迟加载在多线程情况下,可能会创建多个实例,因此需要对 getInstance 方法进行加锁处理。

public synchronized static MySingleton getInstance(){
      if (INSTANCE == null) {
          INSTANCE = new MySingleton();
      }
      return INSTANCE;
  }

加锁的意图在于第一次初始化的时候,保证只有一条线程进入到 if 语句中,生成实例。直接在方法上加锁,会导致性能下降。因为每次去取该实例都要去获得锁,所以需要对该方式进行优化。

public static MySingleton getInstance(){
       if (INSTANCE == null) {
           synchronized (MySingleton.class){
               if (INSTANCE == null) {
                   INSTANCE = new MySingleton();
               }
           }
       }
       return INSTANCE;
   }

由于类加载以及对象初始化的特殊性值,会导致该双重检查不成立,可以使用 volatile 关键字声明变量,防止指令重排,实现最终目的。

public class MySingleton {
    private static volatile MySingleton INSTANCE = null;  
    private MySingleton(){}
    
     public static MySingleton getInstance(){
        if (INSTANCE == null) {
            synchronized (MySingleton.class){
                if (INSTANCE == null) {
                    INSTANCE = new MySingleton();
                }
            }
        }
        return INSTANCE;
    }
}

一份来自 Java 巨佬关于 双重检查的阐述,点击这里

  • 5
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值