繁体   English   中英

单例公共静态最终

[英]singleton public static final

我一直想知道Java中的单例。 按照惯例,单例设置如下:

private static MyClass instance = null;
public static MyClass getInstance(){
    if (instance == null){
        instance = new MyClass();
    }
    return instance;
}
private MyClass(){}

最近,我切换到使用以下内容:

public static final MyClass instance = new MyClass();
private MyClass(){}

由于没有空检查,因此它更短,更快捷,并且键入MyClass.instance对我来说比键入MyClass.getInstance()更好。 有什么理由不能使第二种方法成为主流呢?

第一个版本在第一次实际需要时创建实例,而第二个版本(较短的版本)在初始化类后立即运行构造函数

类或接口类型T将在以下任何一种首次出现之前立即初始化:

  • T是一个类,并创建T的实例。
  • T是一个类,并调用T声明的静态方法。
  • 分配由T声明的静态字段。
  • 使用由T声明的静态字段,并且该字段不是常量变量(第4.12.4节)。
  • T是顶级类(第7.6节),并执行词法嵌套在T中的断言(第14.10节)(第8.1.3节)。 [...]

调用类Class和包java.lang.reflect中的某些反射方法也会导致类或接口初始化。

如果构造函数中的代码进行了昂贵的操作,则首次使用时的初始化会提高性能,从而可以加快应用程序的启动速度。 另一方面,第二个版本易于阅读,并且自动具有线程安全性。

无论如何,最新技术不会以任何方式创建单例:对于一堆KB,您可以获得依赖注入库,使其适合您,并处理更复杂的场景(例如,查看Spring和AOP支持的注入)。 )。

注意:第一个版本在粘贴的代码段中不是线程安全的

您首先描述的方式称为延迟实例化,即仅在首次调用对象时才创建该对象。 此方法不是线程安全的,因为第二个线程可能创建第二个实例。

如果您阅读以下书籍:

Joshua Bloch撰写的有效Java

他解释说,单例模式的最佳实现是通过使用Enum

public enum Singleton {

  INSTANCE;

  public void doSomething() {
     ...
  }

}

然后,您将通过Enum调用您的单身人士,如下所示:

public class Test {

    public void test(){
        Singleton.INSTANCE.doSomething();
    }
}

这与您所说的内容非常吻合,因为它看起来更好,更短,但也保证永远不会出现第二个实例。

我可以想到两个原因:

第一个是封装 :在类暴露给客户端代码之后,您可能会对如何以及何时初始化单例进行第二次思考。 初始化方法为您提供了以后更改策略的更多自由。 例如,您可能改变主意,并根据运行时另一个静态变量的值决定使用两个不同的构造函数,而不是一个。 使用您的解决方案,您必须在将类加载到内存时仅使用一个构造函数,而使用getInstance()可以更改初始化逻辑,而不影响客户端代码的接口。

第二个是惰性初始化 :使用常规的单例实现,仅当客户端代码首次需要时, MyClass对象才加载到内存中。 而且,如果根本不需要客户端代码,则可以节省应用程序分配的内存。 请注意,在程序运行之前,可能不确定是否需要单身人士。 例如,它可能取决于用户与程序的交互。

但是,惰性初始化不是您可能需要的东西。 例如,如果您正在编写一个交互式系统,并且单例的初始化很耗时,那么在加载程序时而不是在用户已经在与之交互时,初始化它实际上可能会更好,因为后者可能会导致首次调用getInstance()时系统响应中的延迟。 但是在这种情况下,您可以使用public方法初始化实例,如下所示:

private static MyClass instance = getInstance();

同步线程的最佳方法是使用Double Checked(确保一次仅一个线程将进入同步块,并避免在每次执行代码时获得锁)。

public class DoubleCheckLocking {

    public static class SearchBox {
        private static volatile SearchBox searchBox;

        // private attribute of this class
        private String searchWord = "";
        private String[] list = new String[]{"Stack", "Overflow"};

        // private constructor
        private SearchBox() {}

        // static method to get instance
        public static SearchBox getInstance() {
            if (searchBox == null) { // first time lock
                synchronized (SearchBox.class) {
                    if (searchBox == null) {  // second time lock
                        searchBox = new SearchBox();
                    }
                }
            }
            return searchBox;
        }
}

反射:可能导致反射破坏单例类的单例属性,如以下示例所示:

 // Java code to explain effect of Reflection 

 import java.lang.reflect.Constructor; 

 // Singleton class 
 class Singleton  
 { 
     // public instance initialized when loading the class 
     public static Singleton instance = new Singleton(); 

     private Singleton()  
     { 
         // private constructor 
     } 
 } 

 public class GFG  
 { 

     public static void main(String[] args) 
     { 
         Singleton instance1 = Singleton.instance; 
         Singleton instance2 = null; 
         try
         { 
             Constructor[] constructors =  
                     Singleton.class.getDeclaredConstructors(); 
             for (Constructor constructor : constructors)  
             { 
                 // Below code will destroy the singleton pattern 
                 constructor.setAccessible(true); 
                 instance2 = (Singleton) constructor.newInstance(); 
                 break; 
             } 
         } 

         catch (Exception e)  
         { 
             e.printStackTrace(); 
         } 

     System.out.println("instance1.hashCode():- " 
                                       + instance1.hashCode()); //366712642
     System.out.println("instance2.hashCode():- " 
                                       + instance2.hashCode()); //1829164700
     } 
 } 


暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM