设计模式解析---------------单例模式

单例模式定义

确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。

单例模式的使用场景

确保某个类只有一个对象的场景,避免产生多个对象消耗过多的资源,或者某种对象有且只能有一个。

单例模式的UML图

图1
角色介绍:

  • Client --------------- 高层客户端
  • Singleton ----------- 单例类

实现单例模式的要点:

  1. 构造函数不对外开放,一般是 Private;
  2. 通过一个静态方法或者枚举类返回单例类对象;
  3. 确保单例类的对象有且只有一个,尤其是在多线程环境下;
  4. 确保单例类对象在反序列化时不会重新构建对象;

饿汉单例模式

一个公司只有一个CEO,可以有几个VP,无数个员工。

/*
*  普通员工
* */
public class Staff {
    public void work(){
        System.out.println("干活");
    }
}
/*
* 副总裁
* */
public class VP extends Staff{
    public void work(){
        System.out.println("管理下面的经理");
    }
}
/*
* CEO  饿汉单例模式
* */
public class CEO extends Staff{
    private static final CEO mCeo = new CEO();
    // 构造函数私有
    private CEO(){
    }

    // 公有的静态函数,对外暴露获取单例对象的接口
    public static CEO getmCeo(){
        return mCeo;
    }

    public void work(){
        System.out.println("管理VP");
    }
}
/*
* 公司类
* */
public class Company {
    private List<Staff> staffList = new ArrayList<Staff>();
    public void addStaff(Staff per){
        staffList.add(per);
    }

    public void showAllStaffs(){
        for (Staff per : staffList){
            System.out.println("obj : " + per.toString());
        }
    }
}

测试类

public class Test {
    public static void main(String[] args) {
        Company company = new Company();
        // CEO 只能通过getCeo函数获取
        CEO ceo = CEO.getmCeo();
        CEO ceo1 = CEO.getmCeo();
        company.addStaff(ceo);
        company.addStaff(ceo1);
        // 通过 new 创建vp 对象
        VP vp = new VP();
        VP vp1 = new VP();
        // 通过 new 创建 staff 对象
        Staff staff = new Staff();
        Staff staff1 = new Staff();
        Staff staff2 = new Staff();

        company.addStaff(vp);
        company.addStaff(vp1);
        company.addStaff(staff);
        company.addStaff(staff1);
        company.addStaff(staff2);

        company.showAllStaffs();
    }
}

运行结果如下图,我们可以发现 CEO 的地址是一样的,说明构建的对象实例是唯一的
在这里插入图片描述

懒汉单例模式

public class Singleton {
    private static Singleton instance;
    private Singleton(){}

    public static synchronized Singleton getInstance(){
        if(instance == null){
            instance = new Singleton();
        }
        return instance;
    }
}

上面的代码中增加了一个 synchronized 关键字,也就是 getInstance 是一个同步方法,这是保证在多线程下单例对象唯一性的手段。
懒汉单例模式的优点是单例只有在使用时才会被实例化,在一定程度上节约了资源;
缺点是第一次加载需要及时进行实例化,反应稍慢,最大问题是每次 getInstance 都进行同步,造成不必要的同步开销。

Double Check Lock (DCL) 单例模式

public class Singleton {
    private static Singleton instance = null;
    private Singleton(){}

    public void doSomething(){
        System.out.println("do.sth.");
    }

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

静态内部类单例模式

/*
*  静态内部类
* */

public class Singleton {
    private Singleton(){}
    public static Singleton getInstance(){
        return SingletonHolder.sInstance;
    }

    /*
    *  静态内部类
    * */
    private static class SingletonHolder{
        private static final Singleton sInstance = new Singleton();
    }
}

当第一次加载 Singleton 类时并不会初始化 sInstance ,只有在第一次调用 Singleton 的 getInstance 的方法才会导致 sInstance 被初始化。所以,第一次调用 getInstance 方法会导致虚拟机加载 SingletonHolder 类,这种方式不仅能够确保线程安全,也能保证单例对象的唯一性,同时也延迟了单例的实例化。

枚举单例

public enum SingletonEnum {
    INSTANCE;
    public void doSomething(){
        System.out.println("do.sth.");
    }
}

枚举单例最大的优点就是简单,枚举在 java 中与普通的类是一样的,不仅能有字段,还能有自己的方法。最重要的是默认枚举实例的创建是线程安全的,并且在任何情况下都是一个单例。

使用容器实现单例模式

public class SingletonManger {
    private static Map<String,Object> objectMap = new HashMap<String,Object>();
    private SingletonManger(){}
    public static void registerService(String key,Object instance){
        if (!objectMap.containsKey(key)){
            objectMap.put(key,instance);
        }
    }

    public static Object getService(String key){
        return objectMap.get(key);
    }
}

在程序的初始,将多种单例类型注入到一个统一的管理类中,在使用时根据key获取对象对应类型的对象。这种方式可以让我们管理多种类型的单例,并且在使用是通过统一的接口进行获取操作,降低用户的使用成本,也对用户隐藏了具体实现,降低了耦合度。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zpeien

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值