模式设计 - 单例模式

一个类能返回对象的一个引用(永远是同一个)和一个获得该实例的方法(必须是静态方法,通常使用getInstance这个名 称)。

使用需要注意:

使用注意事项: 
    1.使用时不能用反射模式创建单例,否则会实例化一个新的对象 
    2.使用懒单例模式时注意线程安全问题。在多线程环境下要注意,如果当唯一实例尚未创建时,有两个线程同时调用创建方法,那么它们同时没有检测到唯一实例的存在,从而同时各自创建了一个实例, 这样就有两个实例被构造出来,从而违反了单例模式中实例唯一的原则。 解决这个问题的办法是为指示类是否已经实例化的变量提供一个互斥锁(虽然这样会降低效率)。
    3.饿单例模式和懒单例模式构造方法都是私有的,因而是不能被继承的,有些单例模式可以被继承(如登记式模式)

优缺点:

优点: 
    1.在单例模式中,活动的单例只有一个实例,对单例类的所有实例化得到的都是相同的一个实例。这样就 防止其它对象对自己的实例化,确保所有的对象都访问一个实例 
    2.单例模式具有一定的伸缩性,类自己来控制实例化进程,类就在改变实例化进程上有相应的伸缩性。 
    3.提供了对唯一实例的受控访问。 
    4.由于在系统内存中只存在一个对象,因此可以 节约系统资源,当 需要频繁创建和销毁的对象时单例模式无疑可以提高系统的性能。 
    5.允许可变数目的实例。 
    6.避免对共享资源的多重占用。 
缺点: 
    1.不适用于变化的对象,如果同一类型的对象总是要在不同的用例场景发生变化,单例就会引起数据的错误,不能保存彼此的状态。 
    2.由于单利模式中没有抽象层,因此单例类的扩展有很大的困难。 
    3.单例类的职责过重,在一定程度上违背了“单一职责原则”。 
    4.滥用单例将带来一些负面问题,如为了节省资源将数据库连接池对象设计为的单例类,可能会导致共享连接池对象的程序过多而出现连接池溢出;如果实例化的对象长时间不被利用,系统会认为是垃圾而被回收,这将导致对象状态的丢失。

适用场景: 
    单例模式只允许创建一个对象,因此节省内存,加快对象访问速度,因此对象需要被公用的场合适合使用,如多个模块使用同一个数据源连接对象等等。如: 
    1.需要频繁实例化然后销毁的对象。 
    2.创建对象时耗时过多或者耗资源过多,但又经常用到的对象。 
    3.有状态的工具类对象。 
    4.频繁访问数据库或文件的对象。 
以下都是单例模式的经典使用场景: 
    1.资源共享的情况下,避免由于资源操作时导致的性能或损耗等。如上述中的日志文件,应用配置。 
    2.控制资源的情况下,方便资源之间的互相通信。如线程池等。 
应用场景举例: 
    1.外部资源:每台计算机有若干个打印机,但只能有一个PrinterSpooler,以避免两个打印作业同时输出到打印机。内部资源:大多数软件都有一个(或多个)属性文件存放系统配置,这样的系统应该有一个对象管理这些属性文件 
    2. Windows的Task Manager(任务管理器)就是很典型的单例模式(这个很熟悉吧),想想看,是不是呢,你能打开两个windows task manager吗? 不信你自己试试看哦~ 
    3. windows的Recycle Bin(回收站)也是典型的单例应用。在整个系统运行过程中,回收站一直维护着仅有的一个实例。 
    4. 网站的计数器,一般也是采用单例模式实现,否则难以同步。 
    5. 应用程序的日志应用,一般都何用单例模式实现,这一般是由于共享的日志文件一直处于打开状态,因为只能有一个实例去操作,否则内容不好追加。 
    6. Web应用的配置对象的读取,一般也应用单例模式,这个是由于配置文件是共享的资源。 
    7. 数据库连接池的设计一般也是采用单例模式,因为数据库连接是一种数据库资源。数据库软件系统中使用数据库连接池,主要是节省打开或者关闭数据库连接所引起的效率损耗,这种效率上的损耗还是非常昂贵的,因为何用单例模式来维护,就可以大大降低这种损耗。 
    8. 多线程的线程池的设计一般也是采用单例模式,这是由于线程池要方便对池中的线程进行控制。 
    9. 操作系统的文件系统,也是大的单例模式实现的具体例子,一个操作系统只能有一个文件系统。 
    10. HttpApplication 也是单位例的典型应用。熟悉ASP.Net(IIS)的整个请求生命周期的人应该知道HttpApplication也是单例模式,所有的HttpModule都共享一个HttpApplication实例. 
   
