关于设计模式系列文章:
关于设计模式系列的文章主要参考菜鸟教程中的示例进行学习和理解,可能文章中有时会有直接从菜鸟教程摘抄下来的片段,先在此说明。除了主要在菜鸟教程上学习设计模式外,我还会看其他的博客,然后自己编码进行学习和理解每个设计模式的内涵。参考教程网址(菜鸟教程)http://www.runoob.com/design-pattern/singleton-pattern.html
关于设计模式:
设计模式(Design pattern)代表了最佳的实践,是软件开发人员
在长期的软件开发和实践中总结出来的针对每种具体问题提出来的解决方案,然后经过人们的整理和总结,最后形成了针对具体问题且颇有成效的设计模式。设计模式分为三类:创建型模式(Creational Patterns)、结构型模式(Structural Patterns)、行为型模式(Behavioral Patterns)。除此之外还有javaee的设计模式。
单例设计模式:
单例设计模式简单来说就是对于一个类而言,它只能创建一个实例对象,这样的应用场景有很多,比如一个游戏中玩家要操作的任务角色,总不能操作不同的游戏角色取修改同一游戏数据吧,当然可能我描述的不太准确,但单例模式运用的地方确实挺多的。
单例设计模式实现:
单例设计模式的实现方式有很多,这里就只介绍3种
1,饿汉式:加载类的时候就创建对象,这种方式是线程安全的,即不会创建多个对象。
//饿汉式单例
class SingletonA
{
//将类的唯一实例定义为静态成员
private static SingletonA ss = new SingletonA();
//这一步是实现单例模式的关键所在
//即让其他用户无法在外部使用new的方式创建对象
private SingletonA(){}
//同时提供一个public的静态方法,供外部获取这个唯一的实例
public static SingletonA getInstance()
{
return ss;
}
}
测试:
public static void test1()
{
SingletonA a = SingletonA.getInstance();
SingletonA b = SingletonA.getInstance();
System.out.println("a---hashCode:"+a.hashCode());
System.out.println("b---hashCode:"+b.hashCode());
System.out.println("a.equals(b):"+a.equals(b));
}
控制台输出如下,可以看出来我们获取的确实是同一个对象,实现了单例
a---hashCode:366712642
b---hashCode:366712642
a.equals(b):true
2,懒汉式单例:在类一加载的时候并不实例化对象,而是等到别人第一次调用了获取该单例对象的方法时才创建对象,以后再有人调用就直接返回该对象。
//懒汉式单例
class SingletonB
{
//创建单例对象的引用,但并不赋值
private static SingletonB ss = null;
//私有化构造器
private SingletonB(){}
//提供公有方法,供外界获取该实例对象
public static SingletonB getInstance()
{
//没有创建过对象才实例化
if(ss == null)
ss = new SingletonB();
//否则直接返回已创建的对象
return ss;
}
}
这种方法也能实现单例模式,但是它是线程不安全的,即在多线程下并不能保证单例,原因如下:
public static SingletonB getInstance()
{
if(ss == null)
{
//1,假设线程1调用了这个方法,ss为null,进入了if语句块,而此时线程1挂起
//2,线程2调用本方法,ss也为null,这时程序向下执行
ss = new SingletonB();
//3,线程2获得该创建的对象,而这个时候线程1被唤醒,
//4,这条语句再次被执行,线程1获得了另一个实例对象,破坏了单例
}
return ss;
}
3,懒汉式单例,线程安全,是针对上述问题,人们提出的又一解决办法,让其实现线程安全
//懒汉式单例,线程安全
class SingletonC
{
//创建单例对象的引用,但并不赋值
private static SingletonC ss = null;
//私有化构造器
private SingletonC(){}
//提供公有方法,供外界获取该实例对象
public static synchronized SingletonC getInstance()
{
//没有创建过对象才实例化
if(ss == null)
ss = new SingletonC();
//否则直接返回已创建的对象
return ss;
}
}
这种方法虽然实现了多线程下的单例,但是由于关键字synchronized的存在,让这种方法效率很低。
总结:
注意,如果考虑java的反射机制的话,那么就算是一个类的构造方法是private了,也能让其实例化,严格来说会让所有的(就我知道的而言,即使我知道的并不多)实现单例的方法失效。以上不考虑反射机制的存在。
对于以上三个实现单例设计模式的方法来看,不建议使用第二种方式,由于第三种方式效率不高,所以没有特殊要求的话建议使用第一种方法实现单例。当然还有其他实现单例的做法,由于本人水平有限,目前无法再做深入的探讨,见笑了!