[java设计模式]之单例模式

-------------------此部分比較深入地解说了单例模式,原文链接已给出。兴许将涉及一些常见面试问题---------------------------

原文地址:http://www.cnblogs.com/coffee/archive/2011/12/05/inside-java-singleton.html

关于单例模式的文章,事实上网上早就已经泛滥了。

但一个小小的单例。里面却是有着很多的变化。

网上的文章大多也是提到了当中的一个或几个点。非常少有比較全面且脉络清晰的文章,于是,我便萌生了写这篇文章的念头。

企图把这个单例说透。说深入。但愿我不会做的太差。

  首先来看一下简单的实现

/**
 * 基础的单例模式。Lazy模式,非线程安全
 * 长处:lazy,初次使用时实例化单例,避免资源浪费
 * 缺点:1、lazy。假设实例初始化很耗时。初始使用时,可能造成性能问题
 * 2、非线程安全。多线程下可能会有多个实例被初始化。
 * 
 * @author laichendong
 * @since 2011-12-5
 */
public class SingletonOne {
    
    /** 单例实例变量 */
    private static SingletonOne instance = null;
    
    /**
     * 私有化的构造方法。保证外部的类不能通过构造器来实例化。
17*/
    private SingletonOne() {
        
    }
    
    /**
     * 获取单例对象实例
     * 
     * @return 单例对象
26*/
    public static SingletonOne getInstance() {
        if (instance == null) { // 1
            instance = new SingletonOne(); // 2
        }
        return instance;
    }
    
}

