故事:
帅哥:“我是一个很专一的人,一生只爱一个。”
美女:“怎么证明?”
伪代码
1.饿汉单例模式
public class MyHeart {
private final static MyHeart MY_HEART = new MyHeart();
private MyHeart(){}
public static MyHeart getMyHeart(){
return MY_HEART;
}
}
优点:随着类的加载而加载,执行效率高,没有线程安全问题
缺点:如果有大量的类都使用单例模式,在不使用的时候也加载在内存里面,这就造成了内存的浪费
2.懒汉单例模式
public class MyHeartLazy {
private static MyHeartLazy MY_HEART = null;
private MyHeartLazy() {
}
public synchronized static MyHeartLazy getMyHeart() {
if (null == MY_HEART) {
MY_HEART = new MyHeartLazy();
}
return MY_HEART;
}
}
/**
* 懒汉模式之双重检查锁(double check lock ----简称DCL)
* 《javab并发编程》提到双重检查的模式现在并不推荐
* 这种写法是因为在无竞争同步的执行速度很慢,以及JVM启动很慢才出现的这种写法,
* 现在无论是JVM还是synchronized关键字都有了优化。
* 所以上面的写法已经完全满足了现实的要求
*
*/
class MyHeartLazyDoubleCheck {
private static volatile MyHeartLazyDoubleCheck MY_HEART_DCL = null;
private MyHeartLazyDoubleCheck() {
}
public static MyHeartLazyDoubleCheck getMyHeart() {
if (MY_HEART_DCL == null) {
synchronized (MyHeartLazyDoubleCheck.class) {
if (MY_HEART_DCL == null) {
MY_HEART_DCL = new MyHeartLazyDoubleCheck();
}
return MY_HEART_DCL;
}
}
return MY_HEART_DCL;
}
}
优点:节省了内存,只有在需要的时候才会创建单例
缺点:存在线程安全问题,需要用到锁
3.注册式单例
public class MyHeartRegister {
private final static Map<String, MyHeartRegister> MY_HEART = new HashMap<>();
private MyHeartRegister() {
}
public static synchronized MyHeartRegister getMyHeart() {
if (MY_HEART.get("myheart") == null) {
MY_HEART.put("myheart", new MyHeartRegister());
}
return MY_HEART.get("myheart");
}
}
优缺点和懒汉模式差不多
4.枚举单例
public enum MyHeartEnum {
MY_HEART;
public void showMyHeart(){
System.out.println("我是很专一的,只有一个对象");
}
}
在《effective Java》中,作者强烈推荐使用该种写法。
反射是可以拿到私有构造方法的,而序列化和反序列化也会生成新的实例,从而破环单例模式。而枚举单例模式可以有效防止这两种攻击。需要注意的是,如果我们的单例需要被继承,则不推荐使用枚举类型模式。
测试枚举单例
/**
* 枚举单例模式测试
* @author 康康的远方
* @date 2021/3/28
*/
public class SingleTest {
public static void main(String[] args) {
MyHeartEnum myHeart = MyHeartEnum.MY_HEART;
MyHeartEnum myHeart2 = MyHeartEnum.MY_HEART;
myHeart.showMyHeart();
System.out.println(myHeart==myHeart2); //返回true
}
}