java设计模式之单例模式(Singleton pattern)

Beaver_uic 发布于 2016/09/10 19:04
阅读 456
收藏 3

java设计模式之单例模式(Singleton pattern)


单例模式的定义:

Singleton pattern restricts the instantiation of a class and ensures that only one instance of the class exists in the java virtual machine. The singleton class must provide a global access point to get the instance of the class.
       限制一个类的实例在一个jvm实例中确保只有一个,而且必须提供一个全局访问点获得该单例。

为什么会出现单例模式:

  •     减少内存开支。由于单例在内存中相对于一个jvm实例内只有一个实例对象,不会重复的创建和jvm垃圾回收,对于内存减少了空间占用,也利于jvm垃圾回收处理。
  •   减少系统性能开销。当一个对象的产生依赖较多的资源时,比如读取配置或者依赖其他对象的时候,单例在jvm启动的时候预加载资源,然后可以永久驻留内存,当然也减少了jvm的垃圾回收线程的负担。
  • 当然还有很有的优势,现流行的spring框架就是默认支持单例模式(相对应spring容器)。

单例的实现方式:

     实现单例模式的方式有很多不同手段,但以下几点我们会同时考虑:
  • 构造函数必须私有,不能让别人有权限随意实例化
  • 该单例一般在类中有一个私有静态变量
  • 该单例一般提供一个静态公共方法获得该单例(对于外界的该单例的全局访问点)

 

饿汉式(Eager initialization)

   饿汉式单例实现方式是在类加载的时候初始化该单例。这种方式实现单例最简单,但也有个缺点就是即使我们应用中没有使用该类的单例,但类加载的时候也必须初始化。

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. package com.doctor.design_pattern.singleton;  
  2.   
  3. /** 
  4.  * @author sdcuike 
  5.  * 
  6.  *         Created on 2016年7月31日 下午11:36:05 
  7.  *  
  8.  *         饿汉式 单例 
  9.  *         EagerInitialized Singleton 
  10.  */  
  11. public class EagerInitializedSingleton {  
  12.     private static final EagerInitializedSingleton instance = new EagerInitializedSingleton();  
  13.   
  14.     private EagerInitializedSingleton() {  
  15.     }  
  16.   
  17.     public static EagerInitializedSingleton getInstance() {  
  18.         return instance;  
  19.     }  
  20.   
  21.     public void doSomething() {  
  22.         System.out.println("test");  
  23.     }  
  24.   
  25.     public static void main(String[] args) {  
  26.         System.out.println(EagerInitializedSingleton.getInstance());  
  27.         System.out.println(EagerInitializedSingleton.getInstance());  
  28.         EagerInitializedSingleton.getInstance().doSomething();  
  29.         // com.doctor.design_pattern.singleton.EagerInitializedSingleton@2a139a55  
  30.         // com.doctor.design_pattern.singleton.EagerInitializedSingleton@2a139a55  
  31.         // test  
  32.   
  33.         System.out.println(EagerInitializedSingleton.getInstance() == EagerInitializedSingleton.getInstance());  
  34.         // true  
  35.     }  
  36.   
  37. }  

   当单例没有涉及到过多的资源使用的时候,饿汉式单例比较适合。但很多场景下,单例的使用一般是为了使用一些资源比如文件系统、数据库连接等等,这中场景下,一般我们尽量使得该单例必须使用的时候,才会初始化,以避免资源的浪费使用。而且饿汉式单例也没提供异常的处理机制。

