Java序列化魔术方法及其示例使用
在上一篇文章中, 您需要了解有關Java序列化的所有知識 ,我們討論了如何通過實現Java序列化來啟用類的可序列化性。
Serializable接口。 如果我們的類未實現Serializable接口,或者該類具有對非Serializable類的引用,則JVM將拋出NotSerializableException 。
可序列化類的所有子類型本身都是可序列化的,并且
Externalizable接口還擴展了可序列化。 所以即使我們
使用Externalizable自定義序列化過程,我們的類仍然是 Serializable 。
Serializable接口是一個沒有方法或字段的標記接口,它的作用類似于JVM的標志。 ObjectInputStream和ObjectOutputStream類提供的Java序列化過程完全由JVM控制。
但是,如果我們想添加一些其他邏輯來增強此正常過程,例如,我們可能希望在對敏感信息進行序列化/反序列化之前對其進行加密/解密。 Java為此提供了一些其他方法,我們將在此博客中討論。
writeObject和readObject方法
希望自定義或添加一些其他邏輯以增強常規序列化/反序列化過程的可序列化類應提供
具有以下確切簽名的writeObject和readObject方法:
- private void writeObject(java.io.ObjectOutputStream out) throws IOException
- private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException
在Java序列化您需要了解的一切文章下,已經對這些方法進行了詳細討論。
readObjectNoData方法
如Serializable類的Java文檔所述,如果我們要在序列化流未將給定類列出為要反序列化的對象的超類的情況下初始化其特定類的對象狀態,則應提供writeObject和具有以下確切簽名的readObject方法:
- private void readObjectNoData() throws ObjectStreamException
在接收方使用與發送方不同的反序列化實例類的版本,并且接收方的版本擴展了發送方的版本未擴展的類的情況下,可能會發生這種情況。 如果序列化流已被篡改,也會發生這種情況。 因此,盡管源流“充滿敵意”或不完整,但readObjectNoData對于正確初始化反序列化的對象很有用。
每個可序列化的類都可以定義自己的readObjectNoData方法。 如果可序列化的類未定義readObjectNoData方法,則在上述情況下,該類的字段將被初始化為其默認值。
writeReplace和readResolve方法
將對象寫入流時需要指定要使用的替代對象的可序列化類應為此特殊方法提供確切的簽名:
- ANY-ACCESS-MODIFIER Object writeReplace() throws ObjectStreamException
當從流中讀取實例時,需要指定替換的Serializable類應為此特殊方法提供確切的簽名:
- ANY-ACCESS-MODIFIER Object readResolve() throws ObjectStreamException
基本上, writeReplace方法允許開發人員提供將被序列化的替換對象,而不是原始對象。 在反序列化過程中使用了readResolve方法,用我們選擇的另一個方法來替換反序列化的對象。
writeReplace和readResolve方法的主要用途之一是使用Serialized類實現單例設計模式。 我們知道, 反序列化過程每次都會創建一個新對象 ,它也可以用作深度克隆對象的方法,如果我們必須使類為單例,那么這樣做就不好了。
您可以在Java Cloning和Java上閱讀有關Java克隆和序列化的更多信息。
Java序列化主題。
在readObject返回之后調用readResolve方法(相反,在writeObject之前(可能在另一個對象上)調用writeReplace )。 該對象在方法返回替換this返回到的用戶對象ObjectInputStream.readObject并流中的對象中的任何進一步的反向引用。 我們可以使用writeReplace方法將序列化對象替換為null,以便不進行序列化,然后使用readResolve方法將反序列化的對象替換為單例實例。
validateObject方法
如果我們想在某些字段上執行某些驗證,則可以通過實現ObjectInputValidation接口并重寫
來自它的validateObject方法。
當我們通過從readObject方法調用ObjectInputStream.registerValidation(this, 0)注冊此驗證時,將自動調用validateObject方法。 在將數據流交還給您的應用程序之前,驗證數據流是否受到篡改或數據有意義是非常有用的。
下面的示例涵蓋了上述所有方法的代碼
public class SerializationMethodsExample { public static void main(String[] args) throws IOException, ClassNotFoundException { Employee emp = new Employee( "Naresh Joshi" , 25 ); System.out.println( "Object before serialization: " + emp.toString()); // Serialization serialize(emp); // Deserialization Employee deserialisedEmp = deserialize(); System.out.println( "Object after deserialization: " + deserialisedEmp.toString()); System.out.println(); // This will print false because both object are separate System.out.println(emp == deserialisedEmp); System.out.println(); // This will print false because both `deserialisedEmp` and `emp` are pointing to same object, // Because we replaced de-serializing object in readResolve method by current instance System.out.println(Objects.equals(emp, deserialisedEmp)); } // Serialization code static void serialize(Employee empObj) throws IOException { try (FileOutputStream fos = new FileOutputStream( "data.obj" ); ObjectOutputStream oos = new ObjectOutputStream(fos)) { oos.writeObject(empObj); } } // Deserialization code static Employee deserialize() throws IOException, ClassNotFoundException { try (FileInputStream fis = new FileInputStream( "data.obj" ); ObjectInputStream ois = new ObjectInputStream(fis)) { return (Employee) ois.readObject(); } } } Employee class implements Serializable, ObjectInputValidation { private static final long serialVersionUID = 2L; private String name; private int age; public Employee(String name, int age) { this .name = name; this .age = age; } // With ObjectInputValidation interface we get a validateObject method where we can do our validations. @Override public void validateObject() { System.out.println( "Validating age." ); if (age < 18 || age > 70 ) { throw new IllegalArgumentException( "Not a valid age to create an employee" ); } } // Custom serialization logic, // This will allow us to have additional serialization logic on top of the default one eg encrypting object before serialization. private void writeObject(ObjectOutputStream oos) throws IOException { System.out.println( "Custom serialization logic invoked." ); oos.defaultWriteObject(); // Calling the default serialization logic } // Replacing de-serializing object with this, private Object writeReplace() throws ObjectStreamException { System.out.println( "Replacing serialising object by this." ); return this ; } // Custom deserialization logic // This will allow us to have additional deserialization logic on top of the default one eg performing validations, decrypting object after deserialization. private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { System.out.println( "Custom deserialization logic invoked." ); ois.registerValidation( this , 0 ); // Registering validations, So our validateObject method can be called. ois.defaultReadObject(); // Calling the default deserialization logic. } // Replacing de-serializing object with this, // It will will not give us a full proof singleton but it will stop new object creation by deserialization. private Object readResolve() throws ObjectStreamException { System.out.println( "Replacing de-serializing object by this." ); return this ; } @Override public String toString() { return String.format( "Employee {name='%s', age='%s'}" , name, age); } } 您可以在此找到本文的完整源代碼。
Github存儲庫 ,請隨時提供寶貴的反饋。
翻譯自: https://www.javacodegeeks.com/2019/09/java-serialization-magic-methods-and-their-uses-with-example.html
總結
以上是生活随笔為你收集整理的Java序列化魔术方法及其示例使用的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 1吨等于多少公斤 1吨是几公斤
- 下一篇: 工业三废是指 工业三废是指什么