凝视中已经有简单的分析了。接下来分析一下关于“非线程安全”的部分。

  1、当线程A进入到第28行(#1)时。检查instance是否为空。此时是空的。


  2、此时,线程B也进入到28行(#1)。切换到线程B运行。相同检查instance为空。于是往下运行29行(#2),创建了一个实例。接着返回了。


  3、在切换回线程A,因为之前检查到instance为空。所以也会运行29行(#2)创建实例。返回。


  4、至此。已经有两个实例被创建了。这不是我们所希望的。 

 怎么解决线程安全问题?

  方法一:同步方法。

即在getInstance()方法上加上synchronizedkeyword。这时单例变成了

使用同步方法的单例
/**
 * copyright © sf-express Inc
 */
package com.something.singleton;

/**
 * 同步方法 的单例模式,Lazy模式,线程安全
 * 长处:
 * 1、lazy,初次使用时实例化单例,避免资源浪费
 * 2、线程安全
 * 缺点:
 * 1、lazy,假设实例初始化很耗时,初始使用时,可能造成性能问题
 * 2、每次调用getInstance()都要获得同步锁,性能消耗。
 * 
 * @author laichendong
 * @since 2011-12-5
 */
public class SingletonTwo {
    
    /** 单例实例变量 */
    private static SingletonTwo instance = null;
    
    /**
     * 私有化的构造方法,保证外部的类不能通过构造器来实例化。
*/
    private SingletonTwo() {
        
    }
    
    /**
     * 获取单例对象实例
     * 同步方法。实现线程相互排斥訪问,保证线程安全。

* * @return 单例对象 */ public static synchronized SingletonTwo getInstance() { if (instance == null) { // 1 instance = new SingletonTwo(); // 2 } return instance; } }

加上synchronized后确实实现了线程的相互排斥訪问getInstance()方法。从而保证了线程安全。可是这样就完美了么?我们看。事实上在典型实现里,会导致问题的仅仅是当instance还没有被实例化的时候。多个线程訪问#1的代码才会导致问题。而当instance已经实例化完毕后。每次调用getInstance(),事实上都是直接返回的。即使是多个线程訪问。也不会出问题。但给方法加上synchronized后。全部getInstance()的调用都要同步了。事实上我们仅仅是在第一次调用的时候要同步。而同步须要消耗性能。这就是问题。

  方法二:双重检查加锁Double-checked locking。


  事实上经过分析发现,我们仅仅要保证 instance = new SingletonOne(); 是线程相互排斥訪问的就能够保证线程安全了。那把同步方法加以改造,仅仅用synchronized块包裹这一句。就得到了以下的代码:

public static SingletonThree getInstance() {
        if (instance == null) { // 1
            synchronized (SingletonThree.class) {
                instance = new SingletonThree(); // 2
            }
        }
        return instance;
    }

这种方法可行么?分析一下发现是不行的!


  1、线程A和线程B同一时候进入//1的位置。这时instance是为空的。
  2、线程A进入synchronized块。创建实例,线程B等待。
  3、线程A返回,线程B继续进入synchronized块。创建实例。


  4、这时已经有两个实例创建了。

  为了解决问题。我们须要在//2的之前。再加上一次检查instance是否被实例化。

双重检查加锁)接下来,代码变成了这样:

public static SingletonThree getInstance() {
        if (instance == null) { // 1
            synchronized (SingletonThree.class) {
                if (instance == null) { 
                    instance = new SingletonThree(); // 2
                }
            }
        }
        return instance;
    }

这样,当线程A返回。线程B进入synchronized块后。会先检查一下instance实例是否被创建,这时实例已经被线程A创建过了。所以线程B不会再创建实例。而是直接返回。

貌似!

到此为止,这个问题已经被我们完美的攻克了。遗憾的是。事实全然不是这样!

这种方法在单核和 多核的cpu下都不能保证非常好的工作。

导致这种方法失败的原因是当前java平台的内存模型。

java平台内存模型中有一个叫“无序写”(out-of-order writes)的机制。正是这个机制导致了双重检查加锁方法的失效。这个问题的关键在上面代码上的第5行:instance = new SingletonThree(); 这行事实上做了两个事情:1、调用构造方法。创建了一个实例。2、把这个实例赋值给instance这个实例变量。

可问题就是,这两步jvm是不保证顺序的。

也就是说。可能在调用构造方法之前,instance已经被设置为非空了。

以下我们看一下出问题的过程:
  1、线程A进入getInstance()方法。


  2、由于此时instance为空,所以线程A进入synchronized块。
  3、线程A运行 instance = new SingletonThree(); 把实例变量instance设置成了非空。

(注意。是在调用构造方法之前。)
  4、线程A退出,线程B进入。
  5、线程B检查instance是否为空,此时不为空(第三步的时候被线程A设置成了非空)。线程B返回instance的引用。(问题出现了,这时instance的引用并非SingletonThree的实例。由于没有调用构造方法。

) 
  6、线程B退出,线程A进入。
  7、线程A继续调用构造方法,完毕instance的初始化。再返回。 

  好吧,继续努力,解决由“无序写”带来的问题。

public static SingletonThree getInstance() {
        if (instance == null) { 
            synchronized (SingletonThree.class) {           // 1
                SingletonThree temp = instance;             // 2
                if (temp == null) {
                    synchronized (SingletonThree.class) {   // 3
                        temp = new SingletonThree();        // 4
                    }
                    instance = temp;                        // 5
                }
            }
        }
        return instance;
    }

解释一下运行步骤。


  1、线程A进入getInstance()方法。
  2、由于instance是空的 ,所以线程A进入位置//1的第一个synchronized块。
  3、线程A运行位置//2的代码,把instance赋值给本地变量temp。instance为空,所以temp也为空。 
  4、由于temp为空。所以线程A进入位置//3的第二个synchronized块。
  5、线程A运行位置//4的代码,把temp设置成非空。但还没有调用构造方法!(“无序写”问题) 
  6、线程A堵塞,线程B进入getInstance()方法。


  7、由于instance为空,所以线程B试图进入第一个synchronized块。但由于线程A已经在里面了。

所以无法进入。线程B堵塞。
  8、线程A激活,继续运行位置//4的代码。调用构造方法。生成实例。
  9、将temp的实例引用赋值给instance。

退出两个synchronized块。

返回实例。
  10、线程B激活,进入第一个synchronized块。
  11、线程B运行位置//2的代码,把instance实例赋值给temp本地变量。


  12、线程B推断本地变量temp不为空,所以跳过if块。

返回instance实例。

  好吧,问题最终攻克了,线程安全了。可是我们的代码由最初的3行代码变成了如今的一大坨~。

于是又有了以下的方法。

  方法三:预先初始化static变量。

/**
 * 预先初始化static变量 的单例模式  非Lazy  线程安全
 * 长处:
 * 1、线程安全 
 * 缺点:
 * 1、非懒载入,假设构造的单例非常大,构造完又迟迟不使用。会导致资源浪费。
 * 
 * @author laichendong
 * @since 2011-12-5
 */
public class SingletonFour {
    
    /** 单例变量 ,static的,在类载入时进行初始化一次,保证线程安全 */
    private static SingletonFour instance = new SingletonFour();
    
    /**
     * 私有化的构造方法。保证外部的类不能通过构造器来实例化。
     */
    private SingletonFour() {
        
    }
    
    /**
     * 获取单例对象实例
     * 
     * @return 单例对象
     */
    public static SingletonFour getInstance() {
        return instance;
    }
    
}

看到这种方法,世界又变得清净了。因为java的机制。static的成员变量仅仅在类载入的时候初始化一次,且类载入是线程安全的。

所以这种方法实现的单例是线程安全的。可是这种方法却牺牲了Lazy的特性。单例类载入的时候就实例化了。如凝视所述:非懒载入,假设构造的单例非常大。构造完又迟迟不使用,会导致资源浪费。

  那究竟有没有完美的办法?懒载入,线程安全,代码简单。

  方法四:使用内部类。

/**
 * 基于内部类的单例模式  Lazy  线程安全
 * 长处:
 * 1、线程安全
 * 2、lazy
 * 缺点:
 * 1、待发现
 * 
 * @author laichendong
 * @since 2011-12-5
 */
public class SingletonFive {
    
    /**
     * 内部类,用于实现lzay机制
*/
    private static class SingletonHolder{
        /** 单例变量  */
        private static SingletonFive instance = new SingletonFive();
    }
    
    /**
     * 私有化的构造方法。保证外部的类不能通过构造器来实例化。
*/
    private SingletonFive() {
        
    }
    
    /**
     * 获取单例对象实例
     * 
     * @return 单例对象
*/
    public static SingletonFive getInstance() {
        return SingletonHolder.instance;
    }
    
}

解释一下。由于java机制规定。内部类SingletonHolder仅仅有在getInstance()方法第一次调用的时候才会被载入(实现了lazy),并且其载入过程是线程安全的(实现线程安全)。内部类载入的时候实例化一次instance。

 

  最后,总结一下:
  1、假设单例对象不大,同意非懒载入,能够用法三。
  2、假设须要懒载入,且同意一部分性能损耗,能够用法一。(官方说眼下高版本号的synchronized已经比較快了)
  3、假设须要懒载入,且不怕麻烦。能够用法二。
  4、假设须要懒载入。没有且。推荐用法四。 

-------------------------------------------------------单例模式的几种写法------------------------------------------------------------------

原文地址:http://www.jfox.info/java-dan-li-mo-shi-de-ji-zhong-xie-fa

第一种(懒汉,线程不安全):
 1 public class Singleton {  
 2     private static Singleton instance;  
 3     private Singleton (){}   
 4     public static Singleton getInstance() {  
 5     if (instance == null) {  
 6         instance = new Singleton();  
 7     }  
 8     return instance;  
 9     }  
10 }  
11

这样的写法lazy loading非常明显,可是致命的是在多线程不能正常工作。

另外一种(懒汉,线程安全):

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

这样的写法可以在多线程中非常好的工作,并且看起来它也具备非常好的lazy loading。可是。遗憾的是,效率非常低。99%情况下不须要同步。

第三种(饿汉):

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

这样的方式基于classloder机制避免了多线程的同步问题,只是,instance在类装载时就实例化,尽管导致类装载的原因有非常多种。在单例模式中大多数都是调用getInstance方法。 可是也不能确定有其它的方式(或者其它的静态方法)导致类装载。这时候初始化instance显然没有达到lazy loading的效果。

第四种(饿汉。变种):

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

表面上看起来区别挺大,事实上更第三种方式几乎相同,都是在类初始化即实例化instance。

第五种(静态内部类):
 1 public class Singleton {  
 2     private static class SingletonHolder {  
 3     private static final Singleton INSTANCE = new Singleton();  
 4     }  
 5     private Singleton (){}
 6     public static final Singleton getInstance() {  
 7         return SingletonHolder.INSTANCE;  
 8     }  
 9 }  
10

这样的方式相同利用了classloder的机制来保证初始化instance时仅仅有一个线程,它跟第三种和第四种方式不同的是(非常细微的区别):第三种和第四种方式是仅仅要Singleton类被装载了,那么instance就会被实例化(没有达到lazy loading效果),而这样的方式是Singleton类被装载了,instance不一定被初始化。

由于SingletonHolder类没有被主动使用,仅仅有显示通过调用getInstance方法时。才会显示装载SingletonHolder类。从而实例化instance。

想象一下,假设实例化instance非常消耗资源,我想让他延迟载入,另外一方面,我不希望在Singleton类载入时就实例化,由于我不能确保Singleton类还可能在其它的地方被主动使用从而被载入,那么这个时候实例化instance显然是不合适的。这个时候。这样的方式相比第三和第四种方式就显得非常合理。

第六种(枚举):
1 public enum Singleton {  
2     INSTANCE;  
3     public void whateverMethod() {  
4     }  
5 }  
6

这样的方式是Effective Java作者Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,并且还能防止反序列化又一次创建新的对象,可谓是非常坚强的壁垒啊,只是。个人觉得因为1.5中才增加enum特性,用这样的方式写不免让人感觉生疏。在实际工作中,我也非常少看见有人这么写过。
 

第七种(双重校验锁):

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

这个是另外一种方式的升级版,俗称双重检查锁定。具体介绍请查看:http://www.ibm.com/developerworks/cn/java/j-dcl.html

在JDK1.5之后,双重检查锁定才可以正常达到单例效果。

总结

有两个问题须要注意:

     1、假设单例由不同的类装载器装入。那便有可能存在多个单例类的实例。

假定不是远端存取,比如一些servlet容器对每一个servlet使用全然不同的类  装载器,这种话假设有两个servlet訪问一个单例类,它们就都会有各自的实例。

     2、假设Singleton实现了java.io.Serializable接口,那么这个类的实例就可能被序列化和复原。无论如何,假设你序列化一个单例类的对象。接下来复原多个那个对象,那你就会有多个单例类的实例。

对第一个问题修复的办法是:

 1 private static Class getClass(String classname)      
 2                                          throws ClassNotFoundException {     
 3       ClassLoader classLoader = Thread.currentThread().getContextClassLoader();     
 4       
 5       if(classLoader == null)     
 6          classLoader = Singleton.class.getClassLoader();     
 7       
 8       return (classLoader.loadClass(classname));     
 9    }     
10 }  
11

 对第二个问题修复的办法是:

 1 public class Singleton implements java.io.Serializable {     
 2    public static Singleton INSTANCE = new Singleton();     
 3       
 4    protected Singleton() {     
 5         
 6    }     
 7    private Object readResolve() {     
 8             return INSTANCE;     
 9       }    
10 }   
11

对我来说。我比較喜欢第三种和第五种方式,简单易懂。并且在JVM层实现了线程安全(假设不是多个类载入器环境)。一般的情况下。我会使用第三种方式。仅仅有在要明白实现lazy loading效果时才会使用第五种方式。另外,假设涉及到反序列化创建对象时我会试着使用枚举的方式来实现单例。只是,我一直会保证我的程序是线程安全的,并且我永远不会使用第一种和另外一种方式。假设有其它特殊的需求,我可能会使用第七种方式。毕竟,JDK1.5已经没有双重检查锁定的问题了。


----------------------------------------------------------------------------面试问题---------------------------------------------------------------------------------

原文地址:http://blog.csdn.net/baimy1985/article/details/8553788

设计模式相关知识在面试中常常被问到。当中的单例模式差点儿是每次必问。同一时候还会要求手写单例模式的代码。至于为什么也不难理解,它的实现代码简短,用较短的时间就能完毕。同一时候代码中也不乏一些细节能够考察面试者的基本功。简单啰嗦一下单例模式的基本知识,借用下网络搜索的结果:
      概念上可理解成一个类仅仅有一个实例,实现上是要注意下面三点:
  1.       单例模式的类仅仅提供私有的构造函数。
  2.       类定义中含有一个该类的静态私有对象。
  3.       该类提供了一个静态公有函数用于创建或获取它的静态私有对象
     理解好上面这几句话。即使面试的时候一时忘代码怎么写,也能够依据原理又一次写出来的,理解原理要比记住代码更重要。用自己的话去描写叙述给面试官听相信效果也是不错的,当然作为最后一关手写代码也非常关键,在面试略有压力的情况。敢不敢保证写下的代码没有编译错误,真机測试一次通过。假设能够,那恭喜你面试临时上岸,面试官要换题了。以下还是看两段代码吧。


[java] view plaincopy
  1. //懒汉式  
  2. public class SingletonA {  
  3.      public static SingletonA instance = null;  
  4.       
  5.      private SingletonA(){     }  
  6.       
  7.      public static SingletonA getSingletonA(){           
  8.           if(instance == null){  
  9.                instance = new SingletonA();  
  10.           }  
  11.           return instance;  
  12.      }      
  13. }  
  14.   
  15. //饿汉式  
  16. public class SingletonB {  
  17.      private static SingletonB instanceB = new SingletonB();  
  18.       
  19.      private SingletonB(){}  
  20.       
  21.      public static synchronized SingletonB getInstance(){  
  22.           return instanceB;  
  23.      }  
  24. }  
  25.   
  26. //双重锁定  
  27. public class SingletonC {  
  28.      private static SingletonC instance = null;  
  29.      private SingletonC(){};  
  30.       
  31.      public static SingletonC getInstance(){  
  32.           if(instance == null){  
  33.                synchronized (SingletonC.class) {  
  34.                     if(null == instance){  
  35.                          instance = new SingletonC();  
  36.                     }  
  37.                }  
  38.           }  
  39.           return instance;  
  40.      }  
  41. }  
       写了这么多。感觉是不是有点孔乙已了。好吧。事实上面试官仅仅是想知道以下几点
  1.        面试者是否了解单例。
  2.        是否知道懒汉式和饿汉式的差别,
  3.        是不是还知道有双重锁定这么一回事,
  4.        是否会注意到饿汉式须要同步操作才好。
      实际的面试过程中一般不会让你三种都写出来,前两种比較常考察,同一时候有经验的面试官还会在写代码过程留意你的表现,借一斑以窥全豹,顺便看看有没有能够引出接下来问题的考察点。

只是最重要还是对概念理解程度的考察,至于代码中的一些小错误,比方命名是否规范这样的类似错误是不会过多纠缠的。

       就写这些,虽说一个挺简单的面试题。只是能够问的东西还是有一些的,文中提到一些个人认为应该注意的地方不知道是否还有遗漏呢,假设有留言告诉我,很感谢。


-----------------------------------------------------------------面试问题集锦------------------------------------------------------------------------

1) 哪些类是单例模式的兴许类?在Java中哪些类会成为单例?

这里它们将检查面试者是否有对使用单例模式有足够的使用经验。他是否熟悉单例模式的长处和缺点。

2)你能在Java中编写单例里的getInstance()的代码?

非常多面试者都在这里失败。然而假设不能编写出这个代码,那么兴许的非常多问题都不能被提及。

3)在getInstance()方法上同步有优势还是仅同步必要的块更优优势?你更喜欢哪个方式?

这确实是一个很好的问题。我差点儿每次都会提该问题。用于检查面试者是否会考虑由于锁定带来的性能开销。由于锁定只在创建实例时才有意义,然后其它时候实例不过只读訪问的。因此只同步必要的块的性能更优,而且是更好的选择。

4)什么是单例模式的延迟载入或早期载入?你怎样实现它?

