Когда мы сериализуем объекты, статические члены не сериализуются, но если нам нужно это сделать, есть ли выход?
Как сериализовать статические данные-члены класса Java?
Ответы (9)
Первый вопрос: зачем вам сериализовать статические элементы?
Статические члены связаны с классом, а не экземплярами, поэтому нет смысла включать их при сериализации экземпляра.
Первое решение состоит в том, чтобы сделать эти элементы нестатическими. Или, если эти члены одинаковы в исходном классе и целевом классе (один и тот же класс, но, возможно, в разных средах выполнения), не сериализуйте их вообще.
У меня есть несколько мыслей о том, как можно отправлять статические члены, но сначала мне нужно увидеть вариант использования, поскольку во всех случаях это означает обновление целевого класса, и я не нашел для этого веской причины.
Ребята, статический не означает НЕИЗМЕННЫЙ. Например, я могу захотеть сериализовать все состояние вычислений (да, включая статические поля - счетчики и т. д.), чтобы возобновить позже, после перезапуска JVM и/или хост-компьютера.
Правильный ответ на это, как уже было сказано, заключается в использовании интерфейса Externalizable, а не Serializable. Тогда у вас есть полный контроль над тем, что и как вы экстернализуете.
Это сериализация для статического поля: newBookingNumber.
class Booking implements Serializable
{
/**
* Generated serial version ID.
*/
private static final long serialVersionUID = 5316748056989930874L;
// To hold new booking number.
private static int newBookingNumber = 0;
// The booking number.
private int bookingNumber;
/*
* Default serializable fields of a class are defined to be
* the non-transient and non-static fields. So, we have to
* write and read the static field separately.
*/
private void writeObject(ObjectOutputStream oos)
throws IOException
{
oos.defaultWriteObject();
oos.writeObject(new Integer(newBookingNumber));
}
private void readObject(ObjectInputStream ois)
throws ClassNotFoundException, IOException
{
ois.defaultReadObject();
newBookingNumber = (Integer)ois.readObject();
}
}
Вы можете управлять сериализацией, реализуя:
private void writeObject(ObjectOutputStream out) throws IOException;
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException;
Существует полное описание сериализации http://java.sun.com/developer/technicalArticles/Programming/serialization/.
Как уже говорилось в других ответах, на самом деле не имеет смысла сериализовать статику, поскольку это объект, а не класс, который вы сериализуете, и вам нужно сделать это, пахнет так, как будто у вас есть другие проблемы с вашим кодом для меня.
Хорошие ответы и комментарии - не делайте этого. Но как?
Скорее всего, вам лучше всего создать объект для хранения всей вашей «Статики». Этот объект, вероятно, также должен иметь какие-либо статические методы из вашего класса.
Каждый экземпляр вашего класса может содержать этот другой класс - или, если вам действительно нужно, вы можете сделать его синглтоном, к которому может получить доступ любой член.
После того, как вы проведете этот рефакторинг, вы обнаружите, что все это время нужно было делать именно так. Вы даже можете обнаружить, что некоторые из ваших предыдущих проектных ограничений, которые беспокоили вас на уровне подсознания, исчезли.
Вы, вероятно, обнаружите, что это решение также решает другие проблемы сериализации, которые вы еще даже не замечали.
Вы можете сделать это без необходимости вручную обновлять свой класс каждый раз, когда вы просто меняете поле. Вы можете сделать это, если хотите использовать статические члены для доступа к настройкам в приложении, но также хотите сохранить эти настройки. В этом случае вы также хотели бы иметь возможность применять их по своему усмотрению, а не загружать по умолчанию, поскольку другие решения здесь необходимы, поскольку они статичны. Это позволяет откатить настройки по понятным причинам.
По сути, используйте методы полей, чтобы получить все члены класса, а затем сопоставьте полные имена этих полей с содержимым. Полное имя требуется, так как само поле не сериализуемо. Сериализуйте это сопоставление и восстановите его, чтобы получить сохраненные настройки.
Вторая часть головоломки — это функция типа apply(). Это проходит через сопоставление и применяет то, что может, к статическому классу.
Вы также должны убедиться, что содержимое статических элементов сериализуемо.
Как видно из этого примера класса, статические члены можно легко сохранить и вернуть. Я оставлю на усмотрение разработчика заботу об UID классов, средствах защиты и т. д. isSameAs() используется для модульного тестирования. AppSettings — это класс, содержащий все статические поля, которые вы хотите сериализовать.
public class AppSettingsReflectorSaver implements Serializable {
HashMap<String, Object> genericNamesAndContents = new HashMap<String, Object>();
private AppSettingsReflectorSaver() {
}
static AppSettingsReflectorSaver createAppSettingsSaver() {
AppSettingsReflectorSaver ret = new AppSettingsReflectorSaver();
ret.copyAppSettings();
return ret;
}
private void copyAppSettings() {
Field[] fields = AppSettings.class.getFields();
for (Field field : fields) {
mapContentsForSerialization(field);
}
}
private void mapContentsForSerialization(Field field) {
try {
Object fieldContents = field.get(AppSettings.class);
genericNamesAndContents.put(field.toGenericString(), fieldContents);
} catch (IllegalArgumentException ex) {
Logger.getLogger(AppSettingsReflectorSaver.class.getName()).log(Level.SEVERE, null, ex);
} catch (IllegalAccessException ex) {
Logger.getLogger(AppSettingsReflectorSaver.class.getName()).log(Level.SEVERE, null, ex);
}
}
boolean isSameAs(AppSettingsReflectorSaver now) {
for( String thisKey : genericNamesAndContents.keySet()){
boolean otherHasThisKey = now.genericNamesAndContents.containsKey(thisKey);
Object thisObject = genericNamesAndContents.get(thisKey);
Object otherObject = now.genericNamesAndContents.get(thisKey);
boolean otherHasThisValue = thisObject.equals(otherObject);
if (!otherHasThisKey || !otherHasThisValue){
return false;
}
}
return true;
}
void applySavedSettingsToStatic() {
Field[] fields = AppSettings.class.getFields();
for (Field field : fields) {
if (!genericNamesAndContents.containsKey(field.toGenericString())){
continue;
}
Object content = genericNamesAndContents.get(field.toGenericString() );
try {
field.set(AppSettings.class, content);
} catch (IllegalArgumentException ex) {
Logger.getLogger(AppSettingsReflectorSaver.class.getName()).log(Level.SEVERE, null, ex);
} catch (IllegalAccessException ex) {
Logger.getLogger(AppSettingsReflectorSaver.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
}
Это мой первый пост - полегче со мной :P~
Статические члены принадлежат классу, а не отдельным объектам.
Вы должны пересмотреть свою структуру данных.
Чтобы иметь компактную реализацию, реализуйте readObject и writeObject в своем классе, вызовите методы defaultReadObject и defaultWriteObject в тех методах, которые обрабатывают обычную сериализацию, а затем приступайте к сериализации и десериализации любых дополнительных полей, которые вам нужны.
С уважением, ГК.
Да, мы можем сериализовать статические переменные. Но мы можем написать свои собственные writeObject() и readObject(). Думаю, это может решить проблему.