实现单利模式的原则和过程: 
    1.单例模式:确保一个类只有一个实例,自行实例化并向系统提供这个实例 
    2.单例模式分类:饿单例模式(类加载时实例化一个对象给自己的引用),懒单例模式(调用取得实例的方法如getInstance时才会实例化对象)(java中饿单例模式性能优于懒单例模式,c++中一般使用懒单例模式) 
    3.单例模式要素: 
        a.私有构造方法 
        b.私有静态引用指向自己实例 
        c.以自己实例为返回值的公有静态方法

 1 饱汉模式:
 2 public class Singleton {
 3     private Singleton(){};
 4     private static Singleton instance = null;
 5     public static Singleton getInstance(){
 6     if(instance==null){
 7         instance = new Singleton();
 8     }return instance;
 9     }
10 }

线程安全单例模式:

 1 public class Singleton {
 2     private Singleton(){};
 3     private static Singleton instance=null;
 4     
 5     public static synchronized Singleton getInstance(){
 6         if(instance==null){
 7             instance=new Singleton();
 8         }
 9         return instance;
10     }
11 }

双重检查锁定示例

 1 public class Singleton {
 2     private Singleton(){};
 3     private static Singleton instance=null;
 4     
 5     public static Singleton getInstance(){
 6         if(instance==null){
 7             synchronized(Singleton.class){
 8                 if(instance==null)
 9                     instance=new Singleton();
10             }
11         }
12         return instance;
13     }
14 }

双重检查锁改进版:

 1 public class Singleton {
 2     private Singleton(){};
 3     private static Singleton instance=null;
 4     
 5     public static Singleton getInstance(){
 6         if(instance==null){
 7             synchronized(Singleton.class){
 8 
 9                 Singleton temp = instance;
10                 if(temp==null){
11 
12                    synchronized(Singleton.class){
13 
14                           temp = new Singleton();
15 
16                     }
17                     instance=temp;
18 
19                }
20             }
21         }
22         return instance;
23     }
24 }

饿汉模式:

1 public class Singleton{
2     private Singleton(){};
3     private static final Singleton instance=new Singleton();
4     
5     public static Singleton getInstance(){
6         return instance;
7     }
8 }

 

 1 public class TestStream {
 2 private String name;
 3 public String getName() {
 4 return name;
 5 }
 6 public void setName(String name) {
 7 this.name = name;
 8 }
 9 // 该类只能有一个实例
10 private TestStream() {
11 } // 私有无参构造方法
12 // 该类必须自行创建
13 // 有2种方式
14 
15 private static TestStream ts1 = null;
16 // 这个类必须自动向整个系统提供这个实例对象
17 public static TestStream getTest() {
18 if (ts1 == null) {
19 ts1 = new TestStream();
20 }
21 return ts1;
22 }
23 public void getInfo() {
24 System.out.println(“output message ” + name);
25 }
26 public static void main(String[] args) {
27 TestStream s = TestStream.getTest();
28 s.setName(“张三”);
29 System.out.println(s.getName());
30 TestStream s1 = TestStream.getTest();
31 s1.setName(“李四”);
32 System.out.println(s1.getName());
33 s.getInfo();
34 s1.getInfo();
35 if (s == s1) {
36 System.out.println(“创建的是同一个实例”);
37 } else if (s != s1) {
38 System.out.println(“创建的不是同一个实例”);
39 } else {
40 System.out.println(“application error”);
41 }
42 }
43 } 

output:
 张三
 李四
output message 李四
output message 李四
创建的是同一个实例
///////////////////////////////////////////
结论:
1. name是private型,不是static型,s和s1对象的name最终输出的都是张孝祥 2,说明改变的name是同一个name;
2. s=s1.

posted @ 2018-01-24 19:12  测试人生-  阅读(216)  评论(0编辑  收藏  举报