这是和Java中类载入的载入和性能开销的理解的又一个很好的问题。我面试过的大部分面试者对此并不熟悉,可是最好理解这个概念。

5) Java平台中的单例模式的实例有哪些?

这是个全然开放的问题,假设你了解JDK中的单例类。请共享给我。

6) 单例模式的两次检查锁是什么?

7)你怎样阻止使用clone()方法创建单例实例的还有一个实例?

该类型问题有时候会通过怎样破坏单例或什么时候Java中的单例模式不是单例来被问及。

8)假设阻止通过使用反射来创建单例类的还有一个实例?

开放的问题。在我的理解中,从构造方法中抛出异常可能是一个选项。

9)假设阻止通过使用序列化来创建单例类的还有一个实例?

又一个很好的问题。这须要Java中的序列化知识并须要理解怎样使用它来序列化单例类。该问题是开放问题。

10) Java中的单例模式什么时候是非单例?

以下是英文:

Singleton pattern is one of the most common patterns available and it’s also used heavily in Java. This is also one of my favorite interview question and has lots of interesting follow-up to digg into details , this not only check the knowledge of design pattern but also check coding , multithreading aspect which is very important while working for a real life application. 

In this short java interview tutorial I have listed some of the most common question asked on Singleton pattern during a Java Interview. I have not provided the answers of these questions as they are easily available via google search but if you guys need I can try to modify this tutorial to include answers as well. 

