Effective Java之用私有构造器或者枚举类型强化Singleton属性(三)

1.饿汉式加载

类加载时就创建

public class MaYun {
private static Mayun instance = new Mayun();
private static getInstance() {
return instance;
}
private MaYun() {
//MaYun诞生要做的事情
}
public void splitAlipay() {
System.out.println(“Alipay是我的啦!看你丫Yahoo绿眉绿眼的望着。。。”);
}
}

2.懒汉式加载

类使用时加载

public class MaYun {
private static MaYun instance = null;
private MaYun() {
//MaYun诞生要做的事情
}
public static synchronized MaYun getInstance() {
if (instance == null) {
instance = new MaYun();
}
return instance;
}
public void splitAlipay() {
System.out.println(“Alipay是我的啦!看你丫Yahoo绿眉绿眼的望着。。。”);
}
}

3.懒汉式的线程安全版(双重检验锁)

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

在getInstance中做了两次null检查,确保了只有第一次调用单例的时候才会做同步,这样也是线程安全的,同时避免了每次都同步的性能损耗

4.静态内部类(推荐)

public class MaYun {
private static class SigletonHolder {
private static final instance = new MaYun();
}
public static final getInstance() {
return SigletonHolder.instance;
}
private MaYun() {
//MaYun诞生要做的事情
}
public void splitAlipay() {
System.out.println(“Alipay是我的啦!看你丫Yahoo绿眉绿眼的望着。。。”);
}
Call:MaYun.getInstance().splitAlipay();

加载MaYun类时,在类的加载阶段把静态内部类加载了,也就是利用了classloader的机制来保证初始化instance时只有一个线程,所以也是线程安全的,同时没有性能损耗,所以这种方法非常推荐。

5.编写一个包含单个元素的枚举类型(极力推荐)

public enum MaYun {
himself; //定义一个枚举的元素,就代表MaYun的一个实例
private String anotherField;
MaYun() {
//MaYun诞生要做的事情
//这个方法也可以去掉。将构造时候需要做的事情放在instance赋值的时候:
/** himself = MaYun() {
* //MaYun诞生要做的事情
* }
**/
}
public void splitAlipay() {
System.out.println(“Alipay是我的啦!看你丫Yahoo绿眉绿眼的望着。。。”);
}
}
Call:MaYun.himself.splitAlipay();

我们来认真分析一下这5种方法:

  1. 1234方法把构造器声明成私有的,但是这种方法是不能抵抗反射机制的攻击的!如下:
Constructor<?> constructor = MaYun.class.getDeclaredConstructors()[0];
constructor.setAccessible(true);
Elvis instance = (Elvis) constructor.newInstance();

依然可以创建一个对象!
解决的方法是添加计数器,大于1时抛出错误:

public class MaYun {
    private static AtomicInteger count = new AtomicInteger(0);
    private static final MaYun INSTANCE = new Elvis();

    private MaYun() {
        if(count > 0) {
            throw new IllegalArgumentException("Cannot create Elvis twice");
        }
        count.incrementAndGet()
    }

而方法5美剧方法可以防止反射攻击,当你试图通过反射去实例化一个枚举类型的时候会抛出IllegalArgumentException异常:

Exception in thread "main" java.lang.IllegalArgumentException: Cannot reflectively create enum objects
    at java.lang.reflect.Constructor.newInstance(Constructor.java:416)
    at org.effectivejava.examples.chapter02.item03.enumoration.MaYun.main(MaYun.java:21

从这里看,方法5具有绝对的优势!

2.如果上面实现的Singleton是可以序列化的。那么方法1234加上implements Serializable只保证它可以序列化,为了保证反序列化的时候,实例还是Singleton,必须声明所有的实例域都是transient的,并且提供 readResolve方法,否则,每次反序列化都会生成新的实例。

但是方法5无偿提供了序列化机制,绝对防止多次实例化,又是绝对的优势!

这就是方法5值得极力推荐的的原因,同时jdk1.5以上才能使用的方法但是这种方法用的人太少了!!所以改变从你我开始!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值