JavaのSerializableとExternalizableの違いは何ですか?


回答:


267

他の回答に追加するには、を実装java.io.Serializableすることで、クラスのオブジェクトの「自動」シリアル化機能を利用できます。他のロジックを実装する必要はありません。機能します。Javaランタイムはリフレクションを使用して、オブジェクトをマーシャリングおよびマーシャリング解除する方法を理解します。

以前のバージョンのJavaでは、リフレクションが非常に遅いため、大きなオブジェクトグラフ(クライアント/サーバーRMIアプリケーションなど)のシリアル化は、パフォーマンスの問題の少しでした。この状況に対処するために、java.io.Externalizableインターフェイスが提供されました。これはjava.io.Serializable、マーシャリング機能とアンマーシャリング機能を実行するためのカスタム作成メカニズムに似ていますが、クラスにメソッドreadExternalwriteExternalメソッドを実装する必要があります。これにより、反射パフォーマンスのボトルネックを回避する手段が得られます。

Javaの最近のバージョン(1.3以降)では、リフレクションのパフォーマンスは以前よりもはるかに優れているため、問題ははるかに少なくなります。Externalizable最近のJVMで意味のある利益を得るのは難しいでしょう。

また、組み込みのJavaシリアライゼーションメカニズムだけではなく、JBoss Serializationなどのサードパーティの置き換えを利用できます。これはかなり高速で、デフォルトのドロップイン置き換えです。

の大きな欠点Externalizableは、このロジックを自分で維持する必要があることです。クラスのフィールドを追加、削除、または変更した場合は、それを説明するためにwriteExternal/ readExternalメソッドを変更する必要があります。

要約Externalizableすると、Java 1.1の遺物です。本当にそれはもう必要ありません。


61
これらのベンチマークに従っていない:[ code.google.com/p/thrift-protobuf-compare/wiki/Benchmarking]、手動のシリアル化(externizableを使用)は、Javaのデフォルトのシリアル化を使用するよりもはるかに高速です。速度が重要な場合は、必ず独自のシリアライザを作成してください。
volni

6
@Jackによって提案された新しいリンクgithub.com/eishay/jvm-serializers/wikiへの更新
noquery

3
github.com/eishay/jvm-serializers/wikiの「java-manual」は、ObjectOutputStreamの使用を意味するExternalizableを使用していませ。コードへのリンクについては、github.com / eishay / jvm-serializers / wiki / ToolBehaviorを参照してください。代わりに、DataOutputStreamを使用する手書きのコードであるため、ObjectOutputStreamを遅くする原因(オブジェクトインスタンスの追跡やオブジェクトサイクルのサポートなど)の影響を受けません。
Esko Luontola、2012年

7
自分でロジックを保守する必要があるのは、クラスが変更されず、永続化されたバージョンの古いデータを読み取る必要がない場合にのみマイナス面です。古いバージョンを逆シリアル化するために地獄のようなコードを書かなくてもクラスを自由に変更したい場合Externalizable、非常に役立ちます。
Tim Boudreau、2013

2
Externalizable空のスペースやプレースホルダーオブジェクトを含む配列を出力したくないので、カスタムコレクションを作成する必要があり、それがはるかに適していると言う必要があります。また、明示的なインターフェイスを使用すると、継承を処理できます。つまり、同期されたサブ-classは、への呼び出しの周りに簡単にロックを追加できますwriteExternal()。そのため、そうです、Externalizableは、非常に関連性が高く、確かに大きなオブジェクトや複雑なオブジェクトに適しています。
Haravikk 2014年

37

シリアライゼーションは、オブジェクトを保存して後で再作成するデフォルトの機能を提供します。これは冗長形式を使用して、格納するオブジェクトのグラフ全体を定義します。たとえば、linkedListがあり、以下のようなコードを記述した場合、デフォルトのシリアル化により、リンクされているすべてのオブジェクトが検出され、シリアル化されます。デフォルトのシリアル化では、オブジェクトは、コンストラクター呼び出しなしで、格納されたビットから完全に構築されます。

  ObjectOutputStream oos = new ObjectOutputStream(
                new FileOutputStream("/Users/Desktop/files/temp.txt"));
        oos.writeObject(linkedListHead); //writing head of linked list
        oos.close();

