#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(...)
или просто используя метод.