日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 默认站点!

默认站点

當(dāng)前位置: 首頁 >

如何使用可外部化的接口在Java中自定义序列化

發(fā)布時(shí)間:2023/12/3 33 豆豆
默认站点 收集整理的這篇文章主要介紹了 如何使用可外部化的接口在Java中自定义序列化 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

在上一篇文章“用示例介紹的有關(guān)Java序列化的一切”中 ,我解釋了如何使用以下方法序列化/反序列化一個(gè)對(duì)象
Serializable接口,還說明了如何使用writeObject和readObject方法自定義序列化過程。

Java序列化過程的缺點(diǎn)

但是,這些定制還不夠,因?yàn)镴VM可以完全控制序列化過程,而這些定制邏輯只是默認(rèn)序列化過程的補(bǔ)充。 我們?nèi)匀槐仨毻ㄟ^從writeObject和ObjectInputStream.defaultReadObject()調(diào)用ObjectOutputStream.defaultWriteObject()和ObjectInputStream.defaultReadObject()來使用默認(rèn)的序列化邏輯。
readObject方法。 如果不調(diào)用這些默認(rèn)方法,我們的對(duì)象將不會(huì)被序列化/反序列化。

默認(rèn)的序列化過程是完全遞歸的。 因此,每當(dāng)我們嘗試序列化一個(gè)對(duì)象時(shí),序列化過程都會(huì)嘗試使用我們的類( static和static除外)對(duì)所有字段(原始和引用)進(jìn)行序列化。
transient場(chǎng))。 這使得序列化過程非常緩慢。

現(xiàn)在,我們假設(shè)我們有一個(gè)對(duì)象,其中包含很多字段,由于某些原因,我們不想序列化這些字段(這些字段將始終分配有默認(rèn)值)。 在默認(rèn)的序列化過程中,我們將不得不使所有這些字段都是瞬態(tài)的,但是它仍然不會(huì)高效,因?yàn)閷⑦M(jìn)行大量檢查以查看這些字段是否為瞬態(tài)的。

