Как сериализовать статические данные членов класса Java? - PullRequest
31 голосов
/ 17 июня 2009

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

Ответы [ 9 ]

18 голосов
/ 17 июня 2009

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

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

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

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

15 голосов
/ 17 июня 2009

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

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

5 голосов
/ 06 декабря 2010

Это сериализация для статического поля: 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();
    }
}
5 голосов
/ 17 июня 2009

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

private void writeObject(ObjectOutputStream out) throws IOException;

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

Там есть полное описание сериализации http://java.sun.com/developer/technicalArticles/Programming/serialization/.

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

2 голосов
/ 05 сентября 2011

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

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

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

2 голосов
/ 17 июня 2009

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

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

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

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

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

1 голос
/ 17 июня 2009

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

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

0 голосов
/ 09 ноября 2010

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

С уважением, GK

0 голосов
/ 27 декабря 2009

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

...