设计模式的概念
在软件工程中,设计模式(design pattern)是对软件设计中普遍存在(反复出现)的各种问题,所提出的解决方案。这个术语是由埃里希·伽玛(Erich Gamma)等人在1990年代从建筑设计领域引入到计算机科学的。
设计模式并不直接用来完成代码的编写,而是描述在各种不同情况下,要怎么解决问题的一种方案。面向对象设计模式通常以类或对象来描述其中的关系和相互作用,但不涉及用来完成应用程序的特定类或对象。设计模式能使不稳定依赖于相对稳定、具体依赖于相对抽象,避免会引起麻烦的紧耦合,以增强软件设计面对并适应变化的能力。
单例模式可能是设计模式中运用最多的模式了,顾名思义,单例模式就是只保证一个类只有一个实例对象,并提供一个访问它的全局接口。
那么怎样才能保证一个类只有一个实例对象呢?在java中自然想到通过static 修饰符来保证变量是全局的。
如何提供一个访问它的全局接口呢?很显然可以定义一个类方法,并隐藏该类的构造方法。
单例模式一般两种写法,一种称为懒汉式另一种称为饿汉式,两者主要区别懒汉式存在线程不安全问题,而饿汉式不存在此问题。
1. 懒汉式样例代码
public class Entity{
//全局对象
private static sInstance;
//私有构造器,以保证访问该实例对象的入口只有一个
private Entity(){
//do something
}
//提供一个访问该实例对象的唯一入口
public static Entity getInstance(){
//
if(sInstance == null){
sInstance = new Entity();
}
return sInstance;
}
public void doAction(){
//doing something
}
}
public class MainTest{
public static void main(String[] args){
//创建一个Entity对象
Entity globalInstance = Entity.getInstance();
//调用类Entity中的相关方法
globalInstance.doAction();
}
}
说明:上述又叫懒汉式,顾名思义就是只有等到需要对象时才会去创建,但懒汉式存在线程不安全问题,多个线程调用时可能会创建两个对象哦。
2. 饿汉式样例代码
public class Entity{
//全局对象,类第一次加载时就会创建对象
private static final sInstance = new Entity();
//私有构造器,以保证访问该实例对象的入口只有一个
private Entity(){
//doing something
}
//提供一个访问该实例对象的唯一入口
public static Entity getInstance(){
return sInstance;
}
public void doAction(){
//doing something
}
}
public class MainTest{
public static void main(String[] args){
//创建一个Entity对象
Entity globalInstance = Entity.getInstance();
//调用类Entity中的相关方法
globalInstance.doAction();
}
}
说明:饿汉式保证了对象全局且只有一个且线程安全。
3. DCL(Double-checked Locking)
public class Entity {
private static Entity sInstance;
private Entity() {
}
public static Entity getInstance() {
if (sInstance == null) {
synchronized (Entity.class) {
if (sInstance == null) {
sInstance = new Entity();
}
}
}
return sInstance;
}
}
此种写法在JDK1.5之前DCL会失效,且很难察觉,JDK1.5以后Sun意识到该问题,调整了JVM,使用static volatile 修饰sInstance 可以保证DCL不失效。
4. 静态内部类
public class Single {
private static class SingleHolder {
private static final Single sInstance = new Single();
}
private Single() {
}
public static Single getInstance() {
return SingleHolder.sInstance;
}
}
此种写法是比较推荐的,因为Single类加载时SingleHolder并不会加载,只有在首次调用getInstance方法时SingleHolder类会被加载,这样既能达到懒加载,又能保证线程安全,可谓一举两得。
5. 枚举
public enum Single {
INSTANCE;
public void doSomeThing() {
}
}
枚举的实例对象创建就是线程安全的,前四种方法在其反序列化时会创建新的对象解决办法是在单利类中添加如下代码
private Entity readResolve() throws ObjectStreamException {
return sInstance;//single object
}
,而枚举则不存在该问题。