JAVA设计模式(二) -- 单例模式

本文深入探讨了单例模式的两种实现方式:饿汉式和懒汉式,对比了它们的优缺点,并提出了一种更灵活的单例模式策略。
  • 单例模式保证一个类仅有一个实例,并提供一个访问它的全局访问点。
  • 单例模式中的“单例”通常用来代表那些本质上具有唯一性的系统组件(或者叫做资源)。比如文件系统、全局使用的配置信息、具有共享状态的对象、资源管理器(例如数据库连接池)、带有状态的工具类,等等。

饿汉式与懒汉式

密码复杂度的工具类(统计校验总次数,成功次数):
饿汉式单例模式,每次加载类都会生成一个实例。

/**
 * 密码复杂度校验(并做总体数据统计)
 * 饿汉式无法达到需求,因为每次都会生成新的实例
 */
public class PasswordComplexityCheck {
	private int totalCount;
	private int successCount;
	public int getTotalCount(){
		return this.totalCount;
	}
	public int getSuccessCount(){
		return this.successCount;
	}
	private PasswordComplexityCheck(){}
    private static final PasswordComplexityCheck instance = new PasswordComplexityCheck();
    
    public static PasswordComplexityCheck getInstance() {
        return instance;
    }
    public boolean checkingComplexity(String password){
    	//总数计数
    	this.totalCount ++;
        //字母、数字、特殊符号必须有2种,且字符位数介于8至16位
        boolean isMatched = password.matches 
        ("^(?![0-9]+$)(?![a-zA-Z]+$)(?![-/^/$/.//,;=:'!@#%&/*/|/?/+/(/)/[/]/{/}]+$)[0-9A-Za-z/^/$/.//,;=:'!@#%&/*/|/?/+/(/)/[/]/{/}/-]{8,16}$");
        if(isMatched){
        	//成功计数
        	this.successCount++;
        }
        return isMatched;
    }
}

懒汉式单例模式,仅会生成一个实例。

/**
 * 密码复杂度校验(并做总体数据统计)
 */
public class PasswordComplexityCheck {
	private int totalCount;
	private int successCount;
	public int getTotalCount(){
		return this.totalCount;
	}
	public int getSuccessCount(){
		return this.successCount;
	}
	private PasswordComplexityCheck(){}
    private static volatile PasswordComplexityCheck instance = null;//1.禁止指令重排2.可见性
    //这么写不好,synchronized 同步 并发效率太低
    /*public static synchronized PasswordComplexityCheck getInstance() {
    	if(instance == null){
    		instance = new PasswordComplexityCheck();
    	}
        return instance;
    }*/
    // DCL(Double check lock 双端检锁机制)
    public static PasswordComplexityCheck getInstance() {
    	if (instance == null) { // 多线程下为同步加锁之前先校验一道,提升执行效率
            synchronized (PasswordComplexityCheck.class) { // 同步锁
                if (instance == null)//此处还需判断一次,因为有可能在排队等待的线程进入时变成了被初始化了的对象
                    instance = new PasswordComplexityCheck();
                    //new 一个对象涉及到的底层操作实际有:1.分配内存空间allocate;2.初始化对象3.将对象指针指向分配的内存地址
                    //有可能操作系统自动优化的指令重排(单线程下不影响结果,没有数据依赖关系,则计算机可能会对其进行指令重排):
                    //2,3互换,即会出现instance不为空但是未初始化完成的情况,因此不用volatile声明的变量存在线程安全的问题
            }
        }
        return instance;
    }

    public boolean checkingComplexity(String password){
    	//总数计数
    	this.totalCount ++;
        //字母、数字、特殊符号必须有2种,且字符位数介于8至16位
        boolean isMatched = password.matches 
        ("^(?![0-9]+$)(?![a-zA-Z]+$)(?![-/^/$/.//,;=:'!@#%&/*/|/?/+/(/)/[/]/{/}]+$)[0-9A-Za-z/^/$/.//,;=:'!@#%&/*/|/?/+/(/)/[/]/{/}/-]{8,16}$");
        if(isMatched){
        	//成功计数
        	this.successCount++;
        }
        return isMatched;
    }
}

先让我们来比较一下这两种实现方式。
首先他们的构造函数都是私有的,彻底断开了使用构造函数来得到类的实例的通道,但
是这样也使得类失去了多态性(大概这就是为什么有人将这种模式称作单态模式)。
在第二种方式中,对静态工厂方法进行了同步处理,原因很明显——为了防止多线程环
境中产生多个实例;而在第一种方式中则不存在这种情况。
在第二种方式中将类对自己的实例化延迟到第一次被引用的时候。而在第一种方式中则
是在类被加载的时候实例化,这样多次加载会照成多次实例化。但是第二种方式由于使用了
同步处理,在反应速度上要比第一种慢一些。

一种灵活的单例模式策略

import java.util.HashMap; 
public class Singleton{ 
	 //用来存放对应关系
	 private static HashMap sinRegistry = new HashMap(); 
	 static private Singleton s = new Singleton(); 
	 //受保护的构造函数
	 protected Singleton() {} 
	 public static Singleton getInstance(String name) { 
		 if(name == null) 
		 name = "Singleton"; 
		 if(sinRegistry.get(name)==null){ 
			 try{ 
			 	sinRegistry.put(name , Class.forName(name).newInstance()); 
			 }catch(Exception e){ 
			 	e.printStackTrace(); 
			 } 
 		} 
 		return (Singleton)(sinRegistry.get(name)); 
 	} 
	public void test(){ 
		System.out.println("getclasssuccess!"); 
	} 
} 
public class PasswordComplexityCheck extends Singleton{
	private int totalCount;
	private int successCount;
	public int getTotalCount(){
		return this.totalCount;
	}
	public int getSuccessCount(){
		return this.successCount;
	} 
	public PasswordComplexityCheck (){} 
	static public PasswordComplexityCheck getInstance(){ 
		return (PasswordComplexityCheck)Singleton.getInstance("PasswordComplexityCheck"); 
	} 
	public boolean checkingComplexity(String password){
    	//总数计数
    	this.totalCount ++;
        //字母、数字、特殊符号必须有2种,且字符位数介于8至16位
        boolean isMatched = password.matches 
        ("^(?![0-9]+$)(?![a-zA-Z]+$)(?![-/^/$/.//,;=:'!@#%&/*/|/?/+/(/)/[/]/{/}]+$)[0-9A-Za-z/^/$/.//,;=:'!@#%&/*/|/?/+/(/)/[/]/{/}/-]{8,16}$");
        if(isMatched){
        	//成功计数
        	this.successCount++;
        }
        return isMatched;
    }
} 

由于在 java 中子类的构造函数的范围不能比父类的小,所以可能存在不守规则的客户程序使用其构造函数来产生实例,造成单例模式失效。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Funnee

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值