So question starts with what is Singleton? Have you used Singleton before? 
Singleton is a class which has only one instance thought out the application and provides a getInstance() method to access the singleton instance. 

1) Which classes are candidates of Singleton?

Which kind of class do you make Singleton in Java? 
Here they will check whether candidate has enough experience on usage of singleton or not. Does he is familiar of advantage/disadvantage or alternatives available for singleton in Java or not. 

2) Can you write code for getInstance() method of a Singleton class in Java? 
Most of the guys fail here if they have mugged up the singleton code because you can ask lots of follow-up question based upon the code written by candidate. In my experience I have seen mostly java interviewee right Singleton getInstance() method with double checking but they are not really familiar with the caveat associated with double checking of singleton prior to Java 5. 

3) Is it better to make whole getInstance() method synchronized or just critical section is enough?

Which one you will prefer? 
This is really nice question and I mostly asked to just quickly check whether candidate is aware of performance trade off of unnecessary locking or not. Since locking only make sense when we need to create instance and rest of the time its just read only access so locking of critical section is always better option. 

4) What is lazy and early loading of Singleton and how will you implement it?

 
This is again great question in terms of understanding of concept of loading and cost associated with class loading in Java. Many of which I have interviewed not really familiar with this but its good to know concept. 

5) Example of Singleton in standard JAVA Development Kit? 
This is open question to all, please share which classes are Singleton in JDK. 

