How to deal with Singleton along with Serialization

后端 未结 9 1576
感情败类
感情败类 2020-12-04 13:06

Consider I have a Singleton class defined as follows.

public class MySingleton implements Serializable{
 private static MySingleton myInstance;

 private MyS         


        
相关标签:
9条回答
  • 2020-12-04 13:44

    I think Singletons can be serialized and here is the code on how to do it:

    
    import java.io.Serializable;
    
    public class MySingleton implements Serializable {
    
        private MySingleton(String name) {
            this.name = name;
        }
    
        private static MySingleton mySingleton;
    
        private String name;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public static MySingleton getInstance(String name) {
            if(mySingleton == null) {
                System.out.println("in if...");
                mySingleton = new MySingleton(name);
            }
    
            return mySingleton;
        }
    }
    

    and here is "main" method which gets the instance of the Singleton class above, serializes and de-serializes it:

    
    
     public static void main (String[] args) {
    
            MySingleton m = MySingleton.getInstance("Akshay");
    
            try {
                ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("D://temp.ser"));
                oos.writeObject(m);
    
                ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D://temp.ser"));
                MySingleton m2 = (MySingleton) ois.readObject();
                System.out.println(m2.getName());
            } catch (IOException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
    
        }
    

    and output is:-

    in if...

    Akshay

    Thanks.

    0 讨论(0)
  • 2020-12-04 13:49

    This may be a familiar solution but just in case for reference.

    public class ConnectionFactory implements Serializable {
    
        //Static variable for holding singleton reference object
        private static ConnectionFactory INSTANCE;
    
        /**
         * Private constructor
         */
        private ConnectionFactory() {
    
        }
    
        /**
         * Static method for fetching the instance
         *
         * @return
         */
        public static ConnectionFactory getIntance() {
            //Check whether instance is null or not
            if (INSTANCE == null) {
                //Locking the class object
                synchronized (ConnectionFactory.class) {
                    //Doing double check for the instance
                    //This is required in case first time two threads simultaneously invoke
                    //getInstance().So when another thread get the lock,it should not create the
                    //object again as its already created by the previous thread.
                    if (INSTANCE == null) {
                        INSTANCE = new ConnectionFactory();
                    }
                }
            }
            return INSTANCE;
        }
    
        /**
         * Special hook provided by serialization where developer can control what object needs to sent.
         * However this method is invoked on the new object instance created by de serialization process.
         *
         * @return
         * @throws ObjectStreamException
         */
        private Object readResolve() throws ObjectStreamException {
            return INSTANCE;
        }
    
    }
    

    Testing the code

    public class SerializationTest {
    
        public static void main(String[] args) {
            ConnectionFactory INSTANCE = ConnectionFactory.getIntance();
    
            try {
                ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("connectFactory.ser"));
                oos.writeObject(INSTANCE);
                oos.close();
    
                ObjectInputStream osi = new ObjectInputStream(new FileInputStream("connectFactory.ser"));
                ConnectionFactory factory1 = (ConnectionFactory) osi.readObject();
                osi.close();
    
                ObjectInputStream osi2 = new ObjectInputStream(new FileInputStream("connectFactory.ser"));
                ConnectionFactory factory2 = (ConnectionFactory) osi2.readObject();
                osi2.close();
    
    
                System.out.println("Instance reference check->" + factory1.getIntance());
                System.out.println("Instance reference check->" + factory2.getIntance());
                System.out.println("===================================================");
                System.out.println("Object reference check->" + factory1);
                System.out.println("Object reference check->" + factory2);
    
            } catch (IOException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
    
        }
    
    }
    

    Output

    Instance reference check->com.javabrains.ConnectionFactory@6f94fa3e
    Instance reference check->com.javabrains.ConnectionFactory@6f94fa3e
    ===================================================
    Object reference check->com.javabrains.ConnectionFactory@6f94fa3e
    Object reference check->com.javabrains.ConnectionFactory@6f94fa3e
    
    0 讨论(0)
  • 2020-12-04 13:51

    Here is the Answer for Breaking the Singleton class and and how to prevent our class from creating different object by using readResolve() methood;

    import java.io.Serializable;

    public class Singleton implements Serializable {

    private static final long serialVersionUID = 1L;
    
    private Singleton() {
    }
    
    private static class SingletonHelper {
    
        private static final Singleton INSTANCE = new Singleton();
    
    }
    
    public static Singleton getInstance() {
    
        return SingletonHelper.INSTANCE;
    }
    
    private Object readResolve() {
        Singleton instance = getInstance();
        return instance;
    }
    

    }

    public class BreakSIngletonUsingSerialization {

    public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
    
        Singleton demo1 =Singleton.getInstance();
        ObjectOutput out = new ObjectOutputStream(new FileOutputStream("C:/Eclipse/serial.ser"));
        out.writeObject(demo1);
        Singleton demo2 =null;
        ObjectInput in = new ObjectInputStream(new FileInputStream("C:/Eclipse/serial.ser"));
    
        demo2 = (Singleton)in.readObject();
    
        System.out.println("Hascode demo1 : " +demo1);
        System.out.println("Hascode demo2 : " +demo2);
    }
    

    }

    0 讨论(0)
  • 2020-12-04 13:52

    @ColinD is kind of right, but his answer also illustrates why singletons don't really jell with serialization.

    Here's what happens when you serialize an enum value (see here).

    The rules for serializing an enum instance differ from those for serializing an "ordinary" serializable object: the serialized form of an enum instance consists only of its enum constant name, along with information identifying its base enum type. Deserialization behavior differs as well--the class information is used to find the appropriate enum class, and the Enum.valueOf method is called with that class and the received constant name in order to obtain the enum constant to return.

    So any additional state that you attach to your enum values does not survive serialization and deserialization.

    You could do the same thing yourself, by adding custom serialization / deserialization code to your singleton classes. That code would need to either not record the singleton's state at all, or throw it away when the singleton is deserialized. Either way, you'd put the logic into a readResolve() method as explained by @ColinD's answer.

    Now, I presume that the reason you want to serialize singletons is that you want to persist their state. Unfortunately, that presents a conceptual problem. Suppose that your application has instantiated the singleton in the normal course of events, and then it deserializes some object graph that includes a copy of a previous instance of the singleton. What can it do?

    • If it deserializes the singleton normally, it violates "singleton-ness".
    • If it doesn't then the application cannot access the singleton's previous state.
    0 讨论(0)
  • 2020-12-04 13:53

    The best way to do this is to use the enum singleton pattern:

    public enum MySingleton {
      INSTANCE;
    }
    

    This guarantees the singleton-ness of the object and provides serializability for you in such a way that you always get the same instance.

    More generally, you can provide a readResolve() method like so:

    protected Object readResolve() {
      return myInstance;
    }
    
    0 讨论(0)
  • 2020-12-04 13:54

    The solution with enum won't work with Singletons managed by Spring, EJB, Guice or any other DI framework. It works only with enums, only because enum is treated specially by the serialization algorithm.

    Firstly, singletons don't need serialization, because if you deserialized it, and then deserialized singleton != YourSingleton.getInstance(), it would mean that you have two instances of your singleton, which means that YourSingleton isn't singleton at all, which may lead to unpredictable bugs.

    However sometimes you need to serialize non-singleton which contains a reference to singleton. The solution is easy:

    class NonSingleton implements Serializable {
        private transient YourSingleton singleton = YourSingleton.getInstance();
        ...
    }
    

    With Spring:

    @Configurable
    class NonSingleton implements Serializable {
        @Autowired
        private transient YourSingleton singleton;
        ...
    }
    
    0 讨论(0)
提交回复
热议问题