单例模式
定义:保证一个类仅有一个实例,并提供一个访问它的全局访问点
引子:读取配置文件
很多地方要用到,如果每次都new 一个对象的话,会浪费内存资源。
改装成饿汉式(饿汉式有线程并发问题,懒汉式没有)
也可以完成需求,但是节约资源了?因为只有一个地方去使用,也见不到效果
场景:需要读取配置,这个用途在开发中是很常见的。
package com.tao.YanMoDesignPattern.singleton.pattern.case1_Origin;import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;/*** @Author Mi_Tao* @Date 2023/7/23* @Description* @Version 1.0**/
public class AppConfig {private String parameterA;private String parameterB;public AppConfig(){readConfig();}private void readConfig() {Properties properties = new Properties();InputStream in = null;try {in = AppConfig.class.getResourceAsStream("/Appconfig.properties");properties.load(in);// 把配置文件中的内容读出来设置到属性上this.parameterA = properties.getProperty("parameterA");this.parameterB = properties.getProperty("parameterB");} catch (IOException e) {System.out.println("转载配置文件出错了!");e.printStackTrace();}finally {try {in.close();} catch (IOException e) {e.printStackTrace();}}}public String getParameterA() {return parameterA;}public String getParameterB() {return parameterB;}
}
配置类位置
直接的做法,读取文件
package com.tao.YanMoDesignPattern.singleton.pattern.case1_Origin;/*** @Author Mi_Tao* @Date 2023/7/23* @Description* @Version 1.0**/
public class Client {public static void main(String[] args) {// 创建读取应用的配置对象AppConfig config = new AppConfig();String parameterA = config.getParameterA();System.out.println("parameterA = " + parameterA);String parameterB = config.getParameterB();System.out.println("parameterB = " + parameterB);}
}
这种方式:快捷。但是如果但很多的类都需要使用到这些配置,就会大量new 出重复的对象,造成资源浪费!!
解决方法:既然这些配置都是一样的,我们其实只需要一个实例就行。所以我们可以使用单例模式
而常见单例的方法也是多样的:
1、饿汉式
package com.tao.YanMoDesignPattern.singleton.pattern.case1_Origin;/*** 饿汉式** @Author Mi_Tao* @Date 2023/7/23* @Description* @Version 1.0**/
public class HungrySingleton {//定义一个变量来存储创建好的类实例,只能创建一次private static HungrySingleton uniqueInstance = new HungrySingleton();/*** 构造私有化方法,可以再内部控制创建实例的数目*/private static HungrySingleton getInstance() {// 直接使用创建好的实例return uniqueInstance;}/*** 单例可以有自己的操作*/public void singletonOperation(){}public String getSingletonData() {return singletonData;}// 属性,单例可以有自己的属性private String singletonData;}
2、懒汉式
package com.tao.YanMoDesignPattern.singleton.pattern.case1_Origin;/*** 懒汉式** @Author Mi_Tao* @Date 2023/7/23* @Description* @Version 1.0**/
public class LazySingleton {//定义一个变量来存储创建好的类实例private static LazySingleton uniqueInstance = null;/*** 构造私有化方法,可以再内部控制创建实例的数目*/private LazySingleton() {}/*** 获取单例** @return {@link LazySingleton}*/public static synchronized LazySingleton getSingleton(){// 判断存储实例的变量是否有值if (uniqueInstance == null){// 如果没有,就创建一个uniqueInstance = new LazySingleton();}// 否则直接返回return uniqueInstance;}/*** 单例可以有自己的操作*/public void singletonOperation(){}public String getSingletonData() {return singletonData;}// 属性,单例可以有自己的属性private String singletonData;}
测试
package com.tao.YanMoDesignPattern.singleton.pattern.case1_Origin;/*** @Author Mi_Tao* @Date 2023/7/23* @Description* @Version 1.0**/
public class ClientV2_Hungry {public static void main(String[] args) {// 创建读取应用的配置对象AppConfigV2_Hungry config = AppConfigV2_Hungry.getInstance();String parameterA = config.getParameterA();System.out.println("parameterA = " + parameterA);String parameterB = config.getParameterB();System.out.println("parameterB = " + parameterB);}
}
懒汉式。从名字不难理解。在需要使用的使用才会去创建,而不去创建就不去创建。但是会有并发问题。
并发问题的解决,我们可以使用双检锁来升级一下,解决问题
package com.tao.YanMoDesignPattern.singleton.pattern.case1_Origin;/*** 懒汉式** @Author Mi_Tao* @Date 2023/7/23* @Description 考虑并发时候的单例 双检锁* @Version 1.0**/
public class LazySingleton_current {//定义一个变量来存储创建好的类实例 内存屏障去掉,直接访问共享内存private volatile static LazySingleton_current uniqueInstance = null;/*** 构造私有化方法,可以再内部控制创建实例的数目*/private LazySingleton_current() {}/*** 获取单例** @return {@link LazySingleton_current}*/public static synchronized LazySingleton_current getSingleton(){// 判断存储实例的变量是否有值if (uniqueInstance == null){synchronized (LazySingleton_current.class){if (uniqueInstance == null){// 如果没有,就创建一个uniqueInstance = new LazySingleton_current();}}}// 否则直接返回return uniqueInstance;}/*** 单例可以有自己的操作*/public void singletonOperation(){}public String getSingletonData() {return singletonData;}// 属性,单例可以有自己的属性private String singletonData;}
tips
使用枚举类也可以实现单例
本文参考:《研磨设计模式》
相关代码: https://gitee.com/zitaoyang/design-mode