ただし、シリアル化を制限したい場合、またはオブジェクトの一部をシリアル化したくない場合は、Externalizableを使用します。ExternalizableインターフェースはSerializableインターフェースを拡張し、writeExternal()およびreadExternal()という2つのメソッドを追加します。これらは、シリアライズまたはデシリアライズ中に自動的に呼び出されます。Externalizableを使用するときは、デフォルトのコンストラクターをパブリックにする必要があることを忘れないでください。そうしないと、コードが例外をスローします。以下のコードに従ってください:

public class MyExternalizable implements Externalizable
{

private String userName;
private String passWord;
private Integer roll;

public MyExternalizable()
{

}

public MyExternalizable(String userName, String passWord, Integer roll)
{
    this.userName = userName;
    this.passWord = passWord;
    this.roll = roll;
}

@Override
public void writeExternal(ObjectOutput oo) throws IOException 
{
    oo.writeObject(userName);
    oo.writeObject(roll);
}

@Override
public void readExternal(ObjectInput oi) throws IOException, ClassNotFoundException 
{
    userName = (String)oi.readObject();
    roll = (Integer)oi.readObject();
}

public String toString()
{
    StringBuilder b = new StringBuilder();
    b.append("userName: ");
    b.append(userName);
    b.append("  passWord: ");
    b.append(passWord);
    b.append("  roll: ");
    b.append(roll);

    return b.toString();
}
public static void main(String[] args)
{
    try
    {
        MyExternalizable m  = new MyExternalizable("nikki", "student001", 20);
        System.out.println(m.toString());
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("/Users/Desktop/files/temp1.txt"));
        oos.writeObject(m);
        oos.close();

        System.out.println("***********************************************************************");
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("/Users/Desktop/files/temp1.txt"));
        MyExternalizable mm = (MyExternalizable)ois.readObject();
        mm.toString();
        System.out.println(mm.toString());
    } 
    catch (ClassNotFoundException ex) 
    {
        Logger.getLogger(MyExternalizable.class.getName()).log(Level.SEVERE, null, ex);
    }
    catch(IOException ex)
    {
        Logger.getLogger(MyExternalizable.class.getName()).log(Level.SEVERE, null, ex);
    }
}
}

ここで、デフォルトのコンストラクタをコメントすると、コードは例外の下でスローされます。

 java.io.InvalidClassException: javaserialization.MyExternalizable;     
 javaserialization.MyExternalizable; no valid constructor.

パスワードは機密情報であるため、writeExternal(ObjectOutput oo)メソッドでシリアル化せず、readExternal(ObjectInput oi)で同じ値を設定していないことを確認できます。それがExternalizableによって提供される柔軟性です。

上記のコードの出力は以下の通りです:

userName: nikki  passWord: student001  roll: 20
***********************************************************************
userName: nikki  passWord: null  roll: 20

passWordの値を設定していないため、nullであることがわかります。

同じことは、パスワードフィールドを一時的なものとして宣言することによっても実現できます。

private transient String passWord;

それが役に立てば幸い。間違えた場合はお詫び申し上げます。ありがとう。


22

主な違いSerializableExternalizable

  1. マーカーインターフェイスSerializableメソッドのないマーカーインターフェイスです。Externalizableインターフェイスには2つのメソッドが含まれています:writeExternal()およびreadExternal()
  2. シリアル化プロセスSerializableインターフェイスを実装するクラスのデフォルトのシリアル化プロセスが開始されます。プログラマー定義のシリアライゼーションプロセスは、Externalizableインターフェイスを実装するクラスに組み込まれます。
  3. メンテナンス互換性のない変更を行うと、シリアル化が壊れる可能性があります。
  4. 下位互換性と制御:複数のバージョンをサポートする必要がある場合は、Externalizableインターフェースで完全に制御できます。オブジェクトのさまざまなバージョンをサポートできます。を実装する場合Externalizablesuperクラスをシリアライズするのはあなたの責任です
  5. 引数なしのコンストラクタSerializableオブジェクトを構築するためにリフレクションを使用し、引数のコンストラクタを必要としません。ただし、Externalizable引数なしのパブリックコンストラクタが必要です。

参照してくださいブログHitesh Garg詳細は。


1
(3)は不正解です。既存のオブジェクトの逆シリアル化を壊すことなくクラスに加えることができる変更の大きなレパートリーがあり、直列化可能メンバーの追加は確かにそれらの1つです。それらを削除することは別です。Java Object Serialization SpecificationObject Versioningの章を参照してください
ローンの侯爵

1
言い換えた文。
Ravindra babu 2017

シリアライゼーション仕様へのリンクを共有していただきありがとうございます。
JL_SO

21