6) What is double checked locking in Singleton?

 
One of the most hyped question on Singleton and really demands complete understanding to get it right because of Java Memory model caveat prior to Java 5. If a guy comes up with a solution of using volatile instance of Singleton then it really shows it has in depth knowledge of Java memory model and he is constantly updating his Java knowledge. 

7) How do you prevent for creating another instance of Singleton using clone() method?

 
This type of questions generally comes some time by asking how to break singleton or when Singleton is not Singleton in Java. 

8) How do you prevent for creating another instance of Singleton using reflection? 
Open to all. In my opinion throwing exception from constructor is an option. 

9) How do you prevent for creating another instance of Singleton during serialization? 
Another great question which requires knowledge of Serialization in Java and how to use it for persisting Singleton classes. This is open to you all but in my opinion use of readResolve() method can sort this out for you. 

10) When is Singleton not a Singleton in Java? 
There is a very good article present in Sun's Java site which discusses various scenarios when a Singleton is not really remains Singleton and multiple instance of Singleton is possible. Here is the link of that article http://java.sun.com/developer/technicalArticles/Programming/singletons/ 

If you like to read more Java interview questions you can have a look on some of my favorites below: 

How to identify and fix deadlock in java 
Difference between HashMap and HashTable?

Can we make hashmap synchronized 
How to check if a thread holds lock on a particular object in Java 

