Javaの Serializable
と Externalizable
の違いは何ですか?
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日の遺物です。本当にそれの必要はもうありません。
シリアル化は、オブジェクトを保存して後で再作成するデフォルトの機能を提供します。詳細形式を使用して、保存するオブジェクトのグラフ全体を定義します。 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;
それが役に立てば幸い。間違いを犯した場合は謝罪します。ありがとう。
完全を期すために、transient
キーワードも2つの間のギャップを埋めます。
オブジェクトの一部のみをシリアル化する場合は、特定のフィールドをtransient
として設定し、永続化しないようにマークして、Serializable
を実装します。
Serializable
とExternalizable
の主な違い
Serializable
はメソッドのないマーカーインターフェイスです。 Externalizable
インターフェイスには、writeExternal()
とreadExternal()
の2つのメソッドが含まれています。Serializable
インターフェイスを実装するクラスのデフォルトのシリアル化プロセスが開始されます。 Externalizable
インターフェイスを実装するクラスのプログラマ定義のシリアル化プロセスが開始されます。Externalizable
インターフェイスで完全に制御できます。オブジェクトのさまざまなバージョンをサポートできます。 Externalizable
を実装する場合は、super
クラスをシリアル化する必要がありますSerializable
はリフレクションを使用してオブジェクトを構築し、引数コンストラクターを必要としません。ただし、Externalizable
は、引数のないパブリックコンストラクターを要求します。詳細については、Hitesh Garg
による blog を参照してください。
シリアル化では、特定のデフォルト動作を使用してオブジェクトを保存し、後で再作成します。参照や複雑なデータ構造をどの順序で処理するか、どのように処理するかを指定できますが、最終的には各プリミティブデータフィールドに対してデフォルトの動作を使用することになります。
外部化は、データフィールドにデフォルトのシリアル化メカニズムを使用せずに、まったく異なる方法でオブジェクトを実際に保存および再構築したいというまれなケースで使用されます。たとえば、独自のエンコードおよび圧縮スキームがあると想像してください。
オブジェクトのシリアル化では、SerializableおよびExternalizableインターフェイスを使用します。Javaオブジェクトはシリアル化のみ可能です。クラスまたはそのスーパークラスのいずれかがJava.io.SerializableインターフェースまたはそのサブインターフェースJava.io.Externalizableを実装する場合。 ほとんどのJavaクラスはシリアライズ可能です。
NotSerializableException
:packageName.ClassName
"シリアル化プロセスにクラスオブジェクトを参加させるには、クラスはSerializableまたはExternalizableインターフェイスを実装する必要があります。オブジェクトのシリアル化は、保存されているオブジェクトのJavaクラスに関する情報を含むストリームを生成します。直列化可能なオブジェクトの場合、クラスの実装の異なる(ただし互換性のある)バージョンが存在する場合でも、それらのオブジェクトを復元するのに十分な情報が保持されます。 Serializableインターフェイスは、シリアル化可能なプロトコルを実装するクラスを識別するために定義されています。
package Java.io;
public interface Serializable {};
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;
}
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();
}
}
}
@見る
https://docs.Oracle.com/javase/8/docs/platform/serialization/spec/serialTOC.html
デフォルトのシリアル化はやや冗長であり、シリアル化されたオブジェクトの可能な限り広い使用シナリオを想定しているため、デフォルトの形式(Serializable)は、シリアル化されたオブジェクトのクラスに関する情報で結果のストリームに注釈を付けます。
外部化により、オブジェクトストリームのプロデューサーは、クラスの最小限必要な識別(名前など)を超える正確なクラスメタデータ(存在する場合)を完全に制御できます。これは、オブジェクトストリームのプロデューサーとそのコンシューマー(ストリームからオブジェクトを具体化する)が一致し、クラスに関する追加のメタデータが目的を果たさず、パフォーマンスを低下させる閉鎖環境などの特定の状況で明らかに望ましいです。
さらに(Uriが指摘しているように)外部化は、Java型に対応するストリーム内のデータのエンコードを完全に制御することもできます。 (不自然な)例として、ブール値のtrueを「Y」、falseを「N」として記録できます。外部化により、それを行うことができます。
Externalizableインターフェイスは、シリアル化プロセスのパフォーマンスを最適化するために実際には提供されていませんでした!しかし、独自のカスタム処理を実装する手段を提供し、オブジェクトとそのスーパータイプのストリームの形式と内容を完全に制御できるようにします!
この例は、ネットワーク経由でネイティブアクションスクリプトオブジェクトを転送するためのAMF(ActionScriptメッセージフォーマット)リモーティングの実装です。
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を拡張できますが、使用するのは少し難しいでしょう。
パフォーマンスを改善するためのオプションを検討するときは、カスタムシリアル化を忘れないでください。 Javaに、それがうまく機能すること、または少なくとも十分なこと、無料を実行させ、ひどいことをカスタムサポートすることができます。これは通常、完全なExternalizableサポートよりもはるかに少ないコードです。
基本的に、Serializable
は、クラスがシリアル化しても安全であることを意味するマーカーインターフェイスであり、JVMはそのシリアル化方法を決定します。 Externalizable
には、readExternal
とwriteExternal
の2つのメソッドが含まれています。 Externalizable
を使用すると、実装者はオブジェクトをシリアル化する方法を決定できます。Serializable
はデフォルトの方法でオブジェクトをシリアル化します。