singleton public static final

2020-02-06 17:14发布

I've been wondering about singletons in Java. By convention, a singleton is set up something like this:

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

Recently I've switched to using the following:

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

This is a lot shorter, faster as there's no null-check, and typing MyClass.instance feels nicer to me than typing MyClass.getInstance(). Is there any reason why the second is not the mainstream way to do this?

5条回答
三岁会撩人
2楼-- · 2020-02-06 18:08

I can think of two reasons:

The first is Encapsulation: you might have second thoughts about how and when your singleton is initialized, after your class has been exposed to client code. And an initialization method gives you more freedom as to changing your strategy later on. For example you might change your mind and decide to use two different constructors instead of one, according to another static variable's value at runtime. With your solution you're bound to using just one constructor at the time your class is loaded into memory, whereas with getInstance() you can change the initialization logic without affecting the interface to the client code.

The second is Lazy Initialization : with the conventional singleton implementation the MyClass object is loaded into memory only when needed by the client code for the first time. And if the client code doesn't need it at all, you save on the memory allocated by your application. Note that whether your singleton is needed or not might not be certain until the program runs. For example it might depend on the user interaction with the program.

However the Lazy Initialization is not something you might necessarily want. For example if you're programming an interactive system and the initialization of your singleton is time consuming, it might actually be better to initialize it when the program is loading rather than when the user is already interacting with it, cause the latter might cause a latency in your system response the first time getInstance() is called. But in this case you can just have your instance initialized with the public method as in:

private static MyClass instance = getInstance();
查看更多
闹够了就滚
3楼-- · 2020-02-06 18:11

The first version creates the instance the first time it is actually needed, while the second (the shorter) runs the constructor as soon as the class is initialized

A class or interface type T will be initialized immediately before the first occurrence of any one of the following:

  • T is a class and an instance of T is created.
  • T is a class and a static method declared by T is invoked.
  • A static field declared by T is assigned.
  • A static field declared by T is used and the field is not a constant variable (§4.12.4).
  • T is a top level class (§7.6), and an assert statement (§14.10) lexically nested within T (§8.1.3) is executed. [...]

Invocation of certain reflective methods in class Class and in package java.lang.reflect also causes class or interface initialization.

The initialization upon first usage is a performance improvement that may speed up the startup of the application if the code in the constructor makes expensive operations. On the other hand, the second version is straightforward to read and is automatically thread-safe.

Anyway, the state of the art is not creating singleton in either ways: for a bunch of KB you can get dependency injection libraries that make it working for you, and also handle more complex scenarios (for example look at Spring and AOP-backed injection).

Note: the first version is not thread safe in the pasted snippet

查看更多
▲ chillily
4楼-- · 2020-02-06 18:14

Reflection: Reflection can be caused to destroy singleton property of singleton class, as shown in following example:

 // 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
     } 
 } 


查看更多
做自己的国王
5楼-- · 2020-02-06 18:17

The best way to synchronize the threads is using Double Checked (to ensure that only one thread will enter the synchronized block at a time and to avoid obtaining the lock every time the code is executed).

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;
        }
}
查看更多
爱情/是我丢掉的垃圾
6楼-- · 2020-02-06 18:18

The way you have first described is known as lazy instantiation, i.e the object will only be created when it is first called. This method is not thread-safe as it is possible for a second thread to create a second instance.

If you read the following book:

Effective Java by Joshua Bloch

He explains that the best implementation of the singleton pattern is through the use of an Enum :

public enum Singleton {

  INSTANCE;

  public void doSomething() {
     ...
  }

}

Then you would call your singleton through the Enum as follows:

public class Test {

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

This fits nicely with what you are saying in terms of it looks nicer and shorter to write but also guarantees there can never be a second instance.

查看更多
登录 后发表回答