シリアライゼーションは、特定のデフォルトの動作を使用してオブジェクトを保存し、後で再作成します。参照と複雑なデータ構造を処理する順序または方法を指定できますが、最終的には各プリミティブデータフィールドのデフォルトの動作を使用することになります。

外部化は、オブジェクトを完全に異なる方法で保存および再構築したい場合に、データフィールドのデフォルトのシリアル化メカニズムを使用せずに、まれに使用されます。たとえば、独自のエンコードおよび圧縮スキームを使用しているとしましょう。


5
「選択されたID」の大きなコレクションにはExternalizableを使用しました。これは、デフォルトのシリアライゼーションよりも、カウントとプリミティブintの配列として多かれ少なかれ効率的な外部化でした。これは非常に単純なユースケースであり、「特別な」または「ユニークな」ものはありません。
トーマスW

9

オブジェクトの直列化は、直列化可能および外部化可能インターフェースを使用します。 Javaオブジェクトはシリアル化のみ可能です。クラスまたはそのスーパークラスのいずれかがjava.io.Serializableインターフェースまたはそのサブインターフェースjava.io.Externalizableを実装する場合。ほとんどのJavaクラスはシリアライズ可能です。

  • NotSerializableExceptionpackageName.ClassName«クラスオブジェクトをシリアル化プロセスに参加させるには、クラスがSerializableまたはExternalizableインターフェイスを実装する必要があります。

ここに画像の説明を入力してください


シリアライズ可能なインターフェース

オブジェクト直列化は、保存されているオブジェクトのJavaクラスに関する情報を含むストリームを生成します。シリアライズ可能なオブジェクトの場合、クラスの実装の異なる(ただし互換性のある)バージョンが存在する場合でも、それらのオブジェクトを復元するために十分な情報が保持されます。Serializableインターフェースは、直列化可能プロトコルを実装するクラスを識別するために定義されています。

package java.io;

public interface Serializable {};
  • シリアル化インターフェースにはメソッドやフィールドがなく、シリアル化可能であることのセマンティクスを識別するためだけに機能します。クラスをシリアライズ/デシリアライズするには、デフォルトのwriteObjectメソッドとreadObjectメソッドを使用できます(または)クラスのwriteObjectメソッドとreadObjectメソッドをオーバーライドできます。
  • JVMはオブジェクトのシリアライズを完全に制御します。一時的なキーワードを使用して、データメンバーがシリアル化されないようにします。
  • ここで、シリアライズ可能なオブジェクトは、実行せずにストリームから直接再構築されます
  • InvalidClassException«逆シリアル化プロセスで、ローカルクラスのserialVersionUID値が対応する送信者のクラスと異なる場合。結果は次のように競合します java.io.InvalidClassException: com.github.objects.User; local class incompatible: stream classdesc serialVersionUID = 5081877, local class serialVersionUID = 50818771
  • クラスの非一時フィールドと非静的フィールドの値はシリアル化されます。

外部化可能なインターフェース

Externalizableオブジェクトの場合、オブジェクトのクラスのIDのみがコンテナーによって保存されます。クラスは内容を保存および復元する必要があります。Externalizableインターフェースは次のように定義されています。

package java.io;

public interface Externalizable extends Serializable
{
    public void writeExternal(ObjectOutput out)
        throws IOException;

    public void readExternal(ObjectInput in)
        throws IOException, java.lang.ClassNotFoundException;
}
  • Externalizableインターフェースには2つのメソッドがあります。externalizableオブジェクトは、オブジェクトの状態を保存/復元するためにwriteExternalメソッドとreadExternalメソッドを実装する必要があります。
  • プログラマーは、シリアル化するオブジェクトを処理する必要があります。プログラマーがシリアライゼーションを処理するので、ここで、transientキーワードはシリアライゼーションプロセスのオブジェクトを制限しません。
  • Externalizableオブジェクトが再構築されると、パブリックの引数なしのコンストラクタを使用してインスタンスが作成され、次にreadExternalメソッドが呼び出されます。シリアル化可能なオブジェクトは、ObjectInputStreamから読み取ることによって復元されます。
  • OptionalDataException«フィールドは、書いたとおりの順序とタイプである必要があります。ストリームからのタイプの不一致がある場合は、OptionalDataExceptionをスローします。

    @Override public void writeExternal(ObjectOutput out) throws IOException {
        out.writeInt( id );
        out.writeUTF( role );
        out.writeObject(address);
    }
    @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        this.id = in.readInt();
        this.address = (Address) in.readObject();
        this.role = in.readUTF();
    }
  • シリアル化するために記述(公開)したクラスのインスタンスフィールドObjectOutput


