设计模式之单例模式

单例模式

定义:保证一个类仅有一个实例,并提供一个访问它的全局访问点

引子:读取配置文件

很多地方要用到,如果每次都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

相关推荐
秋田君2 小时前
深入理解JavaScript设计模式之闭包与高阶函数
开发语言·javascript·设计模式
何中应3 小时前
【设计模式-4.11】行为型——解释器模式
java·设计模式·解释器模式
找不到、了4 小时前
实现单例模式的常见方式
java·开发语言·单例模式
WispX8885 小时前
【设计模式】门面/外观模式
java·开发语言·设计模式·系统架构·外观模式·插件·架构设计
蔡蓝5 小时前
设计模式-外观模式
microsoft·设计模式·外观模式
琢磨先生David5 小时前
简化复杂系统的优雅之道:深入解析 Java 外观模式
java·设计模式·外观模式
on the way 12317 小时前
结构性设计模式之Flyweight(享元)
java·设计模式·享元模式
暴躁哥21 小时前
深入理解设计模式之访问者模式
设计模式·访问者模式
佩奇的技术笔记1 天前
从Java的JDK源码中学设计模式之装饰器模式
java·设计模式·装饰器模式
on the way 1231 天前
结构型设计模式之Proxy(代理)
设计模式·代理模式