《Java与模式》学习笔记——Singleton
Posted brooksychen
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《Java与模式》学习笔记——Singleton相关的知识,希望对你有一定的参考价值。
单例(Singleton)模式的要点有三个:一是某个类只能有一个实例;二是它必须自行创建这个实例;三是它必须自行向整个系统提供这个实例。一些资源管理器常常设计成单例模式。
单例模式的结构如下:
饿汉式单例类:
public
class
EagerSingleton
...
![](https://image.cha138.com/20221205/85f48b9059494c2b8f0fdd9f9fccf7c2.jpg)
private EagerSingleton() ...
![](https://image.cha138.com/20221205/85f48b9059494c2b8f0fdd9f9fccf7c2.jpg)
public static EagerSingleton getInstance() ...
return m_instance;
private static final EagerSingleton m_instance = new EagerSingleton();
由于构造函数是私有的,因此此类不能被继承。
懒汉式单例类:
public
class
LazySingleton
...
![](https://image.cha138.com/20221205/85f48b9059494c2b8f0fdd9f9fccf7c2.jpg)
private LazySingleton() ...
![](https://image.cha138.com/20221205/85f48b9059494c2b8f0fdd9f9fccf7c2.jpg)
synchronized public static LazySingleton getInstance() ...
![](https://image.cha138.com/20221205/85f48b9059494c2b8f0fdd9f9fccf7c2.jpg)
if (m_instance == null) ...
m_instance = new LazySingleton();
return m_instance;
private static LazySingleton m_instance = null;
上例的静态工厂方法使用了同步化,以处理多线程环境。
饿汉式单例类在自己被加载时就将自己实例化,即使加载器是静态的,在饿汉式单例类被加载时仍会将自己实例化。单从资源利用效率角度来讲,这个比懒汉式单例类稍差些。从速度和反应时间角度来讲,则比懒汉式单例类稍好些。然而,懒汉式单例类在实例化时,必须处理好在多个线程同时首次引用此类时的访问限制问题,特别是当单例类作为资源控制器在实例化时必须涉及资源初始化,而资源初始化很有可能耗费时间。这意味着出现多线程同时首次引用此类的几率变得较大。
饿汉式单例类可以在Java语言内实现,但不易在C++内实现,因为静态初始化在C++里没有固定的顺序,因而静态的m_instance变量的初始化与类的加载顺序没有保证,可能会出问题。
登记式单例类:
登记式单例类是GoF为了克服饿汉式单例类及懒汉式单例类均不可继承的缺点而设计的。它的实例化方式可以是饿汉式或懒汉式的,但它的子类实例化的方式只能是懒汉式的。
package
com.javapatterns.singleton.demos;
![](https://image.cha138.com/20221205/f804f552e3214bbbb735fc048d08d9a7.jpg)
import
java.util.HashMap;
![](https://image.cha138.com/20221205/f804f552e3214bbbb735fc048d08d9a7.jpg)
![](https://image.cha138.com/20221205/af110d2a3f8d4ecb9d5227e38f0616eb.jpg)
public
class
RegSingleton
...
![](https://image.cha138.com/20221205/92a0bda571d4494c85a4a91935f2c06f.jpg)
![](https://image.cha138.com/20221205/85f48b9059494c2b8f0fdd9f9fccf7c2.jpg)
protected RegSingleton() ...
![](https://image.cha138.com/20221205/92a0bda571d4494c85a4a91935f2c06f.jpg)
![](https://image.cha138.com/20221205/85f48b9059494c2b8f0fdd9f9fccf7c2.jpg)
static public RegSingleton getInstance(String name) ...
![](https://image.cha138.com/20221205/85f48b9059494c2b8f0fdd9f9fccf7c2.jpg)
if (name == null) ...
name = "com.javapatterns.singleton.demos.RegSingleton";
System.out.println("From RegSingleton: requesting for " + name );
![](https://image.cha138.com/20221205/85f48b9059494c2b8f0fdd9f9fccf7c2.jpg)
if (m_registry.get(name) == null) ...
![](https://image.cha138.com/20221205/85f48b9059494c2b8f0fdd9f9fccf7c2.jpg)
try ...
m_registry.put( name, Class.forName(name).newInstance() ) ;
![](https://image.cha138.com/20221205/85f48b9059494c2b8f0fdd9f9fccf7c2.jpg)
catch(ClassNotFoundException e) ...
System.out.println("Class " + name + " is not found.");
![](https://image.cha138.com/20221205/85f48b9059494c2b8f0fdd9f9fccf7c2.jpg)
catch(InstantiationException e) ...
System.out.println("Class " + name + " can not be instantiated.");
![](https://image.cha138.com/20221205/85f48b9059494c2b8f0fdd9f9fccf7c2.jpg)
catch(IllegalAccessException e) ...
System.out.println("Class " + name + " can not be accessed.");
return (RegSingleton) (m_registry.get(name));
![](https://image.cha138.com/20221205/92a0bda571d4494c85a4a91935f2c06f.jpg)
static private HashMap m_registry = new HashMap();
![](https://image.cha138.com/20221205/92a0bda571d4494c85a4a91935f2c06f.jpg)
![](https://image.cha138.com/20221205/85f48b9059494c2b8f0fdd9f9fccf7c2.jpg)
static ...
RegSingleton x = new RegSingleton();
m_registry.put( x.getClass().getName() , x);
![](https://image.cha138.com/20221205/92a0bda571d4494c85a4a91935f2c06f.jpg)
![](https://image.cha138.com/20221205/85f48b9059494c2b8f0fdd9f9fccf7c2.jpg)
public String about() ...
return "Hello, I am RegSingleton.";
![](https://image.cha138.com/20221205/92a0bda571d4494c85a4a91935f2c06f.jpg)
package
com.javapatterns.singleton.demos;
![](https://image.cha138.com/20221205/f804f552e3214bbbb735fc048d08d9a7.jpg)
import
java.util.HashMap;
![](https://image.cha138.com/20221205/f804f552e3214bbbb735fc048d08d9a7.jpg)
![](https://image.cha138.com/20221205/af110d2a3f8d4ecb9d5227e38f0616eb.jpg)
public
class
RegSingletonChild
extends
RegSingleton
...
![](https://image.cha138.com/20221205/92a0bda571d4494c85a4a91935f2c06f.jpg)
![](https://image.cha138.com/20221205/85f48b9059494c2b8f0fdd9f9fccf7c2.jpg)
public RegSingletonChild() ...
![](https://image.cha138.com/20221205/92a0bda571d4494c85a4a91935f2c06f.jpg)
![](https://image.cha138.com/20221205/85f48b9059494c2b8f0fdd9f9fccf7c2.jpg)
static public RegSingletonChild getInstance() ...
return (RegSingletonChild) RegSingleton.getInstance( "com.javapatterns.singleton.demos.RegSingletonChild" );
![](https://image.cha138.com/20221205/92a0bda571d4494c85a4a91935f2c06f.jpg)
![](https://image.cha138.com/20221205/85f48b9059494c2b8f0fdd9f9fccf7c2.jpg)
public String about() ...
return "Hello, I am RegSingletonChild.";
![](https://image.cha138.com/20221205/92a0bda571d4494c85a4a91935f2c06f.jpg)
由于子类必须允许父类以构造函数调用产生实例,因此,它的构造函数必须是公开的。这样一来,就等于允许了以这样的方式产生实例而不在父类的登记中。这是登记式单例类的一个缺点。由于父类的实例必须存在才可能有子类的实例,这在有些情况下是一个浪费。这是登记式单例类的另一个缺点。
一个单例类可以是有状态的(stateful),一个有状态的单例对象一般也是可变(mutable)单例对象。单例类也可以是没有状态的(stateless),一个没有状态的单例类也就是不变(immutable)单例类。
在任何使用了EJB、RMI和JINI技术的分散式系统中,应当避免使用有状态的单例模式。
除非系统有协调机制,不然在有多个类加载器的情况下应当尽量避免使用有状态的单例类。
Java语言中,像Runtime、java.awt.Toolkit类都是单例类。
默认实例模式(Default Instance Pattern):
有些设计师将不完全的单例模式叫做“默认实例模式”。在所谓的“默认实例模式”里面,一个类提供静态的方法,如同单例模式一样,同时又提供一个公开的构造函数,如同普通的类一样。这样做的唯一好处是,这种模式允许客户端选择如何将类实例化:创建新的自己独有的实例,或者使用共享的实例。以上是关于《Java与模式》学习笔记——Singleton的主要内容,如果未能解决你的问题,请参考以下文章
《Java与模式》学习笔记——Singleton
Java学习笔记(二十四):单例设计模式singleton
Java:Effective java学习笔记之 用私有构造器或者枚举类型强化SIngleton属性
一文彻底搞懂单例模式(Singleton-Pattern)
单例模式的学习笔记
java设计模式学习 ----- 单例模式(Singleton)