类型:创建型
定义:确保一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。
场景问题:在一个系统运行期间,某个类只需要一个类实例就可以了。
具体实现:要求该类的构造方法私有化,提供一个静态方法来获取单例。
有两种方式:
1、懒汉式
懒汉式:需要创建对象的时候才创建,以时间换空间 体现了延迟加载的思想Lazy Load,还体现了缓存的思想(if...)
public final class SingleInstance { //使用final关键字,类不能被继承
private static SingleInstance instance;
private SingleInstance() {
//构造函数私有化,外界无法通过new关键字创建对象
}
public static SingleInstance getInstance() {
if (null == instance) {
synchronized (SingleInstance.class) {//添加同步锁,避免多线程环境下创建出多个实例
if (null == instance)
instance = new SingleInstance();
}
}
return instance;
}
}
饿汉式:一开始就创建好对象,直接访问即可 以空间换时间(占用内存,但是能直接返回对象,速度快).
public final class SingleInstance { //使用final关键字,类不能被继承
private static SingleInstance instance=new SingleInstance;
private SingleInstance() {
//构造函数私有化,外界无法通过new关键字创建对象
}
public static SingleInstance getInstance() {
return instance;
}
}
二、如何创建带有构造参数的单例
如果在创建对象时,需要注入参数,那么这种单例模式如何创建呢?
public final class SingleInstance {
private String param1; // 配置参数1
private String param2; // 配置参数2
private static SingleInstance instance;
private SingleInstance(String param1,String param1) {
this.param1 = param1;
this.param2 = param2;
}
public static SingleInstance getInstance(String param1,param2) {
if (null == instance) {
synchronized (SingleInstance.class) {
if (null == instance)
instance = new SingleInstance(param1,param2);
}
}
return instance;
}
}
由于构造函数需要带有参数,导致我们获取单例时每次都需要传入参数param1,param2, 当然这种方式是不可取的。下面给大家带来一种比较优雅的实现方式
public final class SingleInstance {
private String param1; // 配置参数1
private String param2; // 配置参数2
private static SingleInstance instance;
private SingleInstance() {
this(new SingleInstanceBuilder());// 默认配置
}
private SingleInstance(SingleInstanceBuilder builder) {
param1 = builder.param1;
param2 = builder.param2;
}
public static SingleInstance getInstance() {
if (null == instance) {
synchronized (SingleInstance.class) {
if (null == instance)
instance = new SingleInstance();
}
}
return instance;
}
//对外提供的业务方法
public void fun1(){
}
//---------------------------对象构建器-------------------------------
/**
* 对象构建者,设置不同的属性可构建出不同对象
* @author 张全
*/
public static class SingleInstanceBuilder {
private String param1;
private String param2;
public SingleInstanceBuilder() {
// 初始化一些默认配置
param1 = "";
param2 = "";
}
// 配置1
public SingleInstanceBuilder param1(String param) {
this.param1 = param;
return this;
}
// 配置2
public SingleInstanceBuilder param2(String param) {
this.param2 = param;
return this;
}
// 构建对象
public SingleInstance build() {
//配置校验
if(null==param1)throw new RuntimeException("请配置param1参数");
return new SingleInstance(this);
}
// 构建对象,供SingleInstance.getInstance()获取
public SingleInstance installDefaultInstance() {
synchronized (SingleInstance.class) {
if (SingleInstance.instance != null) {
throw new RuntimeException("已存在实例");
}
SingleInstance.instance = build();
return SingleInstance.instance;
}
}
}
}
下面来看看如何使用这种创建方式
//1、完成配置
SingleInstance instance = new SingleInstance.SingleInstanceBuilder()
.param1("配置1")
.param1("配置2")
.installDefaultInstance();
//2、任何地方调用单列
SingleInstance.getInstance().fun1();
可以看出,通过定义一个内部类来注入属性,可以完成单例对象的初始化。这种方式常见于很多开源框架设计中。
三、关于单例模式的一些注意事项
1、避免内存占用持续增大
由于单例模式保证一个类只有一个对象存在,则该对象的生命周期和系统生命周期一样长。避免持有一些大数据对象,引用的对象在不使用时应及时断开引用。
2、应注意持有对象的生命周期
对那些有固定生命周期的对象(比如Android中的Activity、Fragment等),在该对象生命周期结束时,应该将单例对象中持有的引用也断开,避免造成GC无法回收带来的内存泄漏问题。