您好,欢迎访问三七文档
设计模式之Singleton(单态)Signleton概要单例定义单例模式图单例模式特点单例模式优缺点单例模式应用单例定义单例模式是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单态类的特殊类。通过单态模式可以保证系统中一个类只有一个实例而且该实例易于外界访问,从而方便对实例个数的控制并节约系统资源。如果希望在系统中某个类的对象只能存在一个,单态模式是最好的解决方案。单例模式UML图Singleton模式主要作用是保证在Java应用程序中,一个类Class只有一个实例存在。在很多操作中,比如建立目录数据库连接都需要这样的单线程操作。还有,singleton能够被状态化;这样,多个单态类在一起就可以作为一个状态仓库一样向外提供服务,比如,你要论坛中的帖子计数器,每次浏览一次需要计数,单态类能否保持住这个计数,并且能synchronize的安全自动加1,如果你要把这个数字永久保存到数据库,你可以在不修改单态接口的情况下方便的做到。另外方面,Singleton也能够被无状态化。提供工具性质的功能,Singleton模式就为我们提供了这样实现的可能。使用Singleton的好处还在于可以节省内存,因为它限制了实例的个数,有利于Java垃圾回收(garbagecollection)。我们常常看到工厂模式中类装入器(classloader)中也有用Singleton模式实现的,因为被装入的类实际也属于资源。单例模式特点单例模式的要点有三个:一是某个类只能有一个实例;二是它必须自行创建这个实例;三是它必须自行向整个系统提供这个实例。从具体实现角度来说,就是以下三点:一是单例模式的类只提供私有的构造函数;二是类定义中含有一个该类的静态私有对象;三是该类提供了一个静态的共有的函数用于创建或获取它本身的静态私有对象。单例模式优缺点优点一、实例控制单例模式会阻止其他对象实例化其自己的单例对象的副本,从而确保所有对象都访问唯一实例。二、灵活性因为类控制了实例化过程,所以类可以灵活更改实例化过程。缺点一、开销:虽然数量很少,但如果每次对象请求引用时都要检查是否存在类的实例,将仍然需要一些开销。二、可能的开发混淆:使用单例对象(尤其在类库中定义的对象)时,开发人员必须记住自己不能使用new关键字实例化对象。三、对象生存期:不能解决删除单个对象的问题。单例模式应用在java中,可以使用以下这种方式使用单例模式创建类的实例:publicclassMyBean{privatestaticMyBeaninstance=null;privateMyBean(){//dosomething}publicstaticsynchronizedMyBeangetInstance(){if(instance==null){instance=newMyBean();}returninstance;}}当一个类的实例可以有且只可以一个的时候就需要用到了。WHY?一般Singleton模式通常有几种形式:第一种形式:也是常用的形式:publicclassSingleton{privateSingleton(){}//在自己内部定义自己一个实例,是不是很奇怪?//注意这是private只供内部调用privatestaticSingletoninstance=newSingleton();//这里提供了一个供外部访问本class的静态方法,可以直接访问publicstaticSingletongetInstance(){returninstance;}}第二种形式:publicclassSingleton{//在自己内部定义自己的一个实例,只供内部调用privatestaticSingletoninstance=newSingleton();privateSingleton(){//dosomething}//这里提供了一个供外部访问本class的静态方法,可以直接访问publicstaticSingletongetInstance(){returninstance;}}使用单例模式的时机是当实例存在多个会引起程序逻辑错误的时候深入单例模式Question:在什么情况下,单例有可能成为多例?深入单例模式1,存在两个或两个以上的Java虚拟机在这种情况下,对于方式一和方式二,每个虚拟机将会各有自己的一个独立的singleton实例。使用分布式技术的系统架构,像EJB、RMI、JINI都会涉及到多个虚拟机。在这种情况下,如果要使用单例模式,建议不要使用带状态的singleton实例,而仅在single实例中提供对资源文件的访问。但是在EJB中,对资源文件的访问是由EJB容器进行管理的。所以在EJB中不推荐使用singleton模式。深入单例模式2,使用不同的类加载器一个singleton对象的class文件,分别被类加载器CL1和CL2加载,系统中就会有两个不同的singleton实例存在。同一个Java虚拟机中,允许存在多个类加载器。当使用页面applet技术、JINI技术或者RMI技术,加载不同远程服务器上的class文件的时候,都会使用不同的类加载器。使用自己定制的类加载器,也会出现这种问题。使用不同类加载器加载的singleton对象,对象名可以相同,包名可以一致,但是在虚拟机中,还是把它们视为不同的实例。深入单例模式3,singleton实例创建后被重新加载两种情况singleton实例会被重新加载,重新加载后得到的singleton都不同于之前的singleton实例。一是当没有任何实例持有对singleton实例的引用时,singleton实例会被垃圾回收。当又出现对singleton实例的引用请求时,Java虚拟机会重新加载一个新的singleton实例。二是singleton实例也可以响应Java程序请求,而重新加载。譬如servlet容器就可以这么做,当它决定废弃当前servlet实例时,调用其destory()方法,然后容器重新加载servlet实例,调用其init()方法重新初始化servlet.深入单例模式4,存在singleton对象的继承子类如果singleton类的构造方法不是private,而是public、protected或默认,该singleton类就可以被继承。因为子类的实例同时也是父类的实例,我们就可以通过该singleton类的子类创建出多个singleton实例。深入单例模式5,反系列化已经存在的singleton实例的系列化文件当使用ObjectOutputStream把singleton实例系列化为IO文件1后,关闭这个ObjectOutputStream重新打开、创建一个新的ObjectOutputStream或者调用当前ObjectOutputStream的reset()方法,再次系列化singleton实例会得到一个新的IO文件2。反系列化IO文件1和IO文件2,会得到两个不同的singleton实例。同样,使用ObjectOutputStream读取同一个singleton实例的序列化IO文件,关闭这个ObjectOutputStream重新打开、创建一个新的ObjectOutputStream或者调用当前ObjectOutputStream的reset()方法,都会得到不同的singleton实例。实际上,除了Java的IO机制可以反系列化类文件,基于XML的一些技术,包括SOAP和WDDX也可以做这样的处理,此类问题同样会存在。深入单例模式6,在多线程环境下未使用同步前面提到单例模式实现方式2对整个getInstance()方法使用synchronized,如果在多线程环境下不这样做,也会产生多个singleton实例。publicstaticMySingletongetInstance(){if(_instance==null){_instance=newMySingleton();}return_instance;}深入单例模式7,singleton被克隆使用对象的clone()方法克隆对象,也可以创建一个新的Singleton对象。要禁用这一功能,需要禁用对象的克隆方法,产生一个CloneNotSupportedException例外。publicclassSingleton{privateSingleton(){}privatestaticSingletoninstance=newSingleton();publicstaticSingletongetInstance(){returninstance;}publicObjectclone()throwsCloneNotSupportedException{thrownewCloneNotSupportedException();}}深入单例模式结论:如何正确使用单例模式?绝对的单例是不可能实现的,我们只能在充分考虑项目需求和应用程序所运行的环境的前提下,在有限的范围内实现单例模式。如下几点建议可供参考:1,分布式应用程序不推荐使用,特别是EJB应用。2,使用不同的类加载器时,不推荐使用。3,使用Web容器时,要确保singleton实例不是容器管理的。4,构造方法一定要私有化,确保无继承子类。5,避免实现Serializable接口。如果一定要实现这个接口,严格控制系列化和反系列化的过程。6,使用正确的且效率高的同步。7,禁用对象的克隆方法。2011013105李国文2011013100姬如意软件112班Thanks
本文标题:Signleton
链接地址:https://www.777doc.com/doc-3126562 .html