DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workkloads.

Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • Unraveling Lombok's Code Design Pitfalls: Exploring Encapsulation Issues
  • Architecture and Code Design, Pt. 2: Polyglot Persistence Insights To Use Today and in the Upcoming Years
  • Double-Checked Locking Design Pattern in Java
  • Messaging Design Pattern (MDP) In Java

Trending

  • Build an MCP Server Using Go to Connect AI Agents With Databases
  • My LLM Journey as a Software Engineer Exploring a New Domain
  • Solid Testing Strategies for Salesforce Releases
  • Subtitles: The Good, the Bad, and the Resource-Heavy
  1. DZone
  2. Coding
  3. Java
  4. Singleton Design Pattern: Making Singleton More Effective in Java

Singleton Design Pattern: Making Singleton More Effective in Java

Want to learn more about when to use the singleton design pattern in Java? Check out this post on the singleton pattern and its usage as a properties holder.

By 
Brijesh Saxena user avatar
Brijesh Saxena
DZone Core CORE ·
Aug. 03, 18 · Tutorial
Likes (8)
Comment
Save
Tweet
Share
24.6K Views

Join the DZone community and get the full member experience.

Join For Free

In a response to my previous article (How to Use Singleton Design Pattern in Java), I had a few important implementation points to point out.

A singleton design pattern simply means that we like to make sure that we have only one instance in the JVM. We know that in Java we can create an instance of a class by new, clone, reflection, serializing/de-serializing, and container-provided. So, to make a class singleton, we have to perform the steps listed below: 

  • New: Change the constructor from private to prevent a new operator.

  • Clone: Implement java.lang.Clonable and override the clone() method to return the same singleton instance of the class.

  • Reflection: Implement the singleton pattern using  java-enum. However, this does not support lazy initialization.

public enum SingletonClass {
    SINGLE_INSTANCE;
}
  • Serializing/De-serializing: First, create the method  readReasolve() to return the same singleton instance of the class. The  readResolve() method is called when the ObjectInputStream is read as an object from the stream and is preparing to return it to the caller. ObjectInputStream checks whether the class of the object defines the readResolve method. If the method is defined, the  readResolve()  method is called to allow the object in the stream to designate the object to be returned. The object returned should be of a type that is compatible with all uses. If it is not compatible, a ClassCastException will be thrown when the type mismatch is discovered.

  • Container-Provided: This comes with a framework, like the SpringFramework, and we can control it up to an extent via the configuration.

In the below example, I have created a SingletonClass which is compatible with the Cloning and Serialization. I mean, The class will give the same single instance if we do clone() or writeObject(), readObject(). And if you dont want to have cloning and serialization support (which we do in 99% cases) into your SingletonClass, just simply not implement Cloneable and Serializable/Externilizable interfaces. That's it!

import java.io.Serializable;

class SingletonClass implements Cloneable, Serializable {

    private static final long serialVersionUID = -5664124213376258176L;

    private static SingletonClass SINGLE_INSTANCE = null;

    private SingletonClass() {}

    public static SingletonClass getInstance() {
        if (SINGLE_INSTANCE == null) {
            synchronized (SingletonClass.class) {
                if (SINGLE_INSTANCE == null) {
                    SINGLE_INSTANCE = new SingletonClass();
                }
            }
        }
        return SINGLE_INSTANCE;
    }

    @Override protected Object clone() throws CloneNotSupportedException {
    return SINGLE_INSTANCE;
    }

    protected Object readResolve() {
        return SINGLE_INSTANCE;
    }
}

Singleton as a Properties Holder

I prefer to use the lazy initialization method for singleton as a properties holder. In this example, I have a  dbconnection.properties  that stores database connection-related values. I like to read and hold the values in the memory to use it wherever it needs.

# DB Connection Properties.
# Oracle DB properties
#jdbc.driver=oracle.jdbc.driver.OracleDriver
#jdbc.url=jdbc:oracle:thin:@localhost:1571:MyOracleDB
#jdbc.username=admin
#jdbc.password=admin

# MySQL DB properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/MySQLDB
jdbc.username=root
jdbc.password=admin

Below is the SingletonClass,which reads and keeps the database connection-related values. This class is dependent on the propertyfile and the other values. I am keeping the above property file in the classpath-folder or the root of the project folder. Hence, this path is not needed for this.

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

class SingletonClass implements Cloneable, Serializable {

    private static final long serialVersionUID = 1007236647121448027L;

    private static final String CONNECTION_FILE_WITH_PATH = "dbconnection.properties";
    private static SingletonClass SINGLE_INSTANCE = null;

    private Properties properties = new Properties();

