个人学习笔记分享,当前能力有限,请勿贬低,菜鸟互学,大佬绕道
如有勘误,欢迎指出和讨论,本文后期也会进行修正和补充
前言
单例模式是Java最简单和常见的模式之一
都知道Java中,类需要被实例化为对象才能使用,因而同一个类可以被实例化为多个对象,但是部分场景我们需要使用同一个对象,这就是单例模式出现的原因
1.介绍
使用目的:保证一个类仅有一个实例,并提供一个访问他的全局访问点
使用时机:需要节省系统资源,或者需要在多个地方公用类里面的数据等情况
解决问题:频繁创建和销毁实例导致资源浪费,同一个类的不同实例的数据互不公用
实现方法:单例类通过私有构造函数创建唯一实例,并提供公共访问点给其他对象
- 单例类只能有一个实例
- 单例类必须自己创建自己的唯一实例
- 单例类必须给所有其他对象提供这一实例
应用实例:
- 全局管理器,如手机app里下载管理器,只能有一个管理器,负责调控真个app的下载任务
- 命令池等公用对象,需要公用里面的数据,那么显然只能创建唯一实例并提供给其他对象
- 消耗资源较多的实例,如后端需要发送http请求,若频繁创建client则会消耗大量资源,那么不妨将该实例作为单例,每次都是用同一个client
优点:
- 仅有一个实例,那么避免了频繁创建和销毁带来的资源消耗
- 实例公用,那么可以保证不同对象可以使用同一份数据
- 实例由类自身持有,那么不会因为无人持有而销毁,从而达到数据持久化的目的
缺点:没有接口,不能继承,自己创建自己的实例,与单一职责原则冲突(一个类应该只关心内部逻辑,而不关心外面怎么样来实例化)
2.实现
2.1.基本步骤
-
定义私有构造函数,那么该类将不会被其他对象实例化
private SingleObject(){ }
-
自己创建实例化对象
private static SingleObject instance = new SingleObject();
-
定义全局访问点,提供给其他对象
public static SingleObject getInstance(){ return instance; }
-
定义类相关业务代码
public void showMessage(){ System.out.println("Hello World!"); }
完整代码
public class SingleObject {
//创建 SingleObject 的一个对象
private static SingleObject instance = new SingleObject();
//让构造函数为 private,这样该类就不会被实例化
private SingleObject(){
}
//获取唯一可用的对象
public static SingleObject getInstance(){
return instance;
}
public void showMessage(){
System.out.println("Hello World!");
}
}
-
在其他对象中获取实例,并调用相关业务方法
public class SingletonPatternDemo { public static void main(String[] args) { //不合法的构造函数 //编译时错误:私有构造函数 SingleObject() 不可见的 //SingleObject object = new SingleObject(); //获取唯一可用的对象 SingleObject object = SingleObject.getInstance(); //显示消息 object.showMessage(); } }
2.2.五种常见实现方案
2.2.1.懒汉式
第一次被调用时初始化,节省资源,但线程不安全,
基于“懒“的思想实现,即被动初始化,如果不被使用就不会被初始化
由私有构造器和一个公有静态工厂方法构成,在工厂方法中对singleton进行null判断,如果是null就new一个出来,最后返回singleton对象
public class Singleton {
private static Singleton instance;
private Singleton (){
}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
该方法的优点是节省了资源,如果不需要被使用,就不会初始化,只有第一次使用才会生成实体对象
而缺点很明显,就是线程不安全,如果两个方法同时调用
Singleton.getInstance()
,就可能重复生成对象如果对
Singleton.getInstance()
加上同步锁即可解决线程不安全的问题,但是代价是每次调用都会产生不必要的同步开销