Подтвердить что ты не робот

Как работать с Singleton вместе с сериализацией

Рассмотрим, что у меня есть класс Singleton, определенный следующим образом.

public class MySingleton implements Serializable{
 private static MySingleton myInstance;

 private MySingleton(){

 }
  static{
    myInstance =new MySingleton();
 }
 public static MySingleton getInstance(){
    return MySingleton.myInstance;
 }
}

Вышеприведенное определение по мне удовлетворяет требованиям Singleton. Добавляется только добавленное поведение: класс реализует сериализуемый интерфейс.

Если другой класс X получает экземпляр синтаксиса и записывает его в файл, а в более поздней точке десериализует его для получения другого экземпляра, у нас есть два экземпляра, которые противоречат принципу Singleton.

Как я могу избежать этого или я ошибаюсь в самом определении.

4b9b3361

Ответ 1

Лучший способ сделать это - использовать шаблон однократного переименования:

public enum MySingleton {
  INSTANCE;
}

Это гарантирует односторонность объекта и обеспечивает сериализуемость для вас таким образом, что вы всегда получаете один и тот же экземпляр.

В более общем плане вы можете предоставить метод readResolve(), например:

protected Object readResolve() {
  return myInstance;
}

Ответ 2

@ColinD - это правильно, но его ответ также иллюстрирует, почему одиночные игры действительно не задирают сериализацией.

Здесь, когда вы сериализуете значение перечисления (см. здесь).

Правила сериализации экземпляра перечисления отличаются от правил для сериализации "обычного" сериализуемого объекта: сериализованная форма экземпляра перечисления состоит только из его имени константы перечисления, а также информации, идентифицирующей его базовый тип перечисления. Кроме того, поведение десериализации - информация о классе используется для поиска соответствующего класса enum, и метод Enum.valueOf вызывается с этим классом и полученным именем константы, чтобы получить возвращаемую константу перечисления.

Таким образом, любое дополнительное состояние, которое вы присоединяете к своим значениям перечисления, не выдерживает сериализации и десериализации.

Вы можете сделать то же самое самостоятельно, добавив специальный код сериализации/десериализации для ваших одноэлементных классов. Этот код должен был бы либо не записывать одноэлементное состояние вообще, либо выбросить его, когда singleton будет десериализован. В любом случае, вы поместили бы логику в метод readResolve(), как объяснил ответ @ColinD.

Теперь я полагаю, что причина, по которой вы хотите сериализовать синглтоны, заключается в том, что вы хотите сохранить свое состояние. К сожалению, это представляет собой концептуальную проблему. Предположим, что ваше приложение создало экземпляр singleton в обычном ходе событий, а затем десериализует некоторый граф объектов, который включает в себя копию предыдущего экземпляра синглтона. Что он может сделать?

  • Если он десериализует синглтон нормально, он нарушает "singleton-ness".
  • Если это не так, приложение не сможет получить доступ к предыдущему состоянию singleton.

Ответ 3

Решение с перечислением не будет работать с Singletons, которым управляют Spring, EJB, Guice или любые другие рамки DI. Он работает только с перечислениями, только потому, что перечисление обрабатывается специально алгоритмом сериализации.

Во-первых, синглтоны не нуждаются в сериализации, потому что если вы десериализовали его, а затем десериализовали singleton!= YourSingleton.getInstance(), это означало бы, что у вас есть два экземпляра вашего синглтона, а это означает, что YourSingleton не является одиночным на всех, что может привести к непредсказуемым ошибкам.

Однако иногда вам нужно сериализовать non-singleton, который содержит ссылку на singleton. Решение легко:

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

С Spring:

@Configurable
class NonSingleton implements Serializable {
    @Autowired
    private transient YourSingleton singleton;
    ...
}

Ответ 4

Ниже приведен мой класс Singleton, реализующий интерфейс Serializable. Пометьте, что он также содержит метод readResolve().

import java.io.Serializable;

public class Singleton implements Serializable {

    private static Singleton singleton = new Singleton( );

    public int i = 1;

    private Singleton() { }

    public static Singleton getInstance( ) {

       return singleton;
    }

    public Object readResolve() {
       return getInstance( );
    }

    public static void main(String[] args) {
        Singleton s1 = getInstance();
        System.out.println(s1.hashCode());

        Singleton s2 = getInstance();
        System.out.println(s2.hashCode());
    }
}

Ниже приведен класс, который сначала сериализует, а затем десериализует вышеуказанный класс. Здесь десериализация происходит два раза, но оба раза будет создан только один экземпляр из-за метода readResolve().

public class SingletonSerializableDemo {