因此,如我們所見,使用默認(rèn)序列化過程有很多弊端,例如:

  • 序列化的定制是不夠的,因?yàn)镴VM可以完全控制序列化過程,而我們的定制邏輯只是默認(rèn)序列化過程的補(bǔ)充。
  • 默認(rèn)序列化過程是完全遞歸且緩慢的。
  • 為了不對(duì)字段進(jìn)行序列化,我們必須聲明它為瞬態(tài),而很多瞬態(tài)字段將再次使過程變慢。
  • 我們無法控制如何對(duì)字段進(jìn)行序列化和反序列化。
  • 默認(rèn)序列化過程在創(chuàng)建對(duì)象時(shí)不會(huì)調(diào)用構(gòu)造函數(shù),因此它無法調(diào)用構(gòu)造函數(shù)提供的初始化邏輯。
  • 什么是外部化和外部化接口

    正如我們?cè)谏厦婵吹降?#xff0c;默認(rèn)的Java序列化效率不高。 我們可以通過使用Externalizable接口而不是
    Serializable接口。

    我們可以通過實(shí)現(xiàn)
    可外部化的接口并覆蓋它的方法writeExternal()和
    readExternal() 。 但是使用這種方法,我們將無法從JVM獲得任何類型的默認(rèn)序列化邏輯,而是由我們來提供完整的序列化和反序列化邏輯。

    因此,非常仔細(xì)地對(duì)測(cè)試這些方法進(jìn)行編碼非常有必要,因?yàn)檫@可能會(huì)破壞序列化過程。 但是,如果正確實(shí)現(xiàn),與默認(rèn)序列化過程相比,外部化過程非常快。

    我們將以下面的Employee類對(duì)象為例進(jìn)行說明:

    // Using Externalizable, complete serialization/deserialization logic becomes our responsibility, // We need to tell what to serialize using writeExternal() method and what to deserialize using readExternal(), // We can even serialize/deserialize static and transient variables, // With implementation of writeExternal() and readExternal(), methods writeObject() and readObject() becomes redundant and they do not get called. Employee class implements Externalizable { // This serialVersionUID field is necessary for Serializable as well as Externalizable to provide version control, // Compiler will provide this field if we do not provide it which might change if we modify class structure of our class, and we will get InvalidClassException, // If we provide a value to this field and do not change it, serialization-deserialization will not fail if we change our class structure. private static final long serialVersionUID = 2L; private String firstName; private transient String lastName; // Using Externalizable, we can even serialize/deserialize transient variables, so declaring fields transient becomes unnecessary. private int age; private static String department; // Using Externalizable, we can even serialize/deserialize static variables according to our need. // Mandatory to have to make our class Externalizable // When an Externalizable object is reconstructed, the object is created using public no-arg constructor before the readExternal method is called. // If a public no-arg constructor is not present then a InvalidClassException is thrown at runtime. public Employee() { } // All-arg constructor to create objects manually public Employee(String firstName, String lastName, int age, String department) { this .firstName = firstName; this .lastName = lastName; this .age = age; Employee.department = department; validateAge(); } private void validateAge() { System.out.println( "Validating age." ); if (age < 18 || age > 70 ) { throw new IllegalArgumentException( "Not a valid age to create an employee" ); } } @Override // We need to tell what to serialize in writeExternal() method public void writeExternal(ObjectOutput out) throws IOException { System.out.println( "Custom externalizable serialization logic invoked." ); out.writeUTF(firstName); out.writeUTF(lastName); out.writeInt(age); out.writeUTF(department); } @Override // We need to tell what to deserialize in readExternal() method // The readExternal method must read the values in the same sequence and with the same types as were written by writeExternal public void readExternal(ObjectInput in) throws IOException { System.out.println( "Custom externalizable serialization logic invoked." ); firstName = in.readUTF(); lastName = in.readUTF(); age = in.readInt(); department = in.readUTF(); validateAge(); } @Override public String toString() { return String.format( "Employee {firstName='%s', lastName='%s', age='%s', department='%s'}" , firstName, lastName, age, department); } // Custom serialization logic, It will be called only if we have implemented Serializable instead of Externalizable. private void writeObject(ObjectOutputStream oos) throws IOException { System.out.println( "Custom serialization logic invoked." ); } // Custom deserialization logic, It will be called only if we have implemented Serializable instead of Externalizable. private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { System.out.println( "Custom deserialization logic invoked." ); } }

    序列化如何與可外部化接口一起工作

    如上面在示例Employee類中所見,我們可以通過實(shí)現(xiàn)Externalizable接口并覆蓋其方法writeExternal()和readExternal()來編寫自己的序列化邏輯。

    通過調(diào)用DataOutput方法的原始值或調(diào)用ObjectOutput對(duì)象的writeObject方法的對(duì)象,字符串和數(shù)組,該對(duì)象可以實(shí)現(xiàn)writeExternal方法來保存其內(nèi)容。

    通過調(diào)用原始類型的DataInput方法和對(duì)象,字符串和數(shù)組的readObject方法,該對(duì)象可以實(shí)現(xiàn)readExternal方法以恢復(fù)其內(nèi)容。 readExternal方法必須按與writeExternal相同的順序和相同的類型讀取值。

    // We need to tell what fields to serialize in writeExternal() method public void writeExternal(ObjectOutput out) throws IOException { System.out.println( "Custom externalizable serialization logic invoked." ); out.writeUTF(firstName); out.writeUTF(lastName); out.writeInt(age); out.writeUTF(department); } // We need to tell what fields to deserialize in readExternal() method // The readExternal method must read the values in the same sequence and with the same types as were written by writeExternal public void readExternal(ObjectInput in) throws IOException { System.out.println( "Custom externalizable serialization logic invoked." ); firstName = in.readUTF(); lastName = in.readUTF(); age = in.readInt(); department = in.readUTF(); validateAge(); }

    要將對(duì)象序列化和反序列化為文件,我們需要遵循與Serializable示例相同的過程,這意味著調(diào)用
    如以下代碼所示,完成ObjectOutputStream.writeObject()和ObjectInputStream.readObject() :

    public class ExternalizableExample { public static void main(String[] args) throws IOException, ClassNotFoundException { Employee empObj = new Employee( "Shanti" , "Sharma" , 25 , "IT" ); System.out.println( "Object before serialization => " + empObj.toString()); // Serialization serialize(empObj); // Deserialization Employee deserializedEmpObj = deserialize(); System.out.println( "Object after deserialization => " + deserializedEmpObj.toString()); } // 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(); } } }

    Externalizable接口是Serializable的子接口,即
    Externalizable extends Serializable 。 因此,如果我們實(shí)現(xiàn)Externalizable接口并覆蓋其writeExternal()和
    然后,將使用readExternal()方法優(yōu)先于這些方法,而不是由JVM提供的默認(rèn)序列化機(jī)制。 這些方法取代了writeObject和readObject方法的定制實(shí)現(xiàn),因此,如果我們還提供writeObject()和readObject() ,則將忽略它們。

    在序列化過程中,將針對(duì)要序列化的每個(gè)對(duì)象的Externalizable接口進(jìn)行測(cè)試。 如果對(duì)象支持Externalizable,則調(diào)用writeExternal方法。 如果對(duì)象不支持Externalizable并且實(shí)現(xiàn)了Serializable,則使用ObjectOutputStream保存該對(duì)象。

    重建Externalizable對(duì)象時(shí),將使用公共no-arg構(gòu)造函數(shù)創(chuàng)建一個(gè)實(shí)例,然后調(diào)用readExternal方法。 可序列化的對(duì)象通過從ObjectInputStream讀取來恢復(fù)。

  • 重建Externizable對(duì)象時(shí),在調(diào)用readExternal方法之前,使用公共的無參數(shù)構(gòu)造函數(shù)創(chuàng)建對(duì)象。 如果不存在公共的無參數(shù)構(gòu)造函數(shù),則在運(yùn)行時(shí)引發(fā)InvalidClassException。
  • 使用Externalizable,我們甚至可以序列化/反序列化瞬態(tài)變量,因此無需聲明字段瞬態(tài)。
  • 使用Externalizable,我們甚至可以根據(jù)需要對(duì)靜態(tài)變量進(jìn)行序列化/反序列化。
  • Externalizable實(shí)例可以通過Serializable接口中記錄的writeReplace和readResolve方法指定替換對(duì)象。

    Java 序列化還可以用于深度克隆對(duì)象 。 Java克隆是Java社區(qū)中最有爭(zhēng)議的話題,它的確有其缺點(diǎn),但是在對(duì)象完全滿足Java克隆的強(qiáng)制條件之前,它仍然是創(chuàng)建對(duì)象副本的最流行和最簡(jiǎn)單的方法。 我在3篇文章的Java克隆系列中詳細(xì)介紹了克隆 ,其中包括Java克隆和克隆類型(淺和深)等文章, 并帶有示例 , Java克隆–復(fù)制構(gòu)造器與克隆 , Java克隆–甚至復(fù)制構(gòu)造器都不是如果您想了解更多有關(guān)克隆的知識(shí),請(qǐng)充分閱讀它們。

    可外部化與可序列化之間的差異

    讓我們列出Java中Externalizable和Serializable接口之間的主要區(qū)別。

    您可以在此找到本文的完整源代碼。
    Github存儲(chǔ)庫 ,請(qǐng)隨時(shí)提供寶貴的反饋。

    翻譯自: https://www.javacodegeeks.com/2019/08/customize-serialization-java-using-externalizable-interface.html

    總結(jié)

    以上是默认站点為你收集整理的如何使用可外部化的接口在Java中自定义序列化的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得默认站点網(wǎng)站內(nèi)容還不錯(cuò),歡迎將默认站点推薦給好友。