------------------------------------------------------分隔符-------------------------------------------------------------------------

静态类和单例模式差别

原文链接:http://sooxin.iteye.com/blog/796987

观点一:(单例
单例模式比静态方法有非常多优势:
首先。单例能够继承类。实现接口,而静态类不能(能够集成类,但不能集成实例成员);
其次,单例能够被延迟初始化,静态类一般在第一次载入是初始化;
再次,单例类能够被继承。他的方法能够被覆写。
最后。也许最重要的是。单例类能够被用于多态而无需强迫用户仅仅假定唯一的实例。举个样例。你可能在開始时仅仅写一个配置,可是以后你可能须要支持超过一个配置集,或者可能须要同意用户从外部从外部文件里载入一个配置对象,或者编写自己的。你的代码不须要关注全局的状态,因此你的代码会更加灵活。



观点二:(静态方法

静态方法中产生的对象,会随着静态方法运行完成而释放掉。并且运行类中的静态方法时,不会实例化静态方法所在的类。

假设是用singleton, 产生的那一个唯一的实例,会一直在内存中,不会被GC清除的(原因是静态的属性变量不会被GC清除),除非整个JVM退出了。这个问题我之前也想几天。并且自己写代码来做了个实验。

观点三:(Good!
因为DAO的初始化,会比較占系统资源的,假设用静态方法来取,会不断地初始化和释放,所以我个人觉得假设不存在比較复杂的事务管理。用singleton会比較好。个人意见,欢迎各位高手指正。 

  

总结:大家对这个问题都有一个共识:那就是实例化方法很多其它被使用和稳妥,静态方法少使用。

有时候我们对静态方法和实例化方法会有一些误解。

1、大家都以为“ 静态方法常驻内存,实例方法不是,所以静态方法效率高但占内存。

其实,他们都是一样的,在载入时机和占用内存上,静态方法和实例方法是一样的。在类型第一次被使用时载入。调用的速度基本上没有区别。

2、大家都以为“ 静态方法在堆上分配内存,实例方法在堆栈上

其实全部的方法都不可能在堆或者堆栈上分配内存,方法作为代码是被载入到特殊的代码内存区域。这个内存区域是不可写的。

方法占不占用很多其它内存,和它是不是static没什么关系。

   
由于字段是用来存储每一个实例对象的信息的。所以字段会占有内存,而且由于每一个实例对象的状态都不一致(至少不能觉得它们是一致的),所以每一个实例对象的所以字段都会在内存中有一分拷贝。也由于这样你才干用它们来区分你如今操作的是哪个对象。

   
但方法不一样,不论有多少个实例对象,它的方法的代码都是一样的,所以仅仅要有一份代码就够了。因此不管是static还是non-static的方法,都仅仅存在一份代码,也就是仅仅占用一份内存空间。

   
相同的代码。为什么执行起来表现却不一样?这就依赖于方法所用的数据了。主要有两种数据来源,一种就是通过方法的參数传进来,还有一种就是使用class的成员变量的值……

3、大家都以为“实例方法须要先创建实例才干够调用,比較麻烦。静态方法不用,比較简单

其实假设一个方法与他所在类的实例对象无关。那么它就应该是静态的,而不应该把它写成实例方法。所以全部的实例方法都与实例有关,既然与实例有关,那么创建实例就是必定的步骤。没有麻烦简单一说。

当然你全然能够把全部的实例方法都写成静态的。将实例作为參数传入就可以,普通情况下可能不会出什么问题。

从面向对象的角度上来说。在抉择使用实例化方法或静态方法时,应该依据是否该方法和实例化对象具有逻辑上的相关性。假设是就应该使用实例化对象 反之使用静态方法。这仅仅是从面向对象角度上来说的。

假设从线程安全、性能、兼容性上来看,也是选用实例化方法为宜。

我们为什么要把方法区分为:静态方法和实例化方法 ?

假设我们继续深入研究的话。就要脱离技术谈理论了。早期的结构化编程。差点儿全部的方法都是“静态方法”,引入实例化方法概念是面向对象概念出现以后的事情了,区分静态方法和实例化方法不能单单从性能上去理解,创建c++,java,c#这样面向对象语言的大师引入实例化方法一定不是要解决什么性能、内存的问题,而是为了让开发更加模式化、面向对象化。这样说的话,静态方法和实例化方式的区分是为了解决模式的问题。

拿别人一个样例说事:

比方说“人”这个类,每一个人都有姓名、年龄、性别、身高等,这些属性就应该是非静态的。由于每一个人都的这些属性都不同样;但人在生物学上属于哪个门哪个纲哪个目等,这个属性是属于整个人类,所以就应该是静态的——它不依赖与某个特定的人。不会有某个人是“脊椎动物门哺乳动物纲灵长目”而某个人却是“偶蹄目”的。

----------------------------------------------单例模式面试题&类载入机制------------------------------------------------

class SingleTon {
	private static SingleTon singleTon = new SingleTon();
	public static int count1;
	public static int count2 = 0;


	private SingleTon() {
		count1++;
		count2++;
	}


	public static SingleTon getInstance() {
		return singleTon;
	}
}

public class Test {
	public static void main(String[] args) {
		SingleTon singleTon = SingleTon.getInstance();
		System.out.println("count1=" + singleTon.count1);
		System.out.println("count2=" + singleTon.count2);
	}
}

错误答案
count1=1
count2=1
 正确答案
count1=1
count2=0

分析:
1:SingleTon singleTon = SingleTon.getInstance();调用了类的SingleTon调用了类的静态方法,触发类的初始化
2:类载入的时候在准备过程中为类的静态变量分配内存并初始化默认值 singleton=null count1=0,count2=0
3:类初始化化,为类的静态变量赋值和运行静态代码快。singleton赋值为new SingleTon()调用类的构造方法
4:调用类的构造方法后count1=1;count2=1
5:继续为count1与count2赋值,此时count1没有赋值操作,全部count1为1,可是count2运行赋值操作就变为0 

原文地址:https://www.cnblogs.com/wzjhoutai/p/7116364.html