web-dev-qa-db-ja.com

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

Javaの SerializableExternalizable の違いは何ですか?

273
Sasha

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

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

Javaの最近のバージョン(確か1.3以降)では、リフレクションのパフォーマンスが以前よりも大幅に向上しているため、これはそれほど問題ではありません。最新のJVMでExternalizableから有意義なメリットを得ることに苦心していると思います。

また、組み込みのJavaシリアル化メカニズムだけがありません。JBossSerializationなどのサードパーティの代替品を入手できます。これはかなり高速で、デフォルトのドロップイン代替品です。

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

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

261
skaffman

シリアル化は、オブジェクトを保存して後で再作成するデフォルトの機能を提供します。詳細形式を使用して、保存するオブジェクトのグラフ全体を定義します。 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;

それが役に立てば幸い。間違いを犯した場合は謝罪します。ありがとう。

35
Trying

完全を期すために、transientキーワードも2つの間のギャップを埋めます。

オブジェクトの一部のみをシリアル化する場合は、特定のフィールドをtransientとして設定し、永続化しないようにマークして、Serializableを実装します。

24

SerializableExternalizableの主な違い

  1. Marker interfaceSerializableはメソッドのないマーカーインターフェイスです。 Externalizableインターフェイスには、writeExternal()readExternal()の2つのメソッドが含まれています。
  2. シリアル化プロセスSerializableインターフェイスを実装するクラスのデフォルトのシリアル化プロセスが開始されます。 Externalizableインターフェイスを実装するクラスのプログラマ定義のシリアル化プロセスが開始されます。
  3. メンテナンス互換性のない変更 はシリアル化を中断する可能性があります。
  4. 後方互換性と制御:複数のバージョンをサポートする必要がある場合は、Externalizableインターフェイスで完全に制御できます。オブジェクトのさまざまなバージョンをサポートできます。 Externalizableを実装する場合は、superクラスをシリアル化する必要があります
  5. public No-arg constructorSerializableはリフレクションを使用してオブジェクトを構築し、引数コンストラクターを必要としません。ただし、Externalizableは、引数のないパブリックコンストラクターを要求します。

詳細については、Hitesh Gargによる blog を参照してください。

21
Ravindra babu

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

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

21
Uri

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

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

enter image description here


シリアル化可能なインターフェイス

オブジェクトのシリアル化は、保存されているオブジェクトの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 Interface

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メソッドを実装して、オブジェクトの状態を保存/復元する必要があります。
  • プログラマは、シリアル化するオブジェクトを処理する必要があります。プログラマーがシリアル化の面倒を見るので、ここで一時的なキーワードはシリアル化プロセスのオブジェクトを制限しません。
  • 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(exposed)を書き込んだクラスのインスタンスフィールドはシリアル化されます。


"は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();
        }
    }
}

@見る

9
Yash

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

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

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

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

7
alphazero

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

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

7
Ali Joudeh

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

Externalizableインターフェイスの場合

@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を拡張できますが、使用するのは少し難しいでしょう。

2
Ashish Sharma

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

2
Ed Staub

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