• 设计模式-单例模式


    一、单例模式核心思想

    单例模式是一种特殊的工厂方法模式,它适合于一个类只有一个实例的情况,比如窗口管理器打印缓冲池和文件系统。典型的情况是,那些对象的实例能够被整个软件系统的不同对象访问,因此需要一个全局的访问指针,这便是众所周知的单例模式的应用。当然这只有在不再需要任何多于一个的实例的情况下出现。

    通过单例模式你可以:

    • 确保一个类只有一个实例被建立。
    • 提供了一个对对象的全局访问指针。
    • 在不影响单例类的客户端的情况下允许将来有多个实例。

    单例模式的实例在全局中有且只有一个,并且该实例必须由自身创建,不能够被克隆。为了满足这些要求,一个标准的单例模式需要包含如下4个要素:

    (1) 拥有一个私有的静态实例,该实例禁止外部访问。
    (2) 拥有私有的默认构造函数,防止使用构造函数进行实例化。
    (3) 拥有一个静态工厂方法,并且必须是同步的,防止多线程环境同时执行。
    (4) 重写clone0)函数,并返回当前实例对象,默认的clone()函数会创建新的实例。

    根据以上4点要求,完成的标准单例模式代码如下程序所示:

    package creation.singleton;
    
    public class SingletonFactory {
    
    	//(1)私有的防止外部引用
    	private static SingletonFactory _instance = null;
    	
    	//(2)私有的默认构造函数,防止使用构造函数进行实例化
    	private SingletonFactory(){}
    	
    	//(3)单例静态工厂方法,同步防止多线程环境同时执行
    	synchronized public static SingletonFactory getInstance(){
    		if( _instance == null){
    			_instance = new SingletonFactory();
    		}
    		return _instance;
    	}
    
    	//(4)重写该函数,默认的cloneO函数会创建新的实例
    	public SingletonFactory clone(){
    		return getInstance();
    	}
    }
    

    以上单例模式的代码中的各项缺一不可,读者可以作为单例模式的标准模板使用,在后面的示例中我们也将使用该模板进行开发。

    二、何时使用单例模式

    使用单例模式有一个前提条件:就是在一个系统中某一个类的实例必须只有一个,如果可以有多个实例存在,就不能够使用单例模式。以上的说法比较抽象,具体的可以应用在如下场景:

    • 系统的全局变量、存储区域。
    • 系统的全局配置文件。
    • 系统的全局操作函数。

    由此可见,只要希望在全局使用一个统一的对象,就可以使用单例模式。

    三、属性文件加载工厂实例

    根据以上的场景,我们举例说明。以属性文件为例,在一个系统中,通常需要配置全局的属性文件,例如用户名和密码文件,如下程序所示:

    属性文件user.properties

    admin=123
    liuzhongbing=123
    guest=123
    

    为了在系统中使用该属性文件中定义的用户名和密码,可以根据以上的单例模式模板文件SingletonFactory.java创建一个工厂类,其中定义一个私有变量 properties,用来在系统启动时将文件读取数据存储在该变量中,并提供一个按照用户名查找密码的函数getConfigO。完整的代码如下程序所示:

    属性文件工厂PropertiesFactory.java

    import java.util.Properties;
    import java.io.FilelnputStream;
    
    public class PropertiesFactory {
    
    	//(1)私有的防止外部引用
    	private static PropertiesFactory instance = null;
    	private Properties properties = new Properties();
    	
    	//(2)私有的默认构造函数,防止使用构造函数进行实例化
    	private PropertiesFactory(){
    		try {
    			properties.load(new
    			FileInputStream("sre/creation/singleton/user.properties'));
    		}catch (Exception e) {
    			e.printStackTrace();
    		}
    	}
    
    	//(3)单例静态工厂方法,同步防止多线程环境同时执行
    	public static synchronized PropertiesFactory getInstance(){
    		if( instance == null){
    			instance = new PropertiesFactory();
    			return instance;
    		}
    	}
    
    	//(4)重写该函数,默认的clone()函数会创建新的实例
    	public PropertiesFactory clone(){
    		return getInstance();
    	}
    	
    	public String getConfg(String key){
    		return properties.getProperty(key);
    	}
    
    
    }
    

    编写测试类来读取其中的密码,如下程序所示:

    测试类PropertiesFactoryTest.java

    package creation.singleton;
    
    
    publie class PropertiesFactoryTest {
    	public static void main(String[] args){
    		PropertiesFactory factory= PropertiesFactory.getInstance();
    		String pwdl = factory.getConfig("admin");
    		System.out.println(pwdl);
    	}
    }
    
    

    运行该程序即会输出密码:123。

    四、Java中的应用–日历单例类 Calendar

    package java.util;
    import java.io.IOException;
    import java.io.ObjectInputStream;
    import java.io .ObjectOutputStream;
    import java.io.Serializable;
    import java.security.AccessController;
    import java.security.PrivilegedExceptionAction;
    import java.text.DateFormat;
    import java.text.DateFormatSymbols;
    import sun.util.BuddhistCalendar;
    import sun.util.calendar.Zonelnfo;
    import sun.util.resources.LocaleData;
    public abstract class Calendar implements Serializable, Cloneable, Comparable<Calendar>{
    
    	protected Calendar(){
    		this(TimeZone.getDefaultRef(), Locale.getDefault());
    		sharedZone = true;
    	}
    	
    	protected Calendar(TimeZone zone, Locale aLocale){
    		fields = new int[FIELD_COUNT];
    		isSet = new boolean[FIELD_COUNT];
    		stamp = new int[FIELD_COUNT];
    		this.zone = zone;
    		setWeekCountData(aLocale);
    	}
    	
    	public static Calendar getInstance() {
    		Calendar cal = createCalendar(TimeZone.getDefaultRef(), Locale.getDefault());
    		cal.sharedZone = true;
    		return cal;
    	}
    	
    	public static Calendar getInstance(TimeZone zone) {
    		return createCalendar(zone, Locale.getDefault());
    	}
    	
    	public statie Calendar getInstance(Loeale aLocale) {
    		Calendar cal = createCalendar(TimeZone.getDefaultRef(), aLocale)
    		cal.sharedZone = true;
    		return cal;
    	}
    	
    	public static Calendar getInstance(TimeZone zone, Locale aLocale) {
    		return createCalendar(zone, aLocale);
    	}
    
    }
    

    从代码可以看出,该单例类的应用并不是严格遵守单例模式的四项规则。所以,在实际的开发中,可以根据实际的需要来自由运用。

  • 相关阅读:
    SpringBoot SpringBoot 开发实用篇 4 数据层解决方案 4.15 ES 文档操作
    【转】OAK-D双目相机进行标定及标定结果说明
    开关电源环路稳定性分析(01)-Buck变换器
    【Spring】@Cacheable 注解的使用及原理
    Redis
    客户流失?来看看大厂如何基于spark+机器学习构建千万数据规模上的用户留存模型 ⛵
    低代码掀起“数字革命”,引领制造业数字化转型
    DIVFusion_ Darkness-free infrared and visible image fusion 论文解读
    小米AI实验室最新研究论文收录于COLING 2022,介绍一种更有效的鲁棒性神经机器翻译训练方法...
    大学生面试JAVA程序员应该具备的JAVA面试题库
  • 原文地址:https://blog.csdn.net/qq_35885952/article/details/139618028