Home Java javaTutorial Difficulties in implementing singletons in Java

Difficulties in implementing singletons in Java

Dec 05, 2016 am 11:34 AM
java

There are simple and efficient ways to implement the singleton pattern, but there is no way to ensure the integrity of the singleton under all circumstances.

The singleton pattern means that a class is instantiated only once and is used to represent global or system-wide components. The singleton pattern is often used for logging, factories, window managers, platform component management, etc. I think you should try to avoid using the singleton pattern, because once implemented, it is difficult to change or overload, and it will cause problems such as difficulty in writing test cases and poor code structure. Also, the singleton pattern in the following article is unsafe.

People spend a lot of energy researching how to better implement the singleton pattern, but there is a simple and efficient implementation method. However, there is no one way to ensure the integrity of a singleton under all circumstances. Read below and see if you agree.

Final field

This method privatizes the constructor and provides a public static final object:

public class FooSingleton {    
    public final static FooSingleton INSTANCE = new FooSingleton();   
    private FooSingleton() { }    
    public void bar() { }}
Copy after login

When the class is loaded, the static object is initialized, and at this time the private constructor is used for the first and last time transfer. Even if multiple threads call this class before the class is initialized, the JVM can guarantee that the class is fully initialized when the threads continue to run. However, using reflection and the setAccessible(true) method, it is possible to create other new instances:

Constructor[] constructors = FooSingleton.class.getDeclaredConstructors();
Constructor constructor = constructors[0];
constructor.setAccessible(true);
FooSingleton spuriousFoo = (FooSingleton) constructor.newInstance(new Object[0]);
Copy after login

We need to modify the constructor so that it does not have to be called multiple times, e.g. throwing an exception when it is called again. Modifying the FooSingleton constructor as follows can prevent such attacks:

public class FooSingleton2 {    
private static boolean INSTANCE_CREATED;    
public final static FooSingleton2 INSTANCE = new FooSingleton2();    
private FooSingleton2() {        
    if (INSTANCE_CREATED) {            
        throw new IllegalStateException("You must only create one instance of this class");        
     } else {            
         INSTANCE_CREATED = true;        
     }    
  }    
      public void bar() { }
}
Copy after login

This seems safer, but in fact it is still just as easy to create a new instance. We just need to modify the INSTANCE_CREATED field and play the same trick again:

Field f = FooSingleton2.class.getDeclaredField("INSTANCE_CREATED");
f.setAccessible(true);
f.set(null, false);
Constructor[] constructors = FooSingleton2.class.getDeclaredConstructors();
Constructor constructor = constructors[0];
constructor.setAccessible(true);
FooSingleton2 spuriousFoo = (FooSingleton2) constructor.newInstance(new Object[0]);
Copy after login

Any precautions we take may be bypassed, so this solution is not feasible.

Static factory

Using this method, the public members are similar to static factories:

public class FooSingleton3 {    
  public final static FooSingleton3 INSTANCE = new FooSingleton3();    
  private FooSingleton3() { }    
  public static FooSingleton3 getInstance() { return INSTANCE; }    
  public void bar() { }
}
Copy after login

The getInstance() method always returns the same object reference. Although this solution cannot prevent reflections, it still has some advantages. For example, you can change the implementation of a singleton without changing the API. getInstance() appears in almost all singleton implementations, and it also signals that this is really a singleton pattern.

Lazy loading singleton mode

(Translator’s Note: In software engineering, the idiom Initialization-on-demand holder refers to the lazy loading singleton mode, see Wikipedia)

If you want to delay as much as possible To create a singleton (lazy loading), you can use the lazy initialization method to create a singleton thread-safely when the getInstance() method is called for the first time. Compared with the previous solution, which creates a singleton when the class is referenced for the first time (hungry-style loading), this is an improvement. As follows:

public class FooSingleton4 {    
private FooSingleton4() {    
}    
public static FooSingleton4 getInstance() {       
 return FooSingleton4Holder.INSTANCE;    
 }    
private static class FooSingleton4Holder {       
 private static final FooSingleton4 INSTANCE = new FooSingleton4();    
 }
}
Copy after login

Be careful with serialization

If a singleton implements serialization, it faces another threat. Therefore you need to declare all fields as transient (so that it will not be serialized) and provide a custom readResolve() method that returns a reference to the unique instance INSTANCE.

Enumeration

Here we use enumeration as the container of single instance INSTANCE:

public enum FooEnumSingleton {
    INSTANCE;    
    public static FooEnumSingleton getInstance() { 
    return INSTANCE; 
    }    
    public void bar() { }
}
Copy after login

According to Java Language Specification 8.9, "Enum's final cloning method ensures that the enumeration can never be cloned, and its special serialization mechanism ensures that it cannot be deserialized. At the same time, it is also prohibited to use reflection to instantiate the enumeration. This ensures that there will be no other similar enumeration instances outside the enumeration constant. "

We seem to be easily protected against serialization, cloning, and reflection attacks. The first time I saw this passage, I immediately wanted to prove it wrong. As shown in the following code, it is easy to bypass these protections:

