在 Java 中应用设计模式 -- Singleton
2015-11-30 17:58
519 查看
本文介绍了设计模式中 Singleton 的基本概念,对其功能和用途进行了简单的分析,列出了通常实现 Singleton 的几种方法,并给出了详细的Java 代码.
中随处可见.
回页首
图1: Singleton 模式结构
![](http://www.ibm.com/developerworks/cn/java/designpattern/singleton/fig1.gif)
回页首
一. 用静态方法实现 Singleton这种方法是使用静态方法来监视实例的创建.为了防止创建一个以上的实例,我们最好把构造器声明为private.
这样可以防止客户程序员通过除由我们提供的方法之外的任意方式来创建一个实例,如果不把构造器声明为private,编译器就会自作聪明的自动同步一个默认的friendly构造器.这种实现方法是最常见的,也就是图1中结构的标准实现.
singletonTest运行结果是:
s1 is the same instance with s2
这证明我们只创建了一个实例.
二. 以静态变量为标志实现 Singleton在类中嵌入一个静态变量做为标志,每次都在进入构造器的时候进行检查.
问题在于构造器没有返回类型,如果确定创建一个实例成功与否.一个方法是调用一个函数来检查创建是否成功,然后简单的返回一个来自静态变量的值,但是这样做是不优雅的,而且容易发生错误.比较好的做法是创建一个当创建了一个以上的实例时可以抛出异常的类,这个类仅仅是调用父类方法,好处是用了自己命名的异常类型,错误信息更加清晰:
singletonTest运行结果是:
Creating one instance
Creating two instance
Only one instance allowed
可以看出,第一个实例顺利创建,第二个实例创建实抛出了我们自定义的异常.
三. 用注册器机制来创建 Singleton首先用集合中的Hashtable 和Enumeration来实现addItem(Object key,Object value),getItem(Object key), ,removeItem(Objectkey)等方法实现一个管理器,将key和value一一关联起来,客户程序员创建实例前首先用addItem方法进行注册,再用getItem方法获取实例.Hashtable中的key是唯一的,从而保证创建的实例是唯一的,具体实现限于篇幅不再细说,在Prototype模型的应用一文中我将会给出一个实现注册器的代码.用注册器机制来创建Singleton模式的好处是易于管理,可以同时控制多个不同类型的Singleton实例.
回页首
在The Design Patterns Java Companion一书中曾提到过用静态类的方式来实现Singleton模式,并指出java.lang.Math就是一个例子,这里我并不表示赞同,因为Math并不是一个真正的对象,我们只是直接调用Math类所包装的静态方法而已,根本就没有创建实例的过程,又从何说起只产生一个实例呢?这个问题我曾到Javaranch的论坛上发过帖子,所有回帖的人也都是对这一观点持否定态度.
在多线程的程序中,singleton可能会变的不可靠,可能会出现多个实例,解决的办法很简单,加个同步修饰符:public static synchronized Singleton getInstance().这样就保证了线程的安全性.
最后要说的是大家可能会看见一些其他实现Singleton模式的方法,因为模式在具体的应用时是灵活的,不是一成不变的,并没有一个固定的做法,但大都是上面几种方法的变形.
Design Patterns in Java -- Bob Tarr
The Design Patterns Java Companion -- James W.Copper
Design Patterns -- Gang of Four
Singletons Are Evil --http://c2.com/cgi/wiki?SingletonsAreEvil
Singletons Are Good -- http://c2.com/cgi/wiki?SingletonsAreGood When is a singleton not a singleton? --http://www.javaworld.com/javaworld/jw-01-2001/jw-0112-singleton-2.html
The forum of JavaRanch Big Saloon--http://www.javaranch.com/cgi-bin/ubb/forumdisplay.cgi?action=topics&forum=OO,+Patterns,+UML+and+Refactoring&number=9
基本概念
Singleton是一种创建性模型,它用来确保只产生一个实例,并提供一个访问它的全局访问点.对一些类来说,保证只有一个实例是很重要的,比如有的时候,数据库连接或Socket连接要受到一定的限制,必须保持同一时间只能有一个连接的存在.再举个例子,集合中的set中不能包含重复的元素,添加到set里的对象必须是唯一的,如果重复的值添加到set,它只接受一个实例.JDK中正式运用了Singleton模式来实现 set的这一特性,大家可以查看java.util.Collections里的内部静态类SingletonSet的原代码.其实Singleton是最简单但也是应用最广泛的模式之一,在JDK中随处可见.
回页首
简单分析
为了实现 Singleton模式,我们需要的是一个静态的变量,能够在不创建对象的情况下记忆是否已经产生过实例了.静态变量或静态方法都可以在不产生具体实例的情况下直接调用,这样的变量或方法不会因为类的实例化而有所改变.在图1的结构中可以看到,uniqueInstance就是这个独立的静态变量,它可以记忆对象是否已经实例化了,在静态方法Instance中对这个变量进行判断,若没有实例化过就产生一个新的对象,如果已经实例化了则不再产生新的对象,仍然返回以前产生的实例.图1: Singleton 模式结构
![](http://www.ibm.com/developerworks/cn/java/designpattern/singleton/fig1.gif)
回页首
具体实施
实现 Singleton 模式的办法通常有三种.一. 用静态方法实现 Singleton这种方法是使用静态方法来监视实例的创建.为了防止创建一个以上的实例,我们最好把构造器声明为private.
这样可以防止客户程序员通过除由我们提供的方法之外的任意方式来创建一个实例,如果不把构造器声明为private,编译器就会自作聪明的自动同步一个默认的friendly构造器.这种实现方法是最常见的,也就是图1中结构的标准实现.
public class Singleton { private static Singleton s; private Singleton(){}; /** * Class method to access the singleton instance of the class. */ public static Singleton getInstance() { if (s == null) s = new Singleton(); return s; } } // 测试类 class singletonTest { public static void main(String[] args) { Singleton s1 = Singleton.getInstance(); Singleton s2 = Singleton.getInstance(); if (s1==s2) System.out.println("s1 is the same instance with s2"); else System.out.println("s1 is not the same instance with s2"); } }
singletonTest运行结果是:
s1 is the same instance with s2
这证明我们只创建了一个实例.
二. 以静态变量为标志实现 Singleton在类中嵌入一个静态变量做为标志,每次都在进入构造器的时候进行检查.
问题在于构造器没有返回类型,如果确定创建一个实例成功与否.一个方法是调用一个函数来检查创建是否成功,然后简单的返回一个来自静态变量的值,但是这样做是不优雅的,而且容易发生错误.比较好的做法是创建一个当创建了一个以上的实例时可以抛出异常的类,这个类仅仅是调用父类方法,好处是用了自己命名的异常类型,错误信息更加清晰:
class SingletonException extends RuntimeException { public SingletonException(String s) { super(s); } } class Singleton { static boolean instance_flag = false; // true if 1 instance public Singleton() { if (instance_flag) throw new SingletonException("Only one instance allowed"); else instance_flag = true; // set flag for 1 instance } } // 测试类 public class singletonTest { static public void main(String argv[]) { Singleton s1, s2; // create one incetance--this should always work System.out.println("Creating one instance"); try { s1 = new Singleton(); } catch (SingletonException e) { System.out.println(e.getMessage()); } // try to create another spooler --should fail System.out.println("Creating two instance"); try { s2 = new Singleton(); } catch (SingletonException e) { System.out.println(e.getMessage()); } } }
singletonTest运行结果是:
Creating one instance
Creating two instance
Only one instance allowed
可以看出,第一个实例顺利创建,第二个实例创建实抛出了我们自定义的异常.
三. 用注册器机制来创建 Singleton首先用集合中的Hashtable 和Enumeration来实现addItem(Object key,Object value),getItem(Object key), ,removeItem(Objectkey)等方法实现一个管理器,将key和value一一关联起来,客户程序员创建实例前首先用addItem方法进行注册,再用getItem方法获取实例.Hashtable中的key是唯一的,从而保证创建的实例是唯一的,具体实现限于篇幅不再细说,在Prototype模型的应用一文中我将会给出一个实现注册器的代码.用注册器机制来创建Singleton模式的好处是易于管理,可以同时控制多个不同类型的Singleton实例.
回页首
小结
Singleton模式可以方便的进行扩充,产生指定数目的实例.在The Design Patterns Java Companion一书中曾提到过用静态类的方式来实现Singleton模式,并指出java.lang.Math就是一个例子,这里我并不表示赞同,因为Math并不是一个真正的对象,我们只是直接调用Math类所包装的静态方法而已,根本就没有创建实例的过程,又从何说起只产生一个实例呢?这个问题我曾到Javaranch的论坛上发过帖子,所有回帖的人也都是对这一观点持否定态度.
在多线程的程序中,singleton可能会变的不可靠,可能会出现多个实例,解决的办法很简单,加个同步修饰符:public static synchronized Singleton getInstance().这样就保证了线程的安全性.
最后要说的是大家可能会看见一些其他实现Singleton模式的方法,因为模式在具体的应用时是灵活的,不是一成不变的,并没有一个固定的做法,但大都是上面几种方法的变形.
参考资料
Thinking in Java -- Bruce EckelDesign Patterns in Java -- Bob Tarr
The Design Patterns Java Companion -- James W.Copper
Design Patterns -- Gang of Four
Singletons Are Evil --http://c2.com/cgi/wiki?SingletonsAreEvil
Singletons Are Good -- http://c2.com/cgi/wiki?SingletonsAreGood When is a singleton not a singleton? --http://www.javaworld.com/javaworld/jw-01-2001/jw-0112-singleton-2.html
The forum of JavaRanch Big Saloon--http://www.javaranch.com/cgi-bin/ubb/forumdisplay.cgi?action=topics&forum=OO,+Patterns,+UML+and+Refactoring&number=9
相关文章推荐
- openstack-java-sdk maven 工程环境搭建及编译
- Date类型的实体类属性中,Date必须是Java.util.Date,而不能是Java.sql.Date
- 第二章 Spring MVC入门
- Java新手之路——Day03 Java语言基础组成_数据类型
- JavaEE_Mybatis_SpringMVC_Mybatis_lesson9_引用SQL片段
- Java基础1
- Spring学习之整合Struts2
- Spring注解详解
- Java IDE_Myeclipse快捷键
- Java正则表达式之语法规则
- Calendar获取指定时间
- Hadoop运行mapreduce任务过程中报错:Error: Java heap space问题解决
- spring 事务浅谈
- java并发编程-线程池的使用
- 给Java程序猿们推荐一些值得一看的好书
- java 多线程
- bug--java访问hdfs (Server IPC version 9 cannot communicate with client version 4 错误)
- myeclipse快捷键
- Java开发中的23种设计模式详解(转)
- 【转】java 解析 plist文件