首页 > 其他分享 >1.设计模式-单例

1.设计模式-单例

时间:2022-09-22 20:55:37浏览次数:48  
标签:instance 实例 static 单例 new 设计模式 public

单例模式

单例模式的核心是保证一个类只有一个实例,并且提供一个访问实例的全局访问点。

单例的使用场景

  1. Spring中bean对象的模式实现方式
  2. servlet中每个servlet的实例
  3. spring mvc和struts1框架中,控制器对象是单例模式
  4. 应用程序的日志应用,一般都何用单例模式实现,这一般是由于共享的日志文件一直处于打开状态,因为只能有一个实例去操作,否则内容不好追加
  5. 操作系统的文件系统,也是大的单例模式实现的具体例子,一个操作系统只能有一个文件系统
  6. 项目中,读取配置文件的类,一般也只有一个对象。没有必要每次使用配置文件数据,每次new一个对象去读取。
  7. 数据库连接池的设计一般也是采用单例模式,因为数据库连接是一种数据库资源

单例模式优点

  1. 由于单例模式只生成一个实例,减少了系统性能开销,当一个对象的产生需要比较多的资源时,如读取配置、产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后永久驻留内存的方式来解决
  2. 单例模式可以在系统设置全局的访问点,优化环共享资源访问,例如可以设计一个单例类,负责所有数据表的映射处理

单例的实现方式

实现方式 优缺点
饿汉模式 线程安全,调用效率高 ,但是不能延迟加载
懒汉模式 线程安全,调用效率不高,能延迟加载
双重检测锁式 在懒汉式的基础上解决并发问题
静态内部类式 线程安全,资源利用率高,可以延时加载
枚举单例 线程安全,调用效率高,但是不能延迟加载

饿汉式

/**
 * 单例模式:饿汉式
 * @author 波波烤鸭
 *
 */
public class SingletonInstance1 {
	// 声明此类型的变量,并实例化,当该类被加载的时候就完成了实例化并保存在了内存中
	private static SingletonInstance1 instance = new SingletonInstance1();

	// 私有化所有的构造方法,防止直接通过new关键字实例化
	private SingletonInstance1(){}
	// 对外提供一个获取实例的静态方法
	public static SingletonInstance1 getInstance(){
		return instance;
	}
}

懒汉式

/**
 * 单例模式:懒汉式
 * @author 波波烤鸭
 *
 */
public class SingletonInstance2 {
	// 声明此类型的变量,但没有实例化
	private static SingletonInstance2 instance = null;

	// 私有化所有的构造方法,防止直接通过new关键字实例化
	private SingletonInstance2(){}
	// 对外提供一个获取实例的静态方法,为了数据安全添加synchronized关键字
	public static synchronized SingletonInstance2 getInstance(){
		if(instance == null){
			// 当instance不为空的时候才实例化
			instance = new SingletonInstance2();
		}
		return instance;
	}
}

此种方式在类加载后如果我们一直没有调用getInstance方法,那么就不会实例化对象。实现了延迟加载,但是因为在方法上添加了synchronized关键字,每次调用getInstance方法都会同步,所以对性能的影响比较大。

双重检测锁式

/**
 * 单例模式:懒汉式
 * 双重检测机制
 * @author 波波烤鸭
 *
 */
public class SingletonInstance3 {
	// 声明此类型的变量,但没有实例化
	private static volatile  SingletonInstance3 instance = null;

	// 私有化所有的构造方法,防止直接通过new关键字实例化
	private SingletonInstance3(){}
	// 对外提供一个获取实例的静态方法,
	public static  SingletonInstance3 getInstance(){
		if(instance == null){
			synchronized(SingletonInstance3.class){
				if(instance == null){
					instance = new SingletonInstance3();
				}
			}
		}
		return instance;
	}
}

不加volatile有指令重排序的问题。添加后可以解决。

静态内部类式

/**
 * 静态内部类实现方式
 * @author 波波烤鸭
 *
 */
public class SingletonInstance4 {
	// 静态内部类
	public static class SingletonClassInstance{
		// 声明外部类型的静态常量
		public static final SingletonInstance4 instance = new SingletonInstance4();
	}
	// 私有化构造方法
	private SingletonInstance4(){}
	
	// 对外提供的唯一获取实例的方法
	public static SingletonInstance4 getInstance(){
		return SingletonClassInstance.instance;
	}
}

注意点:

1.外部类没有static属性,则不会像饿汉式那样立即加载对象。
2.只有真正调用getInstance(),才会加载静态内部类。加载类时是线程 安全的instance是static final类型,保证了内存中只有这样一个实例存在,而且只能被赋值一次,从而保证了线程安全性.
3. 兼备了并发高效调用和延迟加载的优势!– 兼备了并发高效调用和延迟加载的优势!

枚举单例

/**
 * 单例模式:枚举方式实现
 * @author dengp
 *
 */
public enum SingletonInstance5 {

	// 定义一个枚举元素,则这个元素就代表了SingletonInstance5的实例
	INSTANCE;
	
	public void singletonOperation(){
		// 功能处理
	}
}

测试代码

public static void main(String[] args) {
	SingletonInstance5 s1  = SingletonInstance5.INSTANCE;
	SingletonInstance5 s2  = SingletonInstance5.INSTANCE;
	System.out.println(s1 == s2); // 输出的是 true
}

优点:

实现简单

枚举本身就是单例模式。由JVM从根本上提供保障!避免通过反射和反序列化的漏洞!

缺点:

无延迟加载

单例模式漏洞

  1. 通过反射的方式我们依然可用获取多个实例(除了枚举的方式)
