Note that there are some explanatory texts on larger screens.

plurals
  1. POThe better Java singleton pattern nowadays?
    primarykey
    data
    text
    <p>You know, that since Java 5 is released the recommended way to write Singleton pattern in Java is using enum.</p> <pre><code>public enum Singleton { INSTANCE; } </code></pre> <p>But, what I don't like in this - is to force the client to use Singleton.INSTANCE in order to have access to the singleton instance. Maybe, the better way to hide Singleton inside the ordinary class, and provide more better access to singleton facilities:</p> <pre><code>public class ApplicationSingleton { private static enum Singleton { INSTANCE; private ResourceBundle bundle; private Singleton() { System.out.println("Singleton instance is created: " + System.currentTimeMillis()); bundle = ResourceBundle.getBundle("application"); } private ResourceBundle getResourceBundle() { return bundle; } private String getResourceAsString(String name) { return bundle.getString(name); } }; private ApplicationSingleton() {} public static ResourceBundle getResourceBundle() { return Singleton.INSTANCE.getResourceBundle(); } public static String getResourceAsString(String name) { return Singleton.INSTANCE.getResourceAsString(name); } } </code></pre> <p>So, the client now can simply write:</p> <pre><code>ApplicationSingleton.getResourceAsString("application.name") </code></pre> <p>for example. Which is much better then:</p> <pre><code>Singleton.INSTANCE.getResourceAsString("application.name") </code></pre> <p>So, the question is: Is it the right way to do it? Does this code have any issues (thread-safety?)? Does it have all the advantages the "enum singleton" pattern has? It seems that it takes the better from the both world. What do you think? Is any better way to achieve this? Thanks. </p> <p><strong>EDIT</strong><br/> @all<br/> First of all enum usage for Singleton pattern was mentioned in the Effective Java, 2nd Edition: <a href="http://en.wikipedia.org/wiki/Singleton_pattern#The_Enum-way">wikipedia:Java Enum Singleton</a>. I totally agree that we should minimize Singleton usage as much as possible, but we can't totally go away from them.<br/> Before I provide another example, let me say, that the first example with ResourceBundle is just a case, the example itself (and the classes names) are not from the real application. But, need to say, that I didn't know about ResourceBundle cache management, thanks for that piece of information )<br/></p> <p>Below, there are 2 different approaches for Singleton pattern, the first is the new approach with Enum, and the second is the standard approach most of us used before. And I try to show significant differences between them.<br/></p> <p><strong>Singleton using Enum:</strong><br/> ApplicationSingleton class is:</p> <pre><code>public class ApplicationSingleton implements Serializable { private static enum Singleton { INSTANCE; private Registry registry; private Singleton() { long currentTime = System.currentTimeMillis(); System.out.println("Singleton instance is created: " + currentTime); registry = new Registry(currentTime); } private Registry getRegistry() { return registry; } private long getInitializedTime() { return registry.getInitializedTime(); } private List&lt;Registry.Data&gt; getData() { return registry.getData(); } }; private ApplicationSingleton() {} public static Registry getRegistry() { return Singleton.INSTANCE.getRegistry(); } public static long getInitializedTime() { return Singleton.INSTANCE.getInitializedTime(); } public static List&lt;Registry.Data&gt; getData() { return Singleton.INSTANCE.getData(); } } </code></pre> <p>Registry class is:</p> <pre><code>public class Registry { private List&lt;Data&gt; data = new ArrayList&lt;Data&gt;(); private long initializedTime; public Registry(long initializedTime) { this.initializedTime = initializedTime; data.add(new Data("hello")); data.add(new Data("world")); } public long getInitializedTime() { return initializedTime; } public List&lt;Data&gt; getData() { return data; } public class Data { private String name; public Data(String name) { this.name = name; } public String getName() { return name; } } } </code></pre> <p>And test class:</p> <pre><code>public class ApplicationSingletonTest { public static void main(String[] args) throws Exception { String rAddress1 = ApplicationSingleton.getRegistry().toString(); Constructor&lt;ApplicationSingleton&gt; c = ApplicationSingleton.class.getDeclaredConstructor(); c.setAccessible(true); ApplicationSingleton applSingleton1 = c.newInstance(); String rAddress2 = applSingleton1.getRegistry().toString(); ApplicationSingleton applSingleton2 = c.newInstance(); String rAddress3 = applSingleton2.getRegistry().toString(); // serialization ByteArrayOutputStream byteOut = new ByteArrayOutputStream(); ObjectOutputStream out = new ObjectOutputStream(byteOut); out.writeObject(applSingleton1); ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(byteOut.toByteArray())); ApplicationSingleton applSingleton3 = (ApplicationSingleton) in.readObject(); String rAddress4 = applSingleton3.getRegistry().toString(); List&lt;Registry.Data&gt; data = ApplicationSingleton.getData(); List&lt;Registry.Data&gt; data1 = applSingleton1.getData(); List&lt;Registry.Data&gt; data2 = applSingleton2.getData(); List&lt;Registry.Data&gt; data3 = applSingleton3.getData(); System.out.printf("applSingleton1=%s, applSingleton2=%s, applSingleton3=%s\n", applSingleton1, applSingleton2, applSingleton3); System.out.printf("rAddr1=%s, rAddr2=%s, rAddr3=%s, rAddr4=%s\n", rAddress1, rAddress2, rAddress3, rAddress4); System.out.printf("dAddr1=%s, dAddr2=%s, dAddr3=%s, dAddr4=%s\n", data, data1, data2, data3); System.out.printf("time0=%d, time1=%d, time2=%d, time3=%d\n", ApplicationSingleton.getInitializedTime(), applSingleton1.getInitializedTime(), applSingleton2.getInitializedTime(), applSingleton3.getInitializedTime()); } } </code></pre> <p>And here is the output:</p> <pre><code>Singleton instance is created: 1304067070250 applSingleton1=ApplicationSingleton@18a7efd, applSingleton2=ApplicationSingleton@e3b895, applSingleton3=ApplicationSingleton@6b7920 rAddr1=Registry@1e5e2c3, rAddr2=Registry@1e5e2c3, rAddr3=Registry@1e5e2c3, rAddr4=Registry@1e5e2c3 dAddr1=[Registry$Data@1dd46f7, Registry$Data@5e3974], dAddr2=[Registry$Data@1dd46f7, Registry$Data@5e3974], dAddr3=[Registry$Data@1dd46f7, Registry$Data@5e3974], dAddr4=[Registry$Data@1dd46f7, Registry$Data@5e3974] time0=1304067070250, time1=1304067070250, time2=1304067070250, time3=1304067070250 </code></pre> <p>What should to mention:</p> <ol> <li>Singleton instance was created only once</li> <li>Yes, there are several different instances of ApplicationSingletion, but all of them contain the same Singleton instance</li> <li>Registry internal data is the same for all <strong>different</strong> ApplicationSingleton instance</li> </ol> <p><em>So, to summarize</em>: Enum approach works fine and prevent duplicate Singleton creation through reflection attack, and return the same instance after being serialized. </p> <p><strong>Singleton using standard approach:</strong> <br/> ApplicationSingleton class is:</p> <pre><code>public class ApplicationSingleton implements Serializable { private static ApplicationSingleton INSTANCE; private Registry registry; private ApplicationSingleton() { try { Thread.sleep(10); } catch (InterruptedException ex) {} long currentTime = System.currentTimeMillis(); System.out.println("Singleton instance is created: " + currentTime); registry = new Registry(currentTime); } public static ApplicationSingleton getInstance() { if (INSTANCE == null) { return newInstance(); } return INSTANCE; } private synchronized static ApplicationSingleton newInstance() { if (INSTANCE != null) { return INSTANCE; } ApplicationSingleton instance = new ApplicationSingleton(); INSTANCE = instance; return INSTANCE; } public Registry getRegistry() { return registry; } public long getInitializedTime() { return registry.getInitializedTime(); } public List&lt;Registry.Data&gt; getData() { return registry.getData(); } } </code></pre> <p>Registry class is (note that Registry and Data classes explicitly should implement Serializable in order serialization to work):</p> <pre><code>//now Registry should be Serializable in order serialization to work!!! public class Registry implements Serializable { private List&lt;Data&gt; data = new ArrayList&lt;Data&gt;(); private long initializedTime; public Registry(long initializedTime) { this.initializedTime = initializedTime; data.add(new Data("hello")); data.add(new Data("world")); } public long getInitializedTime() { return initializedTime; } public List&lt;Data&gt; getData() { return data; } // now Data should be Serializable in order serialization to work!!! public class Data implements Serializable { private String name; public Data(String name) { this.name = name; } public String getName() { return name; } } } </code></pre> <p>And ApplicationSingletionTest class is (mostly the same):</p> <pre><code>public class ApplicationSingletonTest { public static void main(String[] args) throws Exception { String rAddress1 = ApplicationSingleton.getInstance().getRegistry().toString(); Constructor&lt;ApplicationSingleton&gt; c = ApplicationSingleton.class.getDeclaredConstructor(); c.setAccessible(true); ApplicationSingleton applSingleton1 = c.newInstance(); String rAddress2 = applSingleton1.getRegistry().toString(); ApplicationSingleton applSingleton2 = c.newInstance(); String rAddress3 = applSingleton2.getRegistry().toString(); // serialization ByteArrayOutputStream byteOut = new ByteArrayOutputStream(); ObjectOutputStream out = new ObjectOutputStream(byteOut); out.writeObject(applSingleton1); ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(byteOut.toByteArray())); ApplicationSingleton applSingleton3 = (ApplicationSingleton) in.readObject(); String rAddress4 = applSingleton3.getRegistry().toString(); List&lt;Registry.Data&gt; data = ApplicationSingleton.getInstance().getData(); List&lt;Registry.Data&gt; data1 = applSingleton1.getData(); List&lt;Registry.Data&gt; data2 = applSingleton2.getData(); List&lt;Registry.Data&gt; data3 = applSingleton3.getData(); System.out.printf("applSingleton1=%s, applSingleton2=%s, applSingleton3=%s\n", applSingleton1, applSingleton2, applSingleton3); System.out.printf("rAddr1=%s, rAddr2=%s, rAddr3=%s, rAddr4=%s\n", rAddress1, rAddress2, rAddress3, rAddress4); System.out.printf("dAddr1=%s, dAddr2=%s, dAddr3=%s, dAddr4=%s\n", data, data1, data2, data3); System.out.printf("time0=%d, time1=%d, time2=%d, time3=%d\n", ApplicationSingleton.getInstance().getInitializedTime(), applSingleton1.getInitializedTime(), applSingleton2.getInitializedTime(), applSingleton3.getInitializedTime()); } } </code></pre> <p>And here is the output:</p> <pre><code>Singleton instance is created: 1304068111203 Singleton instance is created: 1304068111218 Singleton instance is created: 1304068111234 applSingleton1=ApplicationSingleton@16cd7d5, applSingleton2=ApplicationSingleton@15b9e68, applSingleton3=ApplicationSingleton@1fcf0ce rAddr1=Registry@f72617, rAddr2=Registry@4f1d0d, rAddr3=Registry@1fc4bec, rAddr4=Registry@1174b07 dAddr1=[Registry$Data@1256ea2, Registry$Data@82701e], dAddr2=[Registry$Data@1f934ad, Registry$Data@fd54d6], dAddr3=[Registry$Data@18ee9d6, Registry$Data@19a0c7c], dAddr4=[Registry$Data@a9ae05, Registry$Data@1dff3a2] time0=1304068111203, time1=1304068111218, time2=1304068111234, time3=1304068111218 </code></pre> <p>What should to mention:</p> <ol> <li>Singleton instance was created several! times</li> <li>All registry objects are different objects with its own data</li> </ol> <p><em>So, to summarize</em>: Standard approach is weak for reflection attack, and return different instance after being serialized, but yes with the same data. </p> <hr> <p>So, it seems that Enum approach is more solid and robust. And is it the recommended way for using Singleton pattern in Java nowadays? What do you think?<br/> Interesting fact to explain: why objects inside enum can be serialized with its owning class does not implement Serializable? Is it feature or bug?</p>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload