Блок инициализации экземпляра не вызывается с помощью ObjectInputStream, как воссоздать объект?

#java

Вопрос:

У меня есть следующий код:

 import java.io.*;

public class StackOverflow implements Serializable
{
    private transient Object stackOverflow; 

    {
        stackOverflow = new Object();
    }

    public static void main(String[] args) throws IOException, ClassNotFoundException
    {
        File object = new File("/Users/user/Desktop/stackoverflow.txt");

        try (FileOutputStream fos = new FileOutputStream(object);
             ObjectOutputStream oos = new ObjectOutputStream(fos)) {
            oos.writeObject(new StackOverflow());
        }

        try (FileInputStream fis = new FileInputStream(object);
             ObjectInputStream ois = new ObjectInputStream(fis)) {
            StackOverflow stackOverflow = (StackOverflow) ois.readObject();
            System.out.println(stackOverflow.stackOverflow); //output: null
        }
    }
}
 

Поскольку объект не сериализуем, я сделал его временным:

 private transient Object stackOverflow; 
 

Проблема в том, что IIB (Блок инициализации экземпляра) не выполняется в readObject методе -. Итак, вопрос в том, как воссоздать переменную object.stackoverflow? И поскольку это личное, я не могу установить его снаружи.

Комментарии:

1. Отменить readObject ?

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

3. Это readObject окончательно? В документации ObjectInputStream говорится, что «Сериализуемые классы, требующие специальной обработки во время процесса сериализации и десериализации, должны реализовывать следующие методы: … private void readObject(java.io.ObjectInputStream stream) «.

4. Я думал, что вы имели в виду переопределить метод ObjectInputStream readObject, но мне пришлось объявить следующий метод (в классе Stackoverflow), чтобы заставить его работать: @Serial private void readObject(java.io.ObjectInputStream stream) { this.stackOverflow = new Object(); } Если вы создадите ответ для этого, я его приму.

Ответ №1:

IIB копируется компилятором во все конструкторы класса и выполняется как последний оператор каждого конструктора. FileInputStream#readObject однако не вызывает никаких конструкторов сериализованных классов, которые реализуют Serializable . Вызывается только конструктор по умолчанию родительских классов, которые не реализуются Serializable .

Поскольку ваше свойство stackOverflow является transient , оно не сериализуется, и поскольку конструктор StackOverflow не вызывается во время десериализации, свойство не инициализируется повторно.

Если вам нужно выполнить некоторую инициализацию во время десериализации, вы можете сделать это, реализовав `Сериализуемый#readObject)

 public class StackOverflow implements Serializable {
   private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {
      ...
   }
}
 

@см. https://docs.oracle.com/javase/8/docs/api/java/io/ObjectInputStream.html

Если вы не возражаете, я хотел бы порекомендовать быть очень осторожным с IIBs. Они редко используются и поэтому их трудно понять. Лучше просто инициализировать свойство напрямую, например

 private Cat animal = new Cat();
 

Более сложная инициализация может быть выполнена в конструкторах, для этого они и используются в первую очередь.
Если у вас более одного конструктора и вы не хотите повторять одну и ту же инициализацию в каждом конструкторе, вы можете делегировать ее другому конструктору , вызвав this(...) или просто используя метод.