Constructor con = FooEnumSingleton.class.getDeclaredConstructors()[0];
 Method[] methods = con.getClass().getDeclaredMethods();
 for (Method method : methods) {
     if (method.getName().equals("acquireConstructorAccessor")) {
         method.setAccessible(true);
         method.invoke(con, new Object[0]);
     }
  }
  Field[] fields = con.getClass().getDeclaredFields();
  Object ca = null;  for (Field field : fields) {
      if (field.getName().equals("constructorAccessor")) {
          field.setAccessible(true);
          ca = field.get(con);
      }
  }  Method method = ca.getClass().getMethod("newInstance", new Class[]{Object[].class});
  method.setAccessible(true);
  FooEnumSingleton spuriousEnum = (FooEnumSingleton) method.invoke(ca, new Object[]{new Object[]{"SPURIOUS_INSTANCE", 1}});
  printInfo(FooEnumSingleton.INSTANCE);
  printInfo(spuriousEnum);
}private static void printInfo(FooEnumSingleton e) {
    System.out.println(e.getClass() + ":" + e.name() + ":" + e.ordinal());
}
Copy after login

Executing this code, you get the result:

class com.blogspot.minborgsjavapot.singleton.FooEnumSingleton:INSTANCE:0
class com.blogspot.minborgsjavapot.singleton.FooEnumSingleton:SPURIOUS_INSTANCE:1
Copy after login

The disadvantage of an enumeration is that it cannot be inherited from another base class because it already inherits from java.lang .Enum. If you want to simulate this kind of inheritance, you can refer to the mixin pattern introduced in my other article.

One advantage of enumerations is that if you later want to have a "dualton" or "tringleton", you only need to add a new enumeration instance. For example, after having a singleton cache, you may also want to introduce multiple layers to the cache.

Conclusion

While bypassing these protections for singletons is not easy, there really is no foolproof solution. If you have a better solution, please feel free to let me know!

Enumeration is a simple and efficient way to implement the singleton pattern. If you want inheritance or lazy loading, lazy initialization is a good choice.

Good luck with your singletons!


Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn

Hot AI Tools

Undresser.AI Undress

Undresser.AI Undress

AI-powered app for creating realistic nude photos

AI Clothes Remover

AI Clothes Remover

Online AI tool for removing clothes from photos.

Undress AI Tool

Undress AI Tool

Undress images for free

Clothoff.io

Clothoff.io

AI clothes remover

AI Hentai Generator

AI Hentai Generator

Generate AI Hentai for free.

Hot Article

R.E.P.O. Energy Crystals Explained and What They Do (Yellow Crystal)
4 weeks ago By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Best Graphic Settings
4 weeks ago By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. How to Fix Audio if You Can't Hear Anyone
4 weeks ago By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Chat Commands and How to Use Them
4 weeks ago By 尊渡假赌尊渡假赌尊渡假赌

Hot Tools

Notepad++7.3.1

Notepad++7.3.1

Easy-to-use and free code editor

SublimeText3 Chinese version

SublimeText3 Chinese version

Chinese version, very easy to use

Zend Studio 13.0.1

Zend Studio 13.0.1

Powerful PHP integrated development environment

Dreamweaver CS6

Dreamweaver CS6

Visual web development tools

SublimeText3 Mac version

SublimeText3 Mac version

God-level code editing software (SublimeText3)

Perfect Number in Java Perfect Number in Java Aug 30, 2024 pm 04:28 PM

Guide to Perfect Number in Java. Here we discuss the Definition, How to check Perfect number in Java?, examples with code implementation.

Random Number Generator in Java Random Number Generator in Java Aug 30, 2024 pm 04:27 PM

Guide to Random Number Generator in Java. Here we discuss Functions in Java with examples and two different Generators with ther examples.

Weka in Java Weka in Java Aug 30, 2024 pm 04:28 PM

Guide to Weka in Java. Here we discuss the Introduction, how to use weka java, the type of platform, and advantages with examples.

Smith Number in Java Smith Number in Java Aug 30, 2024 pm 04:28 PM

Guide to Smith Number in Java. Here we discuss the Definition, How to check smith number in Java? example with code implementation.

Java Spring Interview Questions Java Spring Interview Questions Aug 30, 2024 pm 04:29 PM

In this article, we have kept the most asked Java Spring Interview Questions with their detailed answers. So that you can crack the interview.

Break or return from Java 8 stream forEach? Break or return from Java 8 stream forEach? Feb 07, 2025 pm 12:09 PM

Java 8 introduces the Stream API, providing a powerful and expressive way to process data collections. However, a common question when using Stream is: How to break or return from a forEach operation? Traditional loops allow for early interruption or return, but Stream's forEach method does not directly support this method. This article will explain the reasons and explore alternative methods for implementing premature termination in Stream processing systems. Further reading: Java Stream API improvements Understand Stream forEach The forEach method is a terminal operation that performs one operation on each element in the Stream. Its design intention is

TimeStamp to Date in Java TimeStamp to Date in Java Aug 30, 2024 pm 04:28 PM

Guide to TimeStamp to Date in Java. Here we also discuss the introduction and how to convert timestamp to date in java along with examples.

Java Program to Find the Volume of Capsule Java Program to Find the Volume of Capsule Feb 07, 2025 am 11:37 AM

Capsules are three-dimensional geometric figures, composed of a cylinder and a hemisphere at both ends. The volume of the capsule can be calculated by adding the volume of the cylinder and the volume of the hemisphere at both ends. This tutorial will discuss how to calculate the volume of a given capsule in Java using different methods. Capsule volume formula The formula for capsule volume is as follows: Capsule volume = Cylindrical volume Volume Two hemisphere volume in, r: The radius of the hemisphere. h: The height of the cylinder (excluding the hemisphere). Example 1 enter Radius = 5 units Height = 10 units Output Volume = 1570.8 cubic units explain Calculate volume using formula: Volume = π × r2 × h (4

See all articles