Static block initialization

   静态块初始化单例和饿汉式单例相似,差别在于实例的初始化在静态块中,这中方式提供了异常处理。

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. package com.doctor.design_pattern.singleton;  
  2.   
  3. /** 
  4.  * @author sdcuike 
  5.  * 
  6.  *         Created on 2016年8月1日 上午12:30:08 
  7.  */  
  8. public class StaticBlockSingleton {  
  9.   
  10.     private static StaticBlockSingleton instance;  
  11.   
  12.     public static StaticBlockSingleton getInstance() {  
  13.         return instance;  
  14.     }  
  15.   
  16.     static {  
  17.         try {  
  18.             instance = new StaticBlockSingleton();  
  19.         } catch (Exception e) {  
  20.             throw new RuntimeException("Exception occured in creating singleton instance");  
  21.         }  
  22.     }  
  23.   
  24.     public static void main(String[] args) {  
  25.         System.out.println(StaticBlockSingleton.getInstance());  
  26.         System.out.println(StaticBlockSingleton.getInstance());  
  27.   
  28.         System.out.println(StaticBlockSingleton.getInstance() == StaticBlockSingleton.getInstance());  
  29.   
  30.         // com.doctor.design_pattern.singleton.StaticBlockSingleton@2a139a55  
  31.         // com.doctor.design_pattern.singleton.StaticBlockSingleton@2a139a55  
  32.         // true  
  33.   
  34.     }  
  35.   
  36. }  


Lazy Initialization

懒初始化方法承担了单例的创建,并且是获取该单例的入口点。 当我们不需要某个资源的时候,尽量延迟加载或初始化资源,这个时候懒初始化对我们非常有益。懒初始化比饿汉式的好处是,饿汉式初始化失败了,就没机会再初始化 了,而懒初始化还可以给我们至少第二次机会。

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. package com.doctor.design_pattern.singleton;  
  2.   
  3. /** 
  4.  * @author sdcuike 
  5.  * 
  6.  *         Created on 2016年8月1日 上午12:39:40 
  7.  */  
  8. public class LazyInitializedSingleton {  
  9.   
  10.     private static LazyInitializedSingleton instance;  
  11.   
  12.     public static LazyInitializedSingleton getInstance() {  
  13.         if (instance == null) {  
  14.             instance = new LazyInitializedSingleton();  
  15.         }  
  16.   
  17.         return instance;  
  18.     }  
  19.   
  20.     private LazyInitializedSingleton() {  
  21.     }  
  22.   
  23.     public static void main(String[] args) {  
  24.         System.out.println(LazyInitializedSingleton.getInstance() == LazyInitializedSingleton.getInstance());// true  
  25.     }  
  26.   
  27. }  


 上面懒初始化单例的实现在单线程环境下可以很好的工作,但可能面临多线程安全问题。

Thread Safe Singleton

  饿汉式单例存在多线程安全问题,我们可以看一下多线程问题,为了模拟,在饿汉式得到实例的时候加入随机休眠时间:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. /** 
  2.  * @author sdcuike 
  3.  * 
  4.  *         Lazy initialization method to implement Singleton pattern creates the 
  5.  *         instance in the global access method. Here is the sample code for 
  6.  *         creating Singleton class with this approach. Created on 2016年8月1日 
  7.  *         上午12:39:40 
  8.  *  
  9.  * @see http://www.journaldev.com/1377/java-singleton-design-pattern-best-practices-examples 
  10.  *      懒初始化,多线程会导致破坏单例原则 
  11.  */  
  12. public class LazyInitializedSingleton {  
  13.   
  14.     private static LazyInitializedSingleton instance;  
  15.   
  16.     /** 
  17.      * 为了模拟多线程情况下的问题,加入了休眠时间 
  18.      *  
  19.      * @return 
  20.      */  
  21.     public static LazyInitializedSingleton getInstance() {  
  22.         try {  
  23.             Thread.sleep((long) (Math.random() * 100));  
  24.         } catch (InterruptedException e) {  
  25.             e.printStackTrace();  
  26.         }  
  27.         if (instance == null) {  
  28.             try {  
  29.                 Thread.sleep((long) (Math.random() * 100));  
  30.             } catch (InterruptedException e) {  
  31.                 e.printStackTrace();  
  32.             }  
  33.             instance = new LazyInitializedSingleton();  
  34.             try {  
  35.                 Thread.sleep((long) (Math.random() * 100));  
  36.             } catch (InterruptedException e) {  
  37.                 e.printStackTrace();  
  38.             }  
  39.         }  
  40.   
  41.         return instance;  
  42.     }  
  43.   
  44.     private LazyInitializedSingleton() {  
  45.     }  
  46.   
  47.     public static void main(String[] args) {  
  48.         // System.out.println(LazyInitializedSingleton.getInstance() ==  
  49.         // LazyInitializedSingleton.getInstance());// true  
  50.         ExecutorService executorService = Executors.newFixedThreadPool(10);  
  51.   
  52.         for (int i = 0; i < 10; i++) {  
  53.             executorService.submit(() -> {  
  54.                 System.out.println(LazyInitializedSingleton.getInstance());  
  55.             });  
  56.         }  
  57.   
  58.         executorService.shutdown();  
  59.         // com.doctor.design_pattern.singleton.LazyInitializedSingleton@6a1bb74  
  60.         // com.doctor.design_pattern.singleton.LazyInitializedSingleton@6a1bb74  
  61.         // com.doctor.design_pattern.singleton.LazyInitializedSingleton@6a1bb74  
  62.         // com.doctor.design_pattern.singleton.LazyInitializedSingleton@2be16806  
  63.         // com.doctor.design_pattern.singleton.LazyInitializedSingleton@2be16806  
  64.         // com.doctor.design_pattern.singleton.LazyInitializedSingleton@5f93f536  
  65.         // com.doctor.design_pattern.singleton.LazyInitializedSingleton@5f93f536  
  66.         // com.doctor.design_pattern.singleton.LazyInitializedSingleton@5f93f536  
  67.         // com.doctor.design_pattern.singleton.LazyInitializedSingleton@5f93f536  
  68.         // com.doctor.design_pattern.singleton.LazyInitializedSingleton@46d3a9  
  69.         // 得到的实例并不是同一个。  
  70.     }  
  71.   
  72. }  

 执行结果也附在上面了,可以得知,多线程环境下,懒初始化单例实现存在风险,会得到不止一实例,主要问题是方法中修改的是类变量,多线程环境下存在并发访问问题。

  线程安全单例,简单的线程安全我们可以用