« Serializableを実装する

class Role {
    String role;
}
class User extends Role implements Serializable {

    private static final long serialVersionUID = 5081877L;
    Integer id;
    Address address;

    public User() {
        System.out.println("Default Constructor get executed.");
    }
    public User( String role ) {
        this.role = role;
        System.out.println("Parametarised Constructor.");
    }
}

class Address implements Serializable {

    private static final long serialVersionUID = 5081877L;
    String country;
}

« Externalizableを実装する

class User extends Role implements Externalizable {

    Integer id;
    Address address;
    // mandatory public no-arg constructor
    public User() {
        System.out.println("Default Constructor get executed.");
    }
    public User( String role ) {
        this.role = role;
        System.out.println("Parametarised Constructor.");
    }

    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        out.writeInt( id );
        out.writeUTF( role );
        out.writeObject(address);
    }
    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        this.id = in.readInt();
        this.address = (Address) in.readObject();
        this.role = in.readUTF();
    }
}

public class CustomClass_Serialization {
    static String serFilename = "D:/serializable_CustomClass.ser";

    public static void main(String[] args) throws IOException {
        Address add = new Address();
        add.country = "IND";

        User obj = new User("SE");
        obj.id = 7;
        obj.address = add;

        // Serialization
        objects_serialize(obj, serFilename);
        objects_deserialize(obj, serFilename);

        // Externalization
        objects_WriteRead_External(obj, serFilename);
    }