public static void main(String[] args) throws Exception, IllegalAccessException {
	SingletonInstance1 s1 = SingletonInstance1.getInstance();
	// 反射方式获取实例
	Class c1 = SingletonInstance1.class;
	Constructor constructor = c1.getDeclaredConstructor(null);
	constructor.setAccessible(true);
	SingletonInstance1 s2 = (SingletonInstance1)constructor.newInstance(null);
	System.out.println(s1);
	System.out.println(s2);
}

输出结果为:

产生了两个对象,和单例的设计初衷违背了。
解决的方式是在无参构造方法中手动抛出异常控制

// 私有化所有的构造方法,防止直接通过new关键字实例化
private SingletonInstance2(){
	if(instance != null){
		// 只能有一个实例存在,如果再次调用该构造方法就抛出异常,防止反射方式实例化
		throw new RuntimeException("单例模式只能创建一个对象");
	}
}
  1. 通过反序列化的方式也可以破解上面几种方式(除了枚举的方式)
public static void main(String[] args) throws Exception, IllegalAccessException {
	SingletonInstance2 s1 = SingletonInstance2.getInstance();
	// 将实例对象序列化到文件中
	ObjectOutputStream oos = new ObjectOutputStream(
			new FileOutputStream("c:/tools/a.txt"));
	oos.writeObject(s1);
	oos.flush();
	oos.close();
	// 将实例从文件中反序列化出来
	ObjectInputStream ois = new ObjectInputStream(
			new FileInputStream("c:/tools/a.txt"));
	SingletonInstance2 s2 = (SingletonInstance2) ois.readObject();
	ois.close();
	System.out.println(s1);
	System.out.println(s2);
}

输出结果:
是两个不同的对象,同样破坏了单例模式,这种情况怎么解决呢
我们只需要在单例类中重写readResolve方法并在该方法中返回单例对象即可,如下:

package com.dpb.single;

import java.io.ObjectStreamException;
import java.io.Serializable;

/**
 * 单例模式:懒汉式
 * @author 波波烤鸭
 *
 */
public class SingletonInstance2 implements Serializable{
	
	// 声明此类型的变量,但没有实例化
	private static SingletonInstance2 instance = null;

	// 私有化所有的构造方法,防止直接通过new关键字实例化
	private SingletonInstance2(){
		if(instance != null){
			// 只能有一个实例存在,如果再次调用该构造方法就抛出异常,防止反射方式实例化
			throw new RuntimeException("单例模式只能创建一个对象");
		}
	}
	// 对外提供一个获取实例的静态方法,为了数据安全添加synchronized关键字
	public static synchronized SingletonInstance2 getInstance(){
		if(instance == null){
			// 当instance不为空的时候才实例化
			instance = new SingletonInstance2();
		}
		return instance;
	}
	// 重写该方法,防止序列化和反序列化获取实例
	private Object readResolve() throws ObjectStreamException{
		return instance;
	}
}

说明:readResolve方法是基于回调的,反序列化时,如果定义了readResolve()则直接返回此方法指定的对象,而不需要在创建新的对象!

总结:

这几种设计模式如何选择

1、单例对象占用资源少,不需要延时加载:

枚举式 好于 饿汉式

2、单例对象占用资源大,需要延时加载:

静态内部类式 好于 懒汉式

标签:instance,实例,static,单例,new,设计模式,public
From: https://www.cnblogs.com/ytmm/p/16712327.html

相关文章

  • Python实现单例模式
    单例模式介绍:单例模式是一种常用的软件设计模型,该模式的主要目的是确保某一个类只有一个实例存在。当你希望在整个系统中,某个类只能出现一个实例时,单例对象就能派上用场。......
  • 设计模式-创建型模式之工厂
    一、简单工厂创建对象的工作交由统一的函数接口来管理,传入不同的参数,返回不同的类对象1classAqSb:23def__repr__(self):4return"按期申......
  • JAVA中容器设计的进化史:从白盒到黑盒,再到跻身为设计模式之一的迭代器
    大家好,又见面了。在我们的项目编码中,不可避免的会用到一些容器类,我们可以直接使用List、Map、Set、Array等类型。当然,为了体现业务层面的含义,我们也会根据实际需要自行封......
  • 设计模式---原型模式
    简述类型:创建型目标:通过拷贝快速创建相同或相似对象。接下来我们看一个需要改进的案例。优化案例话不多说,先来看一个创建相同或相似对象的传统写法。原版v0public......
  • 单例模式/原型模式
    原型模式:  单例模式: ......
  • 设计模式-创建型模式之单例
    1、元类实现1classSingleton(type):2def__init__(self,*args,**kwargs):3super().__init__(*args,**kwargs)4self._instance=Non......
  • JAVA设计模式-建造者模式
    JAVA设计模式-建造者模式介绍建造者模式是通过一步一步的步骤构建一个包含多个部件的对象,每个不同的对象都是具有相同的构建过程。适用于复杂对象的构建,用户不需要知道......
  • 计算机系统课程和重构设计模式图书推荐
    ▲计算机系统课程▲重构和设计模式的推荐书籍......
  • JAVA进阶--XML、XML解析、XPath、设计模式--2022年9月19日
    第一节1、XML是什么?XML的全称为(EXtensibleMarkupLanguage),是一种可扩展的标记语言它是一种数据表示格式,可以用于自定义数据格式2、......
  • 设计模式---建造者模式
    概要设计模式类型:创建型目标问题:创建对象时,参数设置的灵活性问题。(具体看案例)接下来我们看一个需要改进的案例。对象创建的优化现在有个Employee类,你能预想到在开......