synchronized
实现。

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. package com.doctor.design_pattern.singleton;  
  2.   
  3. /** 
  4.  * @author sdcuike 
  5.  * 
  6.  *         Created on 2016年8月1日 上午11:19:36 
  7.  *  
  8.  *         Thread Safe Singleton 
  9.  *  
  10.  */  
  11. public class ThreadSafeSingleton {  
  12.   
  13.     private static ThreadSafeSingleton instance;  
  14.   
  15.     public static synchronized ThreadSafeSingleton getInstance() {  
  16.         if (instance == null) {  
  17.             instance = new ThreadSafeSingleton();  
  18.         }  
  19.         return instance;  
  20.     }  
  21.   
  22.     private ThreadSafeSingleton() {  
  23.   
  24.     }  
  25.   
  26.     public static void main(String[] args) {  
  27.         System.out.println(ThreadSafeSingleton.getInstance() == ThreadSafeSingleton.getInstance());  
  28.         // true  
  29.     }  
  30.   
  31. }  



上面的线程安全锁的粒度是比较大了,锁的粒度越大,并发性就不好,导致性能下降,我们可以用double checked locking规则减少锁的粒度。
   

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. package com.doctor.design_pattern.singleton;  
  2.   
  3. /** 
  4.  * @author sdcuike 
  5.  * 
  6.  *         Created on 2016年8月1日 上午11:37:25 
  7.  *  
  8.  *         double checked locking principle 
  9.  */  
  10. public class DoubleCheckedLockingThreadSafeSingleton {  
  11.   
  12.     private static DoubleCheckedLockingThreadSafeSingleton instance;  
  13.   
  14.     public static DoubleCheckedLockingThreadSafeSingleton getInstance() {  
  15.         if (instance == null) {  
  16.             synchronized (DoubleCheckedLockingThreadSafeSingleton.class) {  
  17.                 if (instance == null) {  
  18.                     instance = new DoubleCheckedLockingThreadSafeSingleton();  
  19.                 }  
  20.             }  
  21.         }  
  22.   
  23.         return instance;  
  24.     }  
  25.   
  26.     private DoubleCheckedLockingThreadSafeSingleton() {  
  27.   
  28.     }  
  29.   
  30.     public static void main(String[] args) {  
  31.   
  32.     }  
  33.   
  34. }  

  double check的目的是为了进步一获得线程安全,第一步check为null的时候可能先后进来两个线程访问,此时单例还没初始化,这个时候开始上锁,如果前一个线程上锁后初始化了这个单例而释放锁,那后边的线程也可以锁定对象,然后初始化,这样就违背了单例原则,所以我们在锁住对象后又进一步check对象是否初始化了。

