单例设计模式是我学到的第一个设计模式。之前我面试时,别人问我”你知道任何设计模式吗?”,我快速的回答说”我知道单例设计模式”,然后继续问到”你知道除了单例模式之外的其他模式吗?”,然后我就哑然了。
Java初学者都知道单例设计模式。至少他会认为他知道单例模式。单例模式的定义甚至比牛顿第三定律。那么单例模式有什么特殊点呢。它简单和直接吗?难道甚至需要一整篇文章来介绍它吗?你相信你100%了解单例设计模式吗?如果你相信,那么请从头读到尾,我相信你会有所发现的。
单例设计模式的定义中只有两个关键点:
1. 对一个类只应该有一个实例,并且
2. 我们需要全局的访问点来访问这个单一的实例
GOF说”确保类只有一个实例,并提供对它的全局访问点【GoF p127】”。
关键点不是问题和定义。在单例模式中,关键部分是实现和管理这个单一的实例。这两点看上去简单,但是实现起来比较困难。是的,很难确保”单一实例”规则和给出灵活的API、很多灵活的访问实例的方法。
实现非常特定于你所使用的编程语言。因此单例模式的安全性也非常特定于实现所使用的语言。
单例实例创建策略
我们限制构造方法并且不允许该类的一个单独的实例。但是我们在相同的类中定义该类的属性并创建和返回它。工厂设计模式可以用于创建单例实例。
package net.traininginjava.designpattern.sample;
public class Singleton {
private static Singleton instance;
private Singleton() {
}
public static Singleton getInstance() {
if(instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
在多线程环境中需要特别注意。如果你不同步返回实例的方法,那么在多线程场景下有可能允许多个实例返回。基于性能方面考虑,同步时在块级同步而不是在方法级。在上面的单例模式示例中,你可以看到它是线程安全的。
单例模式中的早初始化还是延迟初始化
上述代码示例中,单例模式使用了延迟初始化方式。唯一的实例将在第一次调用getInstance方法时被创建。我们也可以用简单的方式来实现相同的单例设计模式,只是在加载该类时就实例化该单例实例。下面的实例展示了如何在早期来初始化该实例。同样也照顾到了多线程场景。
package net.traininginjava.designpattern.sample;
package net.traininginjava.designpattern.sample;
public class Singleton2 {
private static Singleton2 instance = new Singleton2();
private Singleton2() {}
public static Singleton2 getInstance() {
return instance;
}
}
单例和序列化
使用序列化,单例模式与单例模式之间存在冲突。你可以序列化然后反序列化来获取新的相同的单例模式的类的实例。使用Java API,你可以实现如下的方法并从流中读取来覆盖实例。你可以确保总是有一个唯一的实例。
ANY-ACCESS-MODIFIER Object readResolve() throws ObjectStreamException;
这篇文章主要是想解释单例设计模式的基本知识。如果你希望在读完本文章后对单例设计模式有更深入的了解,你可以读这篇文章”When is a Singleton not a Singleton?“。
Java API中的单例模式
java.lang.Runtime#getRuntime() java.awt.Desktop#getDesktop()
Pingback 引用通告: Java面试时常见的10个问题 | Java视点