web-dev-qa-db-ja.com

Javaクラスの静的データメンバーをシリアル化する方法は?

オブジェクトをシリアル化する場合、静的メンバーはシリアル化されませんが、シリアル化する必要がある場合、解決策はありますか?

32
Maddy.Shik

最初の質問は、静的メンバーをシリアル化する必要がある理由です。

静的メンバーはインスタンスではなくクラスに関連付けられているため、インスタンスをシリアル化するときにそれらを含めることは意味がありません。

最初の解決策は、これらのメンバーを静的にしないことです。または、それらのメンバーが元のクラスとターゲットクラスで同じである場合(同じクラスですが、ランタイム環境が異なる可能性があります)、それらをまったくシリアル化しないでください。

静的メンバー間で送信する方法についていくつかの考えがありますが、最初にユースケースを確認する必要があります。これは、すべての場合において、ターゲットクラスを更新することを意味します。そのための十分な理由がありません。

18
Kathy Van Stone

人々、静的は不変という意味ではありません。たとえば、JVMやホストコンピュータが再起動した後で、計算の全体の状態(静的フィールド(カウンタなど)を含む)をシリアル化して後で再開したい場合があります。

これに対する正しい答えは、すでに述べたように、シリアル化可能ではなく、外部化可能インターフェースを使用することです。次に、何をどのように外部化するかを完全に制御できます。

16

シリアル化を制御するには、以下を実装します。

private void writeObject(ObjectOutputStream out) throws IOException;

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

シリアライゼーションの完全な説明 http://Java.Sun.com/developer/technicalArticles/Programming/serialization/ があります。

他の答えが言ったように、それはあなたがシリアル化しているクラスではなくオブジェクトであり、そうする必要があるので、静的をシリアル化することは実際には意味がありません。

5
Nick Holt

これは静的フィールドのシリアル化です: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
Adam

良い答えとコメント-それをしないでください。しかし、どうやって?

すべての「統計」を保持するオブジェクトを作成することをお勧めします。そのオブジェクトには、おそらくクラスの静的メソッドも含まれているはずです。

クラスのすべてのインスタンスは、この他のクラスを保持できます。または、本当に必要な場合は、任意のメンバーがアクセスできるシングルトンにすることができます。

このリファクタリングを実行すると、これがずっとこの方法で実行されているはずであることがわかります。サブコンシデンスレベルで煩わしかった以前の設計上の制約の一部がなくなっていることに気付くかもしれません。

このソリューションは、まだ気づいていない他のシリアライゼーションの問題も解決することに気付くでしょう。

2
Bill K

フィールドを変更するたびにクラスを手動で更新する必要なく、これを行うことができます。アプリケーションの設定にアクセスするための静的メンバーを簡単にしたいが、それらの設定も保存したい場合は、これを行うことができます。この場合、静的であるため、ここでは他のソリューションが必要とするため、デフォルトでロードするのではなく、気まぐれでそれらを適用するオプションも必要です。これにより、明らかな理由で設定のロールバックが可能になります。

基本的には、フィールドメソッドを使用してクラスのすべてのメンバーを取得し、次にこれらのフィールドのフルネームをコンテンツにマッピングします。 Field自体はシリアル化できないため、フルネームが必要です。このマッピングをシリアル化し、復元して保存された設定を取得します。

パズルの2番目の部分は、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
bobjandal

静的メンバーは、個々のオブジェクトではなく、クラスに属しています。

データ構造を再検討する必要があります。

はい、静的変数をシリアル化できます。しかし、独自のwriteObject()readObject()を書くことができます。これで問題は解決すると思います。

0
naresh

コンパクトに実装するには、クラスにreadObjectとwriteObjectを実装し、通常のシリアル化を処理するメソッド内のdefaultReadObjectとdefaultWriteObjectメソッドを呼び出してから、必要な追加フィールドのシリアル化と非シリアル化に進みます。

よろしく、GK

0
G Kumar