Detailed Explanation of Three Realization Modes of Singleton in Java

  • 2021-08-28 19:56:02
  • OfStack

1. What is Singleton?

Erich Gamma, author of Design Patterns and developer of Eclipse and Junit, defines Singleton as a class that is instantiated only once in its theoretical system. In today's real-world development of object-oriented programs, Singleton is often used to represent a stateless object, such as functions and system components that are essentially 1-only.

It is worth noting that making a class Singleton makes it very difficult to test it on the client side, because it is impossible to replace the mock implementation with Singleton unless we implement an interface that acts as its type.

There are three common ways to implement Singleton, either by keeping the constructor private and exporting public static members, or by declaring an enumerated type that contains a single element.

2. Singleton Implementation-Constructor Private

1. The public static member is an final domain


//Singleton with public final field 
public class Elvis { 
 public static final Elvis INSTANCE = new Elvis(); 
 pritvate Elvis() { ... } 
 public void leaveTheBuilding() { ... }
} 

In this class, we have only one private constructor, which is called only once when initializing the final domain. Subsequent programs can no longer create Elvis objects due to the lack of a constructor that can be used. This ensures that only one Elvis object exists during the entire life cycle of the Java program.

However, it should be noted that some high-privileged clients can call private constructors through reflection mechanism with the help of AccessibleObject. setAccessible method. To avoid such a possible attack, you can modify the constructor so that it throws an exception when asked to create the second instance.

The main advantage of the public domain method is that API clearly indicates that this class is an Singleton, after all, this is a public static property. In addition, this method is simpler.

2. The public static member is a static factory method


//Singleton with static factory
public class Elvis { 
 private static final Elvis INSTANCE = new Elvis(); 
 pritvate Elvis() { ... } 
 public static Elvis getInstance(){ return INSTANCE; }
 public void leaveTheBuilding(){ ... }
} 

Obviously, no matter how the getInstance method is called, it returns a reference to the same object. Note that the reflex attack problem mentioned above still exists.

The static factory approach has three major advantages

First, it provides more flexibility, and we can easily adjust whether this class is Singleton without changing API. The factory method returns only 1 instances of the class, but it can be easily modified to look something else, such as providing only 1 instances for each thread that calls the method. Second, if the program needs it, we can write a generic Singleton factory. Third, we can use method references as providers, for example, Elvis:: instance is an Supplier < Elvis >

(Note: Method reference is a new feature of Java8)

Unless we need one of the above advantages, we should choose a simpler way to use the public domain.

3. Make the Singleton class implemented by the above method serializable

To make Singleton implemented using the above two methods serializable, you can't just add implements Serializable to the declaration. To maintain and guarantee Singleton, we must live all instance domains instantaneously and provide an readResolve method. Otherwise, a new instance will be created every time we serialize. To prevent this, we add the following readResolve method to the Elvis class.


//readResolve method to preserve singleton property 
 private Object readResolve(){
 //Return the one true Elvis and let the garbage collector take care of the Elvis impersonator
 return INSTANCE;
 }

3. Singleton Implementation-Declare an enumerated type that contains a single element


//Enum singleton - the preferred approach
public enum Elvis{
 INSTANCE;
 public void leaveTheBuilding(){ ... }
}

This approach is functionally similar to the public domain approach, but more concise, provides a serialization mechanism free of charge, and absolutely prevents multiple instantiations, even in the face of complex serialization or reflection attacks. Although this approach has not been widely adopted, single-element enumerated types are often the best way to implement Singleton. Note that if Singleton must extend a superclass instead of Enum, this method should not be used (although enumerations can be declared to implement interfaces).

Summarize


Related articles: