- 单例模式保证一个类仅有一个实例,并提供一个访问它的全局访问点。
- 单例模式中的“单例”通常用来代表那些本质上具有唯一性的系统组件(或者叫做资源)。比如文件系统、全局使用的配置信息、具有共享状态的对象、资源管理器(例如数据库连接池)、带有状态的工具类,等等。
饿汉式与懒汉式
密码复杂度的工具类(统计校验总次数,成功次数):
饿汉式单例模式,每次加载类都会生成一个实例。
/**
* 密码复杂度校验(并做总体数据统计)
* 饿汉式无法达到需求,因为每次都会生成新的实例
*/
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 中子类的构造函数的范围不能比父类的小,所以可能存在不守规则的客户程序使用其构造函数来产生实例,造成单例模式失效。

本文深入探讨了单例模式的两种实现方式:饿汉式和懒汉式,对比了它们的优缺点,并提出了一种更灵活的单例模式策略。
2274

被折叠的 条评论
为什么被折叠?