    private SingletonClass() {}

    public static SingletonClass getInstance() throws IOException {
        if (SINGLE_INSTANCE == null) {
            synchronized (SingletonClass.class) {
                if (SINGLE_INSTANCE == null) {
                InputStream file = new FileInputStream(new File(CONNECTION_FILE_WITH_PATH)) ;
                    SINGLE_INSTANCE = new SingletonClass();
                    SINGLE_INSTANCE.properties.load(file);
                }
            }
        }
        return SINGLE_INSTANCE;
    }

    @Override protected Object clone() throws CloneNotSupportedException {
    return SINGLE_INSTANCE;
    }

    protected Object readResolve() {
        return SINGLE_INSTANCE;
    }

    public String getPropertyValue(String propertyKey) {
      if (propertyKey != null && !propertyKey.isEmpty()) {
          return properties.getProperty(propertyKey);
      }
      return null;
    }

    public Map<String, String> getProperties() {
      Map<String, String> propertyMap = new HashMap<String, String>();
      for (String propertyKey : properties.stringPropertyNames()) {
      propertyMap.put(propertyKey, properties.getProperty(propertyKey));
      }
      return propertyMap;
    }

}

Here, in the  getProperties()  method, I have converted  java.util.Properties  to a  java.util.map  to prevent any modification of the loaded values.

Should We Use or Not Use Singleton?

If you search singleton, you will find lots of material claiming that the net stating singletons are bad, including statements like, "singletons are evil," and "it's anti-pattern." According to the masses, there are lots of issues with using singletons. So, should we use singleton or not?

Well, in my opinion, the answer is no. Until you are sure about what you are trying to achieve, for me, a singleton is something we like to only keep only in memory as an object. There is no use or wastage of memory if we duplicate them. I prefer to make them a property-values holder or an error-code supplier.

  • Please avoid making Controller , Connector  , and Managerkinds of classes as a singleton, since they may create a problem with duplicates. For example, this could happen if your application deployment goes into clustering behind any load-balancer.

  • You could also try using the monostate pattern as an alternative to the singleton. The definition I found is presented below:

"A monostate is a 'conceptual singleton' — all data members of a monostate are static, so all instances of the monostate use the same (static) data. Applications using a monostate can create any number of instances that they desire, as each instance uses the same data. What I find very nice about monostate classes is that they avoid all of the complications about having to access a particular instance of a class. Any instance is as good as another. "

Monostate Pattern

Below, we demonstrate one of the possible replacements for the above example using the monostate pattern.

class SingletonClassMono {

private static final String CONNECTION_FILE_WITH_PATH = "dbconnection.properties";

private static Properties properties = new Properties();

    static {
      InputStream file;
      try {
          file = new FileInputStream(new File(CONNECTION_FILE_WITH_PATH));
          properties.load(file);
      } catch (FileNotFoundException exp) {
          exp.printStackTrace();
      } catch (IOException exp) {
          exp.printStackTrace();
      }
    }

    public static String getPropertyValue(String propertyKey) {
      if (propertyKey != null && !propertyKey.isEmpty()) {
      return properties.getProperty(propertyKey);
      }
      return null;
    }

    public static Map<String, String> getProperties() {
      Map<String, String> propertyMap = new HashMap<String, String>();
      for (String propertyKey : properties.stringPropertyNames()) {
      propertyMap.put(propertyKey, properties.getProperty(propertyKey));
      }
      return propertyMap;
    }

}

In that case, instantiation of your class is not a problem anymore, and you don't have to worry about any of the scenarios you listed. I hope you enjoyed this post! Let me know what you think in the comments down below!

Liked the article? Don't forget to press that like button. Happy coding!

Need more articles on Design Patterns? Below are some of them I have shared with you.

  • Null Object Pattern in Java
  • Using the Adapter Design Pattern in Java

  • Using the Bridge Design Pattern in Java

  • Strategy vs. Factory Design Patterns in Java

  • Decorator Design Pattern in Java

  • How to Use Singleton Design Pattern in Java

  • Singleton Design Pattern: Making Singleton More Effective in Java

Some additional Articles:

  • Java Enums: How to Make Enums More Useful

  • Java Enums: How to Use Configurable Sorting Fields

Java (programming language) Design

Opinions expressed by DZone contributors are their own.

Related

  • Unraveling Lombok's Code Design Pitfalls: Exploring Encapsulation Issues
  • Architecture and Code Design, Pt. 2: Polyglot Persistence Insights To Use Today and in the Upcoming Years
  • Double-Checked Locking Design Pattern in Java
  • Messaging Design Pattern (MDP) In Java

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!