不过,由于java内存的可见性问题(java内存模型),一个线程初始化了对象,double check的时候,并不一定能时时判断实例是否被另一个线程初始化了,所以上面的代码还是有线程安全问题的,所以我们必须在实例变量声明的时候加上关键字volatile,使得多线程对变量的修改能时时的让其他线程看到这个结果。
  所以,最终的double check单实例代码应该是这样的:
[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. /** 
  2.  * @author sdcuike 
  3.  * 
  4.  *         Created on 2016年8月1日 上午11:37:25 
  5.  *  
  6.  *         double checked locking principle and volatile variable 
  7.  */  
  8. public class DoubleCheckedLockingThreadSafeSingleton {  
  9.   
  10.     private static volatile DoubleCheckedLockingThreadSafeSingleton instance;  
  11.   
  12.     /** 
  13.      * double check的目的是为了进步一获得线程安全,第一步check为null的时候可能先后进来两个线程访问,此时单例还没初始化, 
  14.      * 这个时候开始上锁,如果前一个线程上锁后初始化了这个单例而释放锁,那后边的线程也可以锁定对象,然后初始化, 
  15.      * 这样就违背了单例原则,所以我们在锁住对象后又进一步check对象是否初始化了。 
  16.      * 不过,由于java内存的可见性问题(java内存模型),一个线程初始化了对象,double 
  17.      * check的时候,并不一定能时时判断实例是否被另一个线程初始化了,所以上面的代码还是有线程安全问题的, 
  18.      * 所以我们必须在实例变量声明的时候加上关键字volatile,使得多线程对变量的修改能时时的让其他线程看到这个结果。 
  19.      *  
  20.      * @return 
  21.      */  
  22.     public static DoubleCheckedLockingThreadSafeSingleton getInstance() {  
  23.         if (instance == null) {  
  24.             synchronized (DoubleCheckedLockingThreadSafeSingleton.class) {  
  25.                 if (instance == null) {  
  26.                     instance = new DoubleCheckedLockingThreadSafeSingleton();  
  27.                 }  
  28.             }  
  29.         }  
  30.   
  31.         return instance;  
  32.     }  
  33.   
  34.     private DoubleCheckedLockingThreadSafeSingleton() {  
  35.   
  36.     }  
  37.   
  38.     public static void main(String[] args) {  
  39.   
  40.     }  
  41.   
  42. }  



Inner static helper class Singleton

 采用内部类来实现资源的懒加载:
[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. package com.doctor.design_pattern.singleton;  
  2.   
  3. /** 
  4.  * @author sdcuike 
  5.  * 
  6.  *         Inner static helper class Singleton 
  7.  *  
  8.  *  
  9.  *         Created on 2016年8月1日 上午11:54:05 
  10.  */  
  11. public class InnerStaticHelperClassSingleton {  
  12.     private InnerStaticHelperClassSingleton() {  
  13.   
  14.     }  
  15.   
  16.     public static InnerStaticHelperClassSingleton getInstance() {  
  17.         return SingletonHelper.instance;  
  18.     }  
  19.   
  20.     private static class SingletonHelper {  
  21.   
  22.         private static final InnerStaticHelperClassSingleton instance = new InnerStaticHelperClassSingleton();  
  23.     }  
  24.   
  25.     public static void main(String[] args) {  
  26.   
  27.     }  
  28.   
  29. }  

  内部类拥有外部类的实例,当外部类被jvm加载的时候,内部类没有被加载,外部类的实例也就没有被实例化,当外部类真正的调用得到单例入口方法的时候,才会触发内部类的加载,同时外部类的单例也就初始化一次。
 这种方式的好处就是不用锁同步,就可以实现线程安全的单例。

枚举单例实现Enum Singleton

 枚举能够确保enum value只有一次实例化,而且enum value全局可访问,所以也是单例的实现方式之一。但这种实现方式不够灵活,而且不能延迟加载。

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. package com.doctor.design_pattern.singleton;  
  2.   
  3. /** 
  4.  * @author sdcuike 
  5.  * 
  6.  *         Enum Singleton 
  7.  *         枚举实现单例不能延迟加载资源,但保证了enum值只实例化一次。而且克服了反射带来的问题 
  8.  *  
  9.  *         Created on 2016年8月1日 下午12:24:06 
  10.  */  
  11. public enum EnumSingleton {  
  12.     instance;  
  13.     private EnumSingleton() {  
  14.   
  15.     }  
  16.   
  17.     public void doSomething() {  
  18.         System.out.println("test do ");  
  19.     }  
  20.   
  21.     public static void main(String[] args) {  
  22.         EnumSingleton.instance.doSomething();  
  23.     }  
  24.   
  25. }  

序列化与单例

 我们一般通过网络交互的时候都会用到序列化,序列化打破了单例的规则,反序列化我们得到了另一个实例(请自行验证)。为了保证序列化不影响单例规则,我们一般实现以下方法:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. protected Object readResolve() {  
  2.     return getInstance();  
  3. }  

具体请参考资料:http://docs.oracle.com/javase/1.5.0/docs/guide/serialization/spec/input.html

反射与单例

拿饿汉式单例实现来模拟,我们用反射实例化此单例带来的后果就是,违反了单例的原则,反射能实例化另一个对象,而不是之前的实例对象。


[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. public static void main(String[] args) throws ReflectiveOperationException, SecurityException {  
  2.        System.out.println(EagerInitializedSingleton.getInstance());  
  3.        System.out.println(EagerInitializedSingleton.getInstance());  
  4.        EagerInitializedSingleton.getInstance().doSomething();  
  5.        // com.doctor.design_pattern.singleton.EagerInitializedSingleton@2a139a55  
  6.        // com.doctor.design_pattern.singleton.EagerInitializedSingleton@2a139a55  
  7.        // test  
  8.   
  9.        System.out.println(EagerInitializedSingleton.getInstance() == EagerInitializedSingleton.getInstance());  
  10.        // true  
  11.   
  12.        // 反射破坏  
  13.        Constructor<EagerInitializedSingleton> constructor = EagerInitializedSingleton.class.getDeclaredConstructor();  
  14.        constructor.setAccessible(true);  
  15.        EagerInitializedSingleton newInstance = constructor.newInstance();  
  16.        System.out.println(newInstance);  
  17.    }  

执行结果:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. com.doctor.design_pattern.singleton.EagerInitializedSingleton@15db9742  
  2. com.doctor.design_pattern.singleton.EagerInitializedSingleton@15db9742  
  3. test  
  4. true  
  5. com.doctor.design_pattern.singleton.EagerInitializedSingleton@6d06d69c  

由于没有重写toString方法,我们可以根据后面的hashcode值判断是不是相同的对象。
为了防止反射破坏单例规则,我们对私有构造函数处理:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. /** 
  2.     * 反射导致单例失败:How to fix: Throw Runtime Exception if someone tries to make 
  3.     * instance in case one instance already exists. This code will go into the 
  4.     * private constructor of the Singleton class 
  5.     */  
  6.    private EagerInitializedSingleton() {  
  7.        if (instance != null) {  
  8.            throw new RuntimeException("plean use  getInstance() method");  
  9.        }  
  10.    }  


反射实例化对象会导致失败:
[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. com.doctor.design_pattern.singleton.EagerInitializedSingleton@15db9742  
  2. com.doctor.design_pattern.singleton.EagerInitializedSingleton@15db9742  
  3. test  
  4. true  
  5. Exception in thread "main" java.lang.reflect.InvocationTargetException  
  6.     at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)  
  7.     at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)  
  8.     at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)  
  9.     at java.lang.reflect.Constructor.newInstance(Constructor.java:423)  
  10.     at com.doctor.design_pattern.singleton.EagerInitializedSingleton.main(EagerInitializedSingleton.java:57)  
  11. Caused by: java.lang.RuntimeException: plean use  getInstance() method  
  12.     at com.doctor.design_pattern.singleton.EagerInitializedSingleton.<init>(EagerInitializedSingleton.java:31)  
  13.     ... 5 more  



Cloning 与单例

  实例化对象的一种方式就是通过克隆。当我们克隆单例对象的时候,我们也违反了单例原则,拿饿汉式单例来说:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. public class EagerInitializedSingleton implements Cloneable {  
  2.     private static final EagerInitializedSingleton instance = new EagerInitializedSingleton();  
  3.   
  4.     /** 
  5.      * 反射导致单例失败:How to fix: Throw Runtime Exception if someone tries to make 
  6.      * instance in case one instance already exists. This code will go into the 
  7.      * private constructor of the Singleton class 
  8.      */  
  9.     private EagerInitializedSingleton() {  
  10.         if (instance != null) {  
  11.             throw new RuntimeException("plean use  getInstance() method");  
  12.         }  
  13.     }  
  14.   
  15.     public static EagerInitializedSingleton getInstance() {  
  16.         return instance;  
  17.     }  
  18.   
  19.     public void doSomething() {  
  20.         System.out.println("test");  
  21.     }  
  22.   
  23.     @Override  
  24.     protected Object clone() throws CloneNotSupportedException {  
  25.         return super.clone();  
  26.     }  
  27.   
  28.     public static void main(String[] args) throws ReflectiveOperationException, SecurityException, CloneNotSupportedException {  
  29.         System.out.println(EagerInitializedSingleton.getInstance());  
  30.         System.out.println(EagerInitializedSingleton.getInstance());  
  31.         EagerInitializedSingleton.getInstance().doSomething();  
  32.         // com.doctor.design_pattern.singleton.EagerInitializedSingleton@2a139a55  
  33.         // com.doctor.design_pattern.singleton.EagerInitializedSingleton@2a139a55  
  34.         // test  
  35.   
  36.         System.out.println(EagerInitializedSingleton.getInstance() == EagerInitializedSingleton.getInstance());  
  37.         // true  
  38.   
  39.         // 反射破坏  
  40.         // Constructor<EagerInitializedSingleton> constructor =  
  41.         // EagerInitializedSingleton.class.getDeclaredConstructor();  
  42.         // constructor.setAccessible(true);  
  43.         // EagerInitializedSingleton newInstance = constructor.newInstance();  
  44.         // System.out.println(newInstance);  
  45.   
  46.         // 克隆破坏  
  47.         EagerInitializedSingleton eagerInitializedSingleton = EagerInitializedSingleton.getInstance();  
  48.         EagerInitializedSingleton eagerInitializedSingletonClone = (EagerInitializedSingleton) eagerInitializedSingleton.clone();  
  49.         System.out.println(eagerInitializedSingleton + "==" + eagerInitializedSingletonClone + " is " + (eagerInitializedSingleton == eagerInitializedSingletonClone));  
  50.     }  
  51.   
  52. }  


执行结果:
[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. 克隆破坏:  
  2. com.doctor.design_pattern.singleton.EagerInitializedSingleton@15db9742==com.doctor.design_pattern.singleton.EagerInitializedSingleton@6d06d69c is false  

为了防止克隆破坏,和反射一样,抛出异常:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. @Override  
  2.    protected Object clone() throws CloneNotSupportedException {  
  3.        if (instance != null) {  
  4.            throw new CloneNotSupportedException("plean use  getInstance() method");  
  5.        }  
  6.        return super.clone();  
  7.    }  


代码:


参考:

http://www.journaldev.com/1377/java-singleton-design-pattern-best-practices-examples

JAVA  DESIGN  PATTERNS  By  Devendra Singh @Copyright 2016 Dev S



原文链接地址:http://blog.csdn.net/doctor_who2004/article/details/52081308


加载中
返回顶部
顶部