设计模式之01单例模式(笔记)

来源:互联网 发布:帝国cms定时审核 编辑:程序博客网 时间:2024/06/08 09:28

1 定义:

1.1文字定义:Ensure a classhas only one instance, and provide a global point of access to it.(确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。)

1.2通用类图:


图1 单例模式通用类图

1.3通用代码:

public class Singleton {         privatestatic final Singleton singleton = new Singleton();                 //限制产生多个对象         privateSingleton(){         }                 //通过该方法获得实例对象         publicstatic Singleton getSingleton(){                   returnsingleton;         }                 //类中其他方法,尽量是static         publicstatic void doSomething(){         }}

 

2优点:

  • 内存仅有一个实例,减少内存开支;特别是一个对象需要频繁地创建、销毁时,而且创建或销毁时性能又无法优化,单例模式的优势就非常明显。
  • 当一个对象的产生需要比较多的资源时,如读取配置、产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后用永驻留内存的方式来解决(在Java EE中注意JVM垃圾回收机制)。
  • 单例模式可以在系统设置全局的访问点,优化和共享资源访问,例如可以设计一个单例类,负责所有数据表的映射处理。

 

3缺点:

3.1 单例模式一般没有接口,扩展很困难,若要扩展,除了修改代码基本上没有别的途径。单例模式为什么不能增加接口呢?因为接口对单例模式是没有任何意义的,它要求“自行实例化”,并且提供单一实例。接口、抽象类是不可能被实例化的。当然特殊情况下,单例模式可以实现接口、被继承等,需要在系统开中根据环境判断。

3.2 单例模式对测试不利。在并行开发环境中,如果单例没有完成,是不能进行测试的。

3.3 单例模式与单一职责原则有冲突。一个类应该只实现一个逻辑,而不关心它是否是单例的,是不是要取决于环境,单例模式把“要单例”和业务逻辑融合在一个类中。


4使用场景:

在一个系统中,要求一个类有且仅有一个对象,如果出现多个对象就会出现“不良反应”,可以采用单例模式,具体场景如下:

  • 要求生成唯一序列号的环境;
  • 在整个项目中需要一个共享访问点或共享数据,如计数器;
  • 创建一个对象需要消耗资源过多,如要访问IO和数据库等资源;
  • 需要定义大量的静态常量和静态方法(如工具类)的环境,可以采用单例模式。

 

5注意事项:

5.1 高并发时注意单例模式的线程同步问题。

         在单例的实例化时,有“饿汉式”如1.3中实例化方法;也有使用同步方法,仅在需要时创建的方式,这种方式称为“懒汉式”单例;后者不是优秀的方式,前者为推荐方式。

         若后者未添加同步关键字,则可能导致“创建 赋值”过程中,被另一调用再次创建。

5.2 考虑对象复制情况。

         在Java中,对象默认是不可以被复制的,若实现了Cloneable接口,并实现了clone方法,则可以直接通过对象复制方式创建一个新对象,对象复制不调用类的构造函数,因此即使是私有的构造函数,对象仍然可以被复制。即,单例类不要实现Cloneable接口。

  

6扩展:

         如果一个类只要一个对象,使用单例模式即可;如果一个类可以只要两个、三个对象呢?亦可参照单例模式,静态初始若干实例,然后使用时或随机或按某种规则取出即可。

  

7范例:

 单例用法(计数器类,部分工具类)

 

package _01_Singleton;public class Counter {private static final Counter counter = new Counter();//创建实例 private static int total = 0;private Counter(){}public static Counter getInstance(){//获取实例return counter;}//做点事。public synchronized void Increase(){total++;}//做点事。public synchronized int getTotal(){return total;}//工具类可参考public static void whatAmI(){System.out.println("我是计数器工具方法");}}
测试:

 

package _01_Singleton;public class TestCounter {public static void main(String[] args) {Counter.whatAmI();Counter c = Counter.getInstance();for(int i=0; i<10; i++){c.Increase();System.out.println("The current total is : " + c.getTotal());}}}
结果:

我是计数器工具方法
The current total is : 1
The current total is : 2
The current total is : 3
The current total is : 4
The current total is : 5
The current total is : 6
The current total is : 7
The current total is : 8
The current total is : 9
The current total is : 10

 

注意:

使用单例模式需要注意的一点就是JVM的垃圾回收机制,如果我们的一个单例对象在内存中长久不使用,JVM就认为这个对象是一个垃圾,在CPU资源空闲的情况下,该对象会被清理掉,下次再调用时就需要重新产生一个对象。如果我们在应用中使用单例类作为有状态值(如计数器)的管理,则会出现恢复原状的情况,应用就会出现故障。如果确实需要采用单例模式来记录有状态的值,有两种方法可以解决该问题:(下述为转述,未尝试

由容器管理单例的生命周期

JavaEE容器或者框架级容器(如Spring)可以让对象长久驻留内存。当然,自行通过管理对象的生命期也是一个可行的办法,既然有那么多的工具提供我们,为什么不用呢?

状态随时记录

可以使用异步记录的方式,或者使用观察者模式,记录状态的变化,写入文件或写入数据库中,确保即使单例对象重新初始化也可以从资源环境获得销毁前的数据,避免应用数据丢失。


原创粉丝点击