35

我们序列化对象的时候,静态成员是不序列化的,但是如果我们需要这样做,有什么办法吗?

4

9 回答 9

23

第一个问题是为什么需要序列化静态成员?

静态成员与类相关联,而不是与实例相关联,因此在序列化实例时包含它们是没有意义的。

第一个解决方案是使这些成员不是静态的。或者,如果这些成员在原始类和目标类(相同的类,但可能不同的运行时环境)中相同,则根本不要序列化它们。

我对如何跨静态成员发送有一些想法,但我首先需要查看用例,因为在所有情况下都意味着更新目标类,我还没有找到这样做的充分理由。

于 2009-06-17T16:12:13.747 回答
20

伙计们,静态并不意味着不可变。例如,我可能想序列化整个计算状态(是的,包括静态字段——计数器等),以便稍后在 JVM 和/或主机重新启动后恢复。

如前所述,正确的答案是使用 Externalizable,而不是 Serializable 接口。然后你可以完全控制你外化的内容和方式。

于 2009-06-17T16:33:00.543 回答
7

这是静态字段的序列化: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();
    }
}
于 2010-12-06T19:32:03.733 回答
5

您可以通过实现来控制序列化:

private void writeObject(ObjectOutputStream out) throws IOException;

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

有序列化的完整描述http://java.sun.com/developer/technicalArticles/Programming/serialization/

正如其他答案所说,序列化静态数据并没有真正意义,因为它是对象而不是您正在序列化的类,并且需要这样做闻起来就像您的代码对我有其他问题。

于 2009-06-17T16:22:20.923 回答
2

好的答案和评论——不要这样做。但是怎么做?

您最好创建一个对象来保存所有“静态”。该对象可能也应该具有您的类中的任何静态方法。

你的类的每个实例都可以容纳另一个类——或者如果你真的需要,你可以让它成为任何成员都可以访问的单例。

在你做了这个重构之后,你会发现它应该一直这样做。您甚至可能会发现,以前在 subconsicionce 级别上困扰您的一些设计约束已经消失。

您可能会发现此解决方案还解决了您尚未注意到的其他序列化问题。

于 2009-06-17T16:26:35.257 回答
2

您可以执行此操作,而无需在每次更改字段时手动更新您的课程。如果您希望静态成员可以轻松访问应用程序中的设置,但也希望保存这些设置,您可能希望这样做。在这种情况下,您还希望可以选择随心所欲地应用它们,而不是默认加载,因为这里的其他解决方案需要,因为它们是静态的。这允许出于显而易见的原因回滚设置。

基本上,使用字段方法获取类中的所有成员,然后将这些字段的全名映射到内容。由于 Field 本身不可序列化,因此需要全名。序列化此映射,并恢复它以获取保存的设置。

谜题的第二部分是 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〜

于 2011-09-05T11:15:02.593 回答
1

静态成员属于类,而不属于单个对象。

您应该重新考虑您的数据结构。

于 2009-06-17T16:13:59.283 回答
0

是的,我们可以序列化静态变量。但是我们可以编写自己的writeObject()and readObject()。我认为这可以解决问题。

于 2009-12-27T04:31:52.920 回答
0

要实现紧凑的实现,请在您的类中实现 readObject 和 writeObject 调用 defaultReadObject 和 defaultWriteObject 方法,这些方法在处理正常序列化的那些方法中,然后继续序列化和反序列化您需要的任何其他字段。

问候, GK

于 2010-11-09T11:56:17.207 回答