    static Singleton sing = Singleton.getInstance();
    static Singleton s1  = null;
    static Singleton s2 = null;
    public static void main(String[] args) {
        try {
             FileOutputStream fileOut =
             new FileOutputStream("E:/singleton.ser");
             ObjectOutputStream out = new ObjectOutputStream(fileOut);
             out.writeObject(sing);
             out.close();
             fileOut.close();
             System.out.println("Serialized data is saved");

             FileInputStream fileIn1 = new FileInputStream("E:/singleton.ser");
             FileInputStream fileIn2 = new FileInputStream("E:/singleton.ser");
             ObjectInputStream in1 = new ObjectInputStream(fileIn1);
             ObjectInputStream in2 = new ObjectInputStream(fileIn2);
             s1 = (Singleton) in1.readObject();
             s2 = (Singleton) in2.readObject();
             System.out.println(s1.hashCode() + " "+ s1.i);
             s1.i = 10;
             System.out.println(s2.hashCode() + " "+ s2.i);
             in1.close();
             in2.close();
             fileIn1.close();
             fileIn2.close();
          }catch(Exception i) {
             i.printStackTrace();
          }
    }
}

И выход будет:

Сохраняются серийные данные
21061094 1
21061094 10

Вывод: Класс Singleton также можно сериализовать, сохранив метод readResolve() в классе Singleton.

Ответ 5

Это может быть знакомое решение, но только для справки.

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;
    }

}

Тестирование кода

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();
        }

    }

}

Выход

Instance reference check->[email protected]
Instance reference check->[email protected]
===================================================
Object reference check->[email protected]
Object reference check->[email protected]

Ответ 6

Скажем, мы имеем следующий одноэлементный класс:

public class ConnectionFactory implements Serializable {
    private static ConnectionFactory INSTANCE;

    private ConnectionFactory() {  }

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

Теперь у нас есть основной класс, как показано ниже для сериализации и десериализации объектов:

 public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
     ConnectionFactory INSTANCE=ConnectionFactory.getInstance();
     ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("connFactory.ser"));  
     oos.writeObject(INSTANCE);  
     oos.close();

     // Here I am recreating the instance by reading the serialized object data store
     ObjectInputStream ois = new ObjectInputStream(new FileInputStream("connFactory.ser"));  
     ConnectionFactory factory1 = (ConnectionFactory) ois.readObject();  
     ois.close();  

     // I am recreating the instance AGAIN by reading the serialized object data store
     ObjectInputStream ois2 = new ObjectInputStream(new FileInputStream("connFactory.ser"));  
     ConnectionFactory factory2 = (ConnectionFactory) ois2.readObject();  
     ois2.close();

     // Let see how we have broken the singleton behavior
     System.out.println("Instance reference check->" +factory1.getInstance());
     System.out.println("Instance reference check->" +factory2.getInstance());
     System.out.println("=========================================================");
     System.out.println("Object reference check->" + factory1);
     System.out.println("Object reference check->" + factory2);
}

Итак, если мы выполним над кодом, мы получим следующее поведение: "он создал два объекта и одну статическую ссылку для INSTANCE. Это означает, что если мы будем считывать сериализованный формат объекта singleton несколько раз, мы создадим несколько объектов. Это не то, что должен делать одноэлементный объект. мы можем избежать i?, Да, мы можем.

Чтобы избежать множественных экземпляров одноэлементного класса, мы будем использовать следующий метод, предоставляемый сериализацией:

private Object readResolve() throws ObjectStreamException {
    return INSTANCE;
}

Это предотвратит создание нескольких экземпляров одноэлементного класса.

Ответ 7

Я думаю, что синглтоны могут быть сериализованы, и вот код о том, как это сделать:


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;
    }
}

и вот "основной" метод, который получает экземпляр класса Singleton выше, сериализует и де-сериализует его:



 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();
        }

    }

и вывод: -

in if...

Акшай

Спасибо.

Ответ 8

Вот ответ за нарушение класса Singleton и как предотвратить создание нашим классом другого объекта с помощью метода readResolve();

импорт java.io.Serializable;

открытый класс Singleton реализует 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;
}

}

открытый класс 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);
}

}

Ответ 9

Одиночные классы - это как менеджер или контроллер, и вообще мы не хотим сохранять состояние какого-либо контроллера вместо объекта. Как правило, нам нужно сохранить состояние объекта любого объекта, а не контроллера.
Singleton singleton для одного загрузчика классов не для загрузчика с несколькими классами. Если класс загружается в загрузчик классов, тогда другой загрузчик классов не будет знать об этом, поэтому он будет вести себя так.