    public static void objects_serialize( User obj, String serFilename ) throws IOException{
        FileOutputStream fos = new FileOutputStream( new File( serFilename ) );
        ObjectOutputStream objectOut = new ObjectOutputStream( fos );

        // java.io.NotSerializableException: com.github.objects.Address
        objectOut.writeObject( obj );
        objectOut.flush();
        objectOut.close();
        fos.close();

        System.out.println("Data Stored in to a file");
    }
    public static void objects_deserialize( User obj, String serFilename ) throws IOException{
        try {
            FileInputStream fis = new FileInputStream( new File( serFilename ) );
            ObjectInputStream ois = new ObjectInputStream( fis );
            Object readObject;
            readObject = ois.readObject();
            String calssName = readObject.getClass().getName();
            System.out.println("Restoring Class Name : "+ calssName); // InvalidClassException

            User user = (User) readObject;
            System.out.format("Obj[Id:%d, Role:%s] \n", user.id, user.role);

            Address add = (Address) user.address;
            System.out.println("Inner Obj : "+ add.country );
            ois.close();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    public static void objects_WriteRead_External( User obj, String serFilename ) throws IOException {
        FileOutputStream fos = new FileOutputStream(new File( serFilename ));
        ObjectOutputStream objectOut = new ObjectOutputStream( fos );

        obj.writeExternal( objectOut );
        objectOut.flush();

        fos.close();

        System.out.println("Data Stored in to a file");

        try {
            // create a new instance and read the assign the contents from stream.
            User user = new User();

            FileInputStream fis = new FileInputStream(new File( serFilename ));
            ObjectInputStream ois = new ObjectInputStream( fis );

            user.readExternal(ois);

            System.out.format("Obj[Id:%d, Role:%s] \n", user.id, user.role);

            Address add = (Address) user.address;
            System.out.println("Inner Obj : "+ add.country );
            ois.close();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

@見る


7

シリアライゼーションプロセスのパフォーマンスを最適化するために、Externalizableインターフェイスは実際には提供されていません。ただし、独自のカスタム処理を実装する手段を提供し、オブジェクトとそのスーパータイプのストリームの形式と内容を完全に制御できるようにします。

この例は、ネットワーク経由でネイティブアクションスクリプトオブジェクトを転送するためのAMF(ActionScript Message Format)リモート処理の実装です。


7

https://docs.oracle.com/javase/8/docs/platform/serialization/spec/serialTOC.html

デフォルトのシリアル化はいくぶん冗長であり、シリアル化されたオブジェクトの可能な最も広い使用シナリオを想定しているため、デフォルトの形式(Serializable)は、結果のストリームにシリアル化されたオブジェクトのクラスに関する情報を注釈します。

外部化により、オブジェクトストリームのプロデューサーは、クラスの必要最小限の識別(例:名前)を超えて、正確なクラスメタデータ(存在する場合)を完全に制御できます。これは、オブジェクトストリームのプロデューサーとそのコンシューマー(ストリームからオブジェクトを具体化する)が一致し、クラスに関する追加のメタデータが目的を果たさず、パフォーマンスを低下させる、閉じた環境などの特定の状況では明らかに望ましいです。

さらに、(Uriが指摘しているように)外部化により、Java型に対応するストリーム内のデータのエンコードを完全に制御することもできます。(不自然な)例の場合、ブール値のtrueを「Y」、falseを「N」として記録できます。外部化はそれを可能にします。


2

パフォーマンスを改善するためのオプションを検討するときは、カスタムのシリアル化を忘れないでください。Javaに、それがうまく機能していること、または少なくとも十分に無料で実行させ、Javaがうまく機能していないことに対してカスタムサポートを提供することができます。これは通常、Externalizableの完全サポートよりもはるかに少ないコードです。


2

SerializableとExternalizableの間には非常に多くの違いがありますが、カスタムSerializable(overrided writeObject()&readObject())とExternalizableの違いを比較すると、カスタム実装が、Externalizableの場合のように、ObjectOutputStreamクラスと緊密にバインドされていることがわかります。 ObjectOutputStreamクラスであるか、org.apache.mina.filter.codec.serialization.ObjectSerializationOutputStreamのようなObjectOutputの実装を提供する

外部化可能インターフェースの場合

@Override
public void writeExternal(ObjectOutput out) throws IOException {
    out.writeUTF(key);
    out.writeUTF(value);
    out.writeObject(emp);
}

@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
    this.key = in.readUTF();
    this.value = in.readUTF();
    this.emp = (Employee) in.readObject();
}





**In case of Serializable interface**


        /* 
             We can comment below two method and use default serialization process as well
             Sequence of class attributes in read and write methods MUST BE same.
        // below will not work it will not work . 
        // Exception = java.io.StreamCorruptedException: invalid type code: 00\
              private void writeObject(java.io.ObjectOutput stream) 
              */
            private void writeObject(java.io.ObjectOutputStream Outstream)
                    throws IOException {

                System.out.println("from writeObject()");
                /*     We can define custom validation or business rules inside read/write methods.
 This way our validation methods will be automatically 
    called by JVM, immediately after default serialization 
    and deserialization process 
    happens.
                 checkTestInfo();
                */

                stream.writeUTF(name);
                stream.writeInt(age);
                stream.writeObject(salary);
                stream.writeObject(address);
            }

            private void readObject(java.io.ObjectInputStream Instream)
                    throws IOException, ClassNotFoundException {
                System.out.println("from readObject()");
                name = (String) stream.readUTF();
                age = stream.readInt();
                salary = (BigDecimal) stream.readObject();
                address = (Address) stream.readObject();
                // validateTestInfo();
            }

わかりやすく説明するためにサンプルコードを追加しました。Externalizableのオブジェクトケースをチェックイン/チェックアウトしてください。これらは、どの実装にも直接バインドされていません。
Outstream / Instreamがクラスにしっかりとバインドされている場合。ObjectOutputStream / ObjectInputStreamを拡張できますが、使用するのが少し難しくなります。


1
もっと詳しく説明してもらえますか?読んでも何を言っているのかよくわかりません。また、いくつかの段落と例を使用してテキストの書式を設定できれば、これは素晴らしい答えになるでしょう。
Shirkam 2017年

0

基本的にSerializableは、クラスがシリアル化に対して安全であり、JVMがクラスのシリアル化方法を決定することを意味するマーカーインターフェイスです。Externalizable2つのメソッドが含まれ、readExternalそしてwriteExternalExternalizableオブジェクトをシリアル化する方法を実装者が決定できるようにしますSerializable。デフォルトでは、オブジェクトをシリアル化します。


0

いくつかの違い:

  1. シリアライゼーションの場合、JVMはリフレクションAPIを使用して同じように構築するので、オブジェクトはそのクラスのデフォルトコンストラクターの必要はありません。引数のない外部化コンストラクターが必要なのは、コントロールがプログラマーの手元にあり、後で逆シリアル化されたデータをセッターを介してオブジェクトに割り当てるためです。

  2. シリアル化では、ユーザーが特定のプロパティをスキップしてシリアル化する必要がある場合、そのプロパティを一時的としてマークする必要があります。その逆は、外部化には必要ありません。

  3. どのクラスでも下位互換性のサポートが期待される場合は、Externalizableを使用することをお勧めします。シリアル化はdefaultObjectの永続化をサポートしており、オブジェクト構造が壊れている場合、逆シリアル化中に問題が発生します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.