2014-01-26 5 views
6

Я проходил эффективную Java и наткнулся на этот пример.Deserializing a singleton in java

class Elvis implements Serializable { 
    public static final Elvis inst = new Elvis(); 
    private Elvis() { 
     System.out.println("In elvis constructor "); 
    } 

    public static Elvis getInstance() { 
     return inst; 
    } 
} 

Согласно книге, когда я десериализации, новый объект ЭЛВИС должен быть построен, но я вижу, что конструктор не вызывается во время десериализации?

Вот мой код, который сериализуется и десериализуется.

FileOutputStream fos = new FileOutputStream("myserial1.txt"); 
ObjectOutputStream oos = new ObjectOutputStream(fos); 
Elvis e = Elvis.getInstance(); 
System.out.println(" e = "+e.getInstance()); 

oos.writeObject(e); 

System.out.println("Serialization done."); 
FileInputStream fis = new FileInputStream("myserial1.txt"); 
ObjectInputStream ois = new ObjectInputStream(fis); 
Elvis el = (Elvis) ois.readObject(); 
System.out.println(" el = "+el.getInstance()); 

I Вид и e и e1 присваивают одну и ту же ссылку, а конструктор вызывается только один раз.

Я не понимаю концепцию здесь?

Пожалуйста, помогите.

+0

Deserialization подразумевает, что вы можете делать это более одного раза, и это нарушает концепцию одноэлементного. Дело не в том, что вы не можете этого сделать, но что бы вы ни делали, это будет взлом. –

+0

JBoss Smart клонирование, похоже, повторно использует конечные поля, может быть, это лучший вариант для вас? http://www.jboss.org/serialization – KIC

ответ

6

Во время сериализации не конструктор не вызывается, поля инициализируются с помощью процесса десериализации или readObject() method (если добавить этот метод в классе). Если вы хотите реализовать сериализуемый синглтон, вы должны дополнительно добавить метод readResolve(), как описано here.


PS.
Имейте в виду, что getInstance() является статическим методом класса Elvis, поэтому такие звонки, как e.getInstance() и el.getInstance(), равны Elvis.getInstance().

+0

из книги - «Чтобы сохранить синглтонную гарантию, вы должны объявить все экземпляры экземпляра переходными и предоставить метод readResolve (пункт 77). В противном случае каждый раз, когда сериализован экземпляр десериализован, будет создан новый экземпляр , который приведет, в нашем примере, к ложным наблюдениям Elvis . Чтобы этого не произошло, добавьте этот метод readResolve в класс Elvis ». Согласно автору, если я не реализую readResolve(), синглтон не работает. Но я вижу, что он не сломан, и я получаю тот же объект (ссылка такая же). Это то, чего я не понимаю. – venkataratnam

+1

@venkataratnam «Но я вижу, что его не сломал, и я получаю тот же объект (ссылка такая же)». Без метода readResolve() «el» ** ** ** ** ** не совпадает с «e», что является просто «Elvis.getInstance()». Вы можете проверить его, добавив «System.out.println (el == e)»; в конце вашей программы. –

+0

Да, вы правы. El == e возвращает false. Благодаря! – venkataratnam

1

Я не понимаю концепцию здесь?

Вы ошибаетесь в том, что конструктор создает объект. Нет, нет. Конструктор просто инициализирует объект. Теперь десериализация не требует вызова конструктора, поскольку он уже имеет состояние сериализованного объекта, и это то, что он должен нам предоставить.

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

Вы можете пройти через Serialization Specification.

0
According to the book, when i deserialize, a new ELVIS object should be constructed, 
but i see the constructor is not called at the time of deserialization? 

Прежде всего Object creation and constructor invocation are two separate thing. В обычном сценарии при создании объекта с new ключевое слово Object сначала создается, а затем вызывается конструктор. Вы можете видеть байт-коды любого класса Java, в котором создается объект.

Теперь, по вашему вопросу, в объекте Serialization создается как любой другой объект, а вместо запуска значения/состояние конструктора восстанавливается с использованием reflection. Таким образом, в основном значения считываются из потока (постоянное хранилище - ваш случай) и вводится в объект с использованием отражения.

+0

Да, просто не делайте inst final и используйте Elvis.inst = (Elvis) ois.readObject(); что ни один конструктор не называется! Результат: в конструкторе elvis e = [email protected] Выполнение сериализации. el = [email protected] – KIC

0

С чтения и записи операции оба выполнены в одном Jvm инстанции и класс уже был загружен для операции чтения и статические поля магазин уровень atclass Так вот статическая переменная инст никогда не требуется повторно инстанцирован public static final Elvis inst = new Elvis();

Так же ссылка - это возврат, который был прикреплен с уровнем класса