In
my previous post, I explained how to implement a singleton pattern. This is
continuation to my previous post, so I recommend you to go through the post,
before reading this.
Singleton
pattern restricts the instantiation of a class to one object. That is you can't
create more than one object to this class.
Connection.java
package com.sample.singleton; public class Connection { private static final Connection INSTANCE = new Connection(); private int connId = 0; public static Connection getInstance() { return INSTANCE; } private Connection() { connId++; System.out.println("Connection object is created"); } @Override public String toString() { StringBuilder builder = new StringBuilder(); builder.append("Connection [connId=").append(connId).append("]"); return builder.toString(); } }
ConnectionTest.java
package com.sample.singleton; public class ConnectionTest { public static void main(String args[]) { Connection conn1 = Connection.getInstance(); Connection conn2 = Connection.getInstance(); Connection conn3 = Connection.getInstance(); System.out.println("conn1 = " + conn1); System.out.println("conn2 = " + conn2); System.out.println("conn3 = " + conn3); System.out.println("conn1 == conn2 : " + (conn1 == conn2)); System.out.println("conn1 == conn3 : " + (conn1 == conn3)); System.out.println("conn2 == conn3 : " + (conn2 == conn3)); } }
Connection
class works absolutely fine until Connection class don't implement Serializable
interface. If Connection class implements Serializable interface, whenever
application deserialize the object, it return new instance, which violates
singleton property. Let's see it by an example.
Connection.java
package com.sample.singleton; import java.io.Serializable; public class Connection implements Serializable { private static final long serialVersionUID = 1234L; private static final Connection INSTANCE = new Connection(); private int connId = 0; public static Connection getInstance() { return INSTANCE; } private Connection() { connId++; System.out.println("Connection object is created"); } @Override public String toString() { StringBuilder builder = new StringBuilder(); builder.append("Connection [connId=").append(connId).append("]"); return builder.toString(); } }
ConnectionTest.java
package com.sample.singleton; import java.io.*; public class ConnectionTest { public static void serializeObject(Connection conn, String fileName) { try (FileOutputStream fos = new FileOutputStream(fileName); ObjectOutputStream out = new ObjectOutputStream(fos)) { out.writeObject(conn); } catch (IOException e) { System.out.println("Error while serializing Connection object " + e.getMessage()); } } public static Connection deserializeObject(String fileName) { try (FileInputStream fis = new FileInputStream("ser.out"); ObjectInputStream in = new ObjectInputStream(fis)) { Connection conn = (Connection) in.readObject(); return conn; } catch (Exception e) { System.out.println("Error while Deserializing Connection object " + e.getMessage()); } return null; } public static void main(String args[]) { Connection conn1 = Connection.getInstance(); String fileName = "ser.out"; serializeObject(conn1, fileName); Connection conn2 = deserializeObject(fileName); Connection conn3 = deserializeObject(fileName); System.out.println("conn1 = " + conn1); System.out.println("conn2 = " + conn2); System.out.println("conn3 = " + conn3); System.out.println("conn1 == conn2 : " + (conn1 == conn2)); System.out.println("conn1 == conn3 : " + (conn1 == conn3)); System.out.println("conn2 == conn3 : " + (conn2 == conn3)); } }
Output
Connection object is created conn1 = Connection [connId=1] conn2 = Connection [connId=1] conn3 = Connection [connId=1] conn1 == conn2 : false conn1 == conn3 : false conn2 == conn3 : false
As
you see the output, the objects conn1, conn2, conn3 are not equal. Every time
application deserialize the object, it is returning different object.
How to resolve the
issue?
To
make singleton pattern works properly, Singleton class must implement the
method readResolve. By using readResolve method, you can control what object
should be returned on deserialization. Update Connection class like below.
Connection.java
package com.sample.singleton; import java.io.Serializable; public class Connection implements Serializable { private static final long serialVersionUID = 1234L; private static final Connection INSTANCE = new Connection(); private int connId = 0; public static Connection getInstance() { return INSTANCE; } private Connection() { connId++; System.out.println("Connection object is created"); } @Override public String toString() { StringBuilder builder = new StringBuilder(); builder.append("Connection [connId=").append(connId).append("]"); return builder.toString(); } private Object readResolve() { return INSTANCE; } }
Re
run ConnectionTest.java, you can able to see following output.
Connection object is created conn1 = Connection [connId=1] conn2 = Connection [connId=1] conn3 = Connection [connId=1] conn1 == conn2 : true conn1 == conn3 : true conn2 == conn3 : true
Better way to provide
singleton behavior to Connection class using Enum
From
java1.5 onwards, by defining enum type with one element we can create singleton
class very easily.
Update
Connection class like below.
Connection.java
package com.sample.singleton; import java.io.Serializable; public enum Connection implements Serializable { INSTANCE; private int connId = 0; public static Connection getInstance() { return INSTANCE; } private Connection() { connId++; System.out.println("Connection object is created"); } @Override public String toString() { StringBuilder builder = new StringBuilder(); builder.append("Connection [connId=").append(connId).append("]"); return builder.toString(); } }
Re
run ConnectionTest.java file, you can able to see following output.
Connection object is created conn1 = Connection [connId=1] conn2 = Connection [connId=1] conn3 = Connection [connId=1] conn1 == conn2 : true conn1 == conn3 : true conn2 == conn3 : true
One
advantage of this approach is, you no need to take care of serialization &
deserialization issue, java handles it internally. I always prefer to implement
singleton using enum.
No comments:
Post a Comment