Как сериализовать статические данные-члены класса Java?

Когда мы сериализуем объекты, статические члены не сериализуются, но если нам нужно это сделать, есть ли выход?


person Maddy.Shik    schedule 17.06.2009    source источник
comment
Прекратить использовать статические члены для данных, которые должны быть сериализованы?   -  person krosenvold    schedule 17.06.2009
comment
Приложение: прекратите использовать статические элементы для хранения изменяемого состояния.   -  person Juliet    schedule 17.06.2009
comment
+1, если вы сериализуете, не используйте статические члены   -  person marcgg    schedule 17.06.2009
comment
Shared и mutable НЕ являются взаимоисключающими. Нет смысла сериализовать статические константы (поскольку они неизменяемы), но иногда имеет смысл сериализовать статические переменные (поскольку они должны быть синхронизированы с остальным состоянием при десериализации).   -  person Vladimir Dyuzhev    schedule 17.06.2009
comment
Владимир, нет смысла сериализовать статические поля.   -  person Tom Hawtin - tackline    schedule 17.06.2009
comment
Никогда не имеет смысла говорить никогда.   -  person Vladimir Dyuzhev    schedule 17.06.2009
comment
Именно в такой ситуации оправдывается шаблон проектирования Singleton.   -  person Paul Draper    schedule 03.02.2013


Ответы (9)


Первый вопрос: зачем вам сериализовать статические элементы?

Статические члены связаны с классом, а не экземплярами, поэтому нет смысла включать их при сериализации экземпляра.

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

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

person Kathy Van Stone    schedule 17.06.2009
comment
предположим, я хочу подсчитать количество экземпляров класса, такого как Car. и я не использую базу данных. Поэтому в этом случае после закрытия приложения мне нужно сохранить эту информацию с другой информацией. - person Maddy.Shik; 18.06.2009
comment
Вам нужно хранить этот номер напрямую? Если вы обновите номер при десериализации каждого автомобиля (путем переопределения readObject), он будет правильно отражать количество автомобилей в сериализации. - person Kathy Van Stone; 18.06.2009
comment
Статическая переменная является общей для всех экземпляров объектов класса (общая память). Если вам нужно сохранить что-то статичное, например, позволяет вам иметь перечисление разных значений, и время от времени вы меняете статическое значение этого типа перечисления, изменение сохраненного значения может быть таким же простым, как если бы все классы установили локальную переменную к тому типу. Или вы могли бы сделать еще один шаг и иметь кучу классов, расширяющих класс Whatever, а класс Whatever мог бы просто использовать локальную переменную для всех своих дочерних элементов. Когда вы десериализуете, вы можете перезагрузить свои статические значения обратно в то состояние, в котором они были. - person ; 23.11.2012

Ребята, статический не означает НЕИЗМЕННЫЙ. Например, я могу захотеть сериализовать все состояние вычислений (да, включая статические поля - счетчики и т. д.), чтобы возобновить позже, после перезапуска JVM и/или хост-компьютера.

Правильный ответ на это, как уже было сказано, заключается в использовании интерфейса Externalizable, а не Serializable. Тогда у вас есть полный контроль над тем, что и как вы экстернализуете.

person Vladimir Dyuzhev    schedule 17.06.2009
comment
Если состояние вычисления не встроено в объект, который вы сериализуете, вам не следует сохранять его как часть сериализации этого объекта. - person DJClayworth; 17.06.2009
comment
Изменяемый статический обычно означает сломанный. - person Tom Hawtin - tackline; 17.06.2009
comment
Подумайте еще раз. Как насчет многопоточных генетических алгоритмов, например? Общее (статическое) хранилище используется для ведения реестра оцененных вариантов. Сделать синглтоном? Возможно, но у него есть свои недостатки. - person Vladimir Dyuzhev; 17.06.2009
comment
Да. Сделайте его синглтоном. Сериализировать синглтон. Или, лучше, избавьтесь от общего статического хранилища, храните его в явном объекте контекста. - person Thilo; 18.06.2009
comment
... И тогда еще одна группа пуристов будет жаловаться, что Синглтон - это анти-шаблон, от которого я тоже должен избавиться. Что касается явного объекта контекста, как разные потоки получают к нему доступ? С помощью статических методов? Как лучше? - person Vladimir Dyuzhev; 18.06.2009
comment
Под явным объектом контекста я имел в виду явную передачу его всем, кому он нужен. - person Thilo; 19.06.2009

Это сериализация для статического поля: 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();
    }
}
person Adam    schedule 06.12.2010

Вы можете управлять сериализацией, реализуя:

private void writeObject(ObjectOutputStream out) throws IOException;

private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException;

Существует полное описание сериализации http://java.sun.com/developer/technicalArticles/Programming/serialization/.

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

person Nick Holt    schedule 17.06.2009
comment
Вы можете управлять сериализацией, но некуда помещать статические данные, потому что это не имеет никакого смысла в контексте. - person Tom Hawtin - tackline; 17.06.2009
comment
Поскольку мы не знаем контекста, мы не можем сказать, имеет ли это смысл или нет. Например, после использования сериализации по умолчанию может иметь смысл добавить в поток статический член. При десериализации можно установить статический член, если он равен нулю, в противном случае отбросить сериализованный объект. - person erickson; 17.06.2009
comment
Том, ты злоупотребляешь доказательством повторением, не так ли? ;) - person Vladimir Dyuzhev; 17.06.2009
comment
есть ли способ сериализовать статические члены путем сериализации объекта класса из моего класса. - person Maddy.Shik; 18.06.2009

Хорошие ответы и комментарии - не делайте этого. Но как?

Скорее всего, вам лучше всего создать объект для хранения всей вашей «Статики». Этот объект, вероятно, также должен иметь какие-либо статические методы из вашего класса.

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

После того, как вы проведете этот рефакторинг, вы обнаружите, что все это время нужно было делать именно так. Вы даже можете обнаружить, что некоторые из ваших предыдущих проектных ограничений, которые беспокоили вас на уровне подсознания, исчезли.

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

person Bill K    schedule 17.06.2009

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

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

Вторая часть головоломки — это функция типа 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~

person bobjandal    schedule 05.09.2011

Статические члены принадлежат классу, а не отдельным объектам.

Вы должны пересмотреть свою структуру данных.

person Thorbjørn Ravn Andersen    schedule 17.06.2009

Чтобы иметь компактную реализацию, реализуйте readObject и writeObject в своем классе, вызовите методы defaultReadObject и defaultWriteObject в тех методах, которые обрабатывают обычную сериализацию, а затем приступайте к сериализации и десериализации любых дополнительных полей, которые вам нужны.

С уважением, ГК.

person G Kumar    schedule 09.11.2010

Да, мы можем сериализовать статические переменные. Но мы можем написать свои собственные writeObject() и readObject(). Думаю, это может решить проблему.

person naresh    schedule 27.12.2009