Java のシリアル化プロセスは次のとおりです:
Java の逆シリアル化プロセスは次のとおりです:
注: すべてのクラスをシリアル化する必要があるわけではありません。主な理由は 2 つあります
1) 秘密の質問。 Java の一部のクラスは機密クラスであり、この種のオブジェクト データは外部に公開するのが不便であり、一般的にはデータの安全性が保証されません。オブジェクトはシリアル化されません。
2) リソースの問題。オブジェクトはシリアル化されたバイト ストリームを使用して作成できますが、この作成には制限がありません。作成しすぎると大きなリソースの問題が発生する可能性があるため、そのようなオブジェクトはシリアル化には適していません。
Serializable
Serializable は、Java によって提供されるシリアル化インターフェイスであり、オブジェクトの標準的なシリアル化および逆シリアル化操作を提供する空のインターフェイスです。
シリアル化プロセス:
Person p = new Person("name","id"); File file = new File("cache.txt"); FileOutputStream output = new FileOutputStream(file); ObjectOutputStream objectOutputStream = new ObjectOutputStream(output); objectOutputStream.writeObject(p); output.close(); objectOutputStream.close();
逆シリアル化プロセス:
File file = new File("cache.txt"); FileInputStream input= new FileInputStream(file); ObjectInputStream objectInputStream = new ObjectInputStream(input); Person p = (Person)objectInputStream.readObject(); System.out.println(p.getName()+"---"+p.getId()); input.close(); objectInputStream.close();
シリアル化する必要があるクラスメンバー
オブジェクトがシリアル化されるとき、ストレージやストレージを節約するために、すべてのメンバーをバイナリバイトシーケンスに変換する必要はありません。スペースを転送してシリアル化効率を向上させるために、一部の不要なメンバーをシリアル化する必要はありません。これらには次のものが含まれます:
静的変数。静的変数はクラスの属性に属し、特定のインスタンスに属さないため、シリアル化解除中にシリアル化する必要はなく、クラスの静的メンバー参照を直接取得できます。
方法。メソッドは単なる操作の集合であり、オブジェクトに依存せず、逆シリアル化時にメソッド情報をクラスから直接取得することもできます。
継承関係のシリアル化
親クラスがSerializableを実装すると、子クラスがシリアル化され、親クラスもシリアル化されます。
親クラスがSerializableを実装していない場合、サブクラスはシリアル化され、親クラスはシリアル化されません
参照関係のシリアル化
Serializableを実装しているクラスがシリアル化されている場合は、同時にシリアル化されます。参照クラスはシリアル化操作を実行します。参照クラスが Serializable インターフェースを実装していない場合、JVM は java.io.NotSerializableExeception.
class Person implements Serializable{ private String name; private Tool tool = new Tool(); } class Tool implements Serializable{ }
をスローします。 このとき、person クラスがシリアル化されていれば、Tool クラスも同時にシリアル化されます。 Tool クラスが Serializable インターフェイスを実装していない場合、例外がスローされます。
機密データの保護:
シリアル化識別子を使用してクラスが追加されると、このクラスのオブジェクトのすべての属性情報がシリアル化され、ローカルに保存されるか、ネットワーク経由で送信されます。その場合、オブジェクト内の一部のフィールドが機密情報であるため、公開すべきではない場合があります。シリアル化されている場合は、簡単に解読される可能性があり、一般的なパスワード フィールドなどのセキュリティ リスクが発生します。
Javaには、transientキーワードであるキーワードtransientが用意されています。このキーワードは、保護された情報がシリアル化によって公開されないように、フィールドのシリアル化をオフにします。
シリアル化識別 ID
次のシナリオを想像してください。何らかの理由で、両端がシリアル化されたオブジェクトを送信していると、受信側のクラスのフィールドが削除されているとします。送信者がオブジェクトのシリアル化されたバイト ストリームを受信者に送信すると、受信者のクラスにいくつかのフィールドが欠落しているため、解析できません。
Javaでは、シリアル化インターフェイスを実装するクラスがserialVersionUID静的属性を宣言する必要があります。そのような属性がない場合、JVMは自動的に属性を宣言し、その属性に値を割り当てます(クラスの作成時に異なる値が割り当てられます)。変わります)。この属性の値は一意であり、さまざまなシリアル化クラスを識別するために使用されます。クラスのシリアル化識別子がまったく同じである場合にのみ、Java はシリアル化解除作業を実行します。これがシリアル化識別子の役割です。
前述のシナリオでは、serialVersionUIDが手動で宣言されていないと仮定すると、JVMは送信者と受信者が使用するクラスのserialVersionUIDに異なる値を割り当て、逆シリアル化は失敗します。 SerialVersionUID が手動で割り当てられている場合、クラスのフィールドが変更されても逆シリアル化が成功する可能性があります。
カスタム シリアル化戦略
カスタム シリアル化戦略
Java は、シリアル化および逆シリアル化中の対応する処理にカスタマイズされたメソッドを使用できるようにする一連の効果的なメカニズムを提供します。送信側がシリアル化戦略に同意した後は、この一連の戦略を実装するために送信する必要がある一連のメソッドをシリアル化クラスに追加するだけで、シリアル化中、これらの指定されたメソッドがシリアル化および逆シリアル化のために自動的に呼び出されます。メソッドは以下の通りです:
1) private void writeObject(ObjectOutputSteam out) throws IOException
在方法的内部有重要的代码:out.defaultWriteObject() //将对象数据以默认方式写入到输出流中
2)private void readObject(ObjectInputStream in) throws IOException,ClassNotFoundException
同样的,此方法内部也有相似代码:in.defaultReadObject(); //以默认方式从输入流中恢复对象
这两个方法的作用分别是将特定的对象写入到输出流中以及从输入流中恢复特定的对象,通过这两个方法,用户即可实现自定义的序列化。当在实现Serializable接口的类中写了上面两个方法之后,序列化或反序列化该类时则会通过反射来调用这两个方法,从而实现自定义序列化。
限制序列化对象的数量
我们看下面的单例模式:
public class Singleton implements Serializable { private volatile static Singleton mInstance; private Singleton() { } public static Singleton getInstance() { if (mInstance == null) { synchronized (Singleton.class) { if (mInstance == null) { mInstance = new Singleton(); } } } return mInstance; } }
此时通过反序列化获取实例,则单例模式会失效。那该如何解决这个问题呢?
Java有一种机制,可以让我们在序列化和反序列化时,可以根据自己的需要,写入或读取指定的实例。使用这种机制,需要在实现Serializable接口的类中添加两个方法:
private Object readResolve() //如果用户在序列化类中添加了该方法,则在进行反序列化时,使用该方法返回的对象,作为反序列化对象。
private Object writeReplace() //如果用户在序列化类中添加了该方法,则在进行序列化时,序列化该类返回的对象。
再看使用了该机制的单例模式:
public class Singleton implements Serializable { private volatile static Singleton mInstance; private Singleton() { } public static Singleton getInstance() { if (mInstance == null) { synchronized (Singleton.class) { if (mInstance == null) { mInstance = new Singleton(); } } } return mInstance; } private Object readResolve() { return getInstance(); } private Object writeReplace() { return getInstance(); } }
此时的通过反序列化得到的对象也是同一个,即单例模式依然有效!
相关文章:
Java序列化Serializable和Externalizable区别的示例代码