欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java 內(nèi)置接口 Serializable示例詳解

 更新時(shí)間:2022年11月02日 15:20:13   作者:kevinyan  
這篇文章主要為大家介紹了Java 內(nèi)置接口 Serializable示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

引言

上一部分我們著重講了 Java 集合框架中在開發(fā)項(xiàng)目時(shí)經(jīng)常會(huì)被用到的數(shù)據(jù)容器,在講解、演示使用實(shí)踐的同時(shí),把這個(gè)過程中遇到的各種相關(guān)知識(shí)點(diǎn):泛型、Lambada、Stream 操作,一并給大家做了梳理。

從這篇開始我們進(jìn)入下一部分,用三到五部分給大家梳理一下,在用 Java 編程時(shí),那些我們繞不開的 interface;從最基本的 Serializable ComparableIterator 這些,再到 Java 為了支持函數(shù)式編程而提供的 FunctionPredicateinterface。

這些 Java 內(nèi)置提供的 interface 或多或少我們?cè)趯?Java 代碼的時(shí)候都見過,有的甚至是潛移默化地在日常編碼中已經(jīng)實(shí)現(xiàn)過其中的一些 interface,只不過我們沒有察覺到罷了。相信通過閱讀著幾篇文章,一定會(huì)讓你在寫 Java 代碼時(shí)更清楚自己是在做什么,不會(huì)再被這些個(gè)似曾相識(shí)的 interface 困擾到。

本文大綱如下:

Serializable 接口

作為 Java 中那些繞不開的內(nèi)置接口 這個(gè)小系列的開篇文章,首先要給大家介紹的 interface 是 Serializable。

Serializable這個(gè)接口的全限定名(包名 + 接口名)是 java.io.Serializable,這里給大家說個(gè)小技巧,當(dāng)你看到一個(gè)類或者接口的包名前綴里包含java.io那就證明這個(gè)類 / 接口它跟數(shù)據(jù)的傳輸有關(guān)。

Serializable 是 Java 中非常重要的一個(gè)接口,如果一個(gè)類的對(duì)象是可序列化的,即對(duì)象在程序里可以進(jìn)行序列化和反序列化,對(duì)象的類就一定要實(shí)現(xiàn)Serializable接口。那么為什么要進(jìn)行序列化和反序列化呢?

序列化的意思是將對(duì)象的狀態(tài)轉(zhuǎn)換為字節(jié)流;反序列化則相反。換句話說,序列化是將 Java 對(duì)象轉(zhuǎn)換為靜態(tài)字節(jié)流(序列),然后我們可以將其保存到文件、數(shù)據(jù)庫或者是通過通過網(wǎng)絡(luò)傳輸,反序列化則是在我們讀取到字節(jié)流后再轉(zhuǎn)換成 Java 對(duì)象的過程;這也正好解釋了為什么Serializable 接口會(huì)歸屬到java.io包下面。

Serializable 是一個(gè)標(biāo)記型接口

雖說需要進(jìn)行序列化的對(duì)象,它們的類都需要實(shí)現(xiàn) Serializable 接口,但其實(shí)你會(huì)發(fā)現(xiàn),我們?cè)谧屢粋€(gè)類實(shí)現(xiàn) Serializable 接口時(shí),并沒有額外實(shí)現(xiàn)過什么抽線方法。

import java.io.Serializable;
public class Person implements Serializable {
    private String name;
    private int age;
}

比如向上面?zhèn)€類文件里的內(nèi)容,Person 類聲明實(shí)現(xiàn) Serializable 接口后,并沒有去實(shí)現(xiàn)什么抽象方法,IDE 也不會(huì)用紅線警告提示我們:“你有一個(gè)抽象方法需要實(shí)現(xiàn)” ,原因是 Serializable 接口里并沒有聲明抽象方法。

public interface Serializable {
}

這種不包含任何方法的 interface 被稱為標(biāo)記型接口,類實(shí)現(xiàn) Serializable接口不必實(shí)現(xiàn)任何特定方法,它只起標(biāo)記作用,讓 Java 知道該類可以用于對(duì)象序列化。

serializable Version UID

雖說一個(gè)類實(shí)現(xiàn)了 Serializable 接口的時(shí)候不需要實(shí)現(xiàn)特定的方法,但是經(jīng)常會(huì)看到一些實(shí)現(xiàn)了Serializable的類中,都有一個(gè)名為serialVersionUID類型為long的私有靜態(tài) 屬性。

import java.io.Serializable;
public static class Person implements Serializable {
    private static final long serialVersionUID = -7792628363939354385L;
    public String name;
    public int    age;
}

該屬性修飾符里使用了final即賦值后不可更改。Java 的對(duì)象序列化 API 在從讀取到的字節(jié)序列中反序列化出對(duì)象時(shí),使用 serialVersionUID 這個(gè)靜態(tài)類屬性來判斷:是否序列化對(duì)象時(shí)使用了當(dāng)前相同版本的類進(jìn)行的序列化。Java 使用它來驗(yàn)證保存和加載的對(duì)象是否具有相同的屬性,確保在序列化上是兼容的。

大多數(shù)的 IDE 都可以自動(dòng)生成這個(gè) serialVersionUID靜態(tài)屬性的值,規(guī)則是基于類名、屬性和相關(guān)的訪問修飾符。任何更改都會(huì)導(dǎo)致不同的數(shù)字,并可能導(dǎo)致 InvalidClassException。 如果一個(gè)實(shí)現(xiàn) Serializable 的類沒有聲明 serialVersionUID,JVM 會(huì)在運(yùn)行時(shí)自動(dòng)生成一個(gè)。但是,強(qiáng)烈建議每個(gè)可序列化類都聲明 serialVersionUID,因?yàn)槟J(rèn)生成的serialVersionUID依賴于編譯器,因此可能會(huì)導(dǎo)致意外的InvalidClassExceptions。

我上面那個(gè)例子里,Person 類的serialVersionUID是用 Intelij IDEA 自動(dòng)生成的,所以值看起來一大串,不是我自己些的。IDEA 默認(rèn)不會(huì)給可序列化類自動(dòng)生成 serialVersionUID 需要安裝一個(gè)插件。

這里給大家放一個(gè)截圖,插件的安裝和使用,網(wǎng)上有很多例子,大家需要的話動(dòng)手搜一下,這里就不再占用太多篇幅講怎么安裝和使用這個(gè)插件了。

Java 序列化與JSON序列化的區(qū)別

Java 的序列化與現(xiàn)在互聯(lián)網(wǎng)上 Web 應(yīng)用交互數(shù)據(jù)常用的 JSON 序列化并不是一回事兒,這是咱們需要注意的,像 Java、C#、PHP 這些編程語言,都有自己的序列化機(jī)制把自家的對(duì)象序列化成字節(jié)然后進(jìn)行傳輸或者保存,但是這些語言的序列化機(jī)制之間并不能互認(rèn),即用 Java 把對(duì)象序列化成字節(jié)、通過網(wǎng)絡(luò) RESTful API 傳給一個(gè) PHP 開發(fā)的服務(wù),PHP 是沒辦法反序列化還原出這個(gè)對(duì)象的。這樣才有了 JSON、XML、Protocol Buffer 這樣的更通用的序列化標(biāo)準(zhǔn)。

例如在實(shí)際項(xiàng)目開發(fā)的時(shí)候,Java 對(duì)象往往被序列化為 JSON、XML 后再在網(wǎng)絡(luò)上傳輸,如果對(duì)數(shù)據(jù)大小敏感的場(chǎng)景,會(huì)把 Java 對(duì)象序列化成空間占用更小的一些二進(jìn)制格式,比如 Protocol Buffer ( 分布式 RPC 框架 gRPC 的數(shù)據(jù)交換格式)。這樣做的好處是序列化后的數(shù)據(jù)可以被非 Java 應(yīng)用程序讀取和反序列化,例如,在 Web 瀏覽器中運(yùn)行的 JavaScript 可以在本地將對(duì)象序列化成 JSON 傳輸給 Java 寫的 API 接口,也可以從 Java API接口返回響應(yīng)中的 JSON 數(shù)據(jù),反序列化成 JavaScript 本地的對(duì)象 。

像上面列舉的這些對(duì)象序列化機(jī)制,是不需要我們的 Java 類實(shí)現(xiàn) Serializable 接口的。這些 JSON、XML 等格式的序列化類,通常使用 Java 反射來檢查類,配合一些特定的注解完成序列化。

Java序列化相較于 JSON 的優(yōu)勢(shì)

上面介紹了 JSON 這樣的通用序列化格式的優(yōu)勢(shì),有的可能會(huì)問了,那還用 Java 序列化干啥。這里再給大家分析一下,Java 對(duì)象序列化雖然在通用性上不如 JSON 那些序列化格式,但是在 Java 生態(tài)內(nèi)部卻是十分好用的,其最聰明的一點(diǎn)是,它不僅能保存對(duì)象的副本,而且還會(huì)跟著對(duì)象里面的reference,把它所引用的對(duì)象也保存起來,然后再繼續(xù)跟蹤那些對(duì)象的reference,以此類推。

這個(gè)機(jī)制所涵蓋的范圍不僅包括對(duì)象的成員數(shù)據(jù),而且還包含數(shù)組里面的reference。如果你要自己實(shí)現(xiàn)對(duì)象序列化的話,那么編寫跟蹤這些鏈接的程序?qū)?huì)是一件非常痛苦的任務(wù)。但是,Java的對(duì)象序列化就能精確無誤地做到這一點(diǎn),毫無疑問,它的遍歷算法是做過優(yōu)化的。

另外你們?cè)谝恍┵Y料里看過 Java Bean 的定義

1、所有屬性為private

2、提供默認(rèn)構(gòu)造方法

3、提供getter和setter

4、實(shí)現(xiàn)java.io.Serializable接口

那么問題來了,為什么要進(jìn)行序列化?每個(gè)實(shí)體bean都必須實(shí)現(xiàn)serializabel接口嗎?以及我做項(xiàng)目的時(shí)候,沒有實(shí)現(xiàn)序列化,同樣沒什么影響,到底什么時(shí)候應(yīng)該進(jìn)行序列化操作呢?

這里轉(zhuǎn)載一個(gè)網(wǎng)上大佬對(duì)這個(gè)問題的解釋

首先第一個(gè)問題,實(shí)現(xiàn)序列化的兩個(gè)原因:

1、將對(duì)象的狀態(tài)保存在存儲(chǔ)媒體中以便可以在以后重新創(chuàng)建出完全相同的副本;

2、按值將對(duì)象從一個(gè)應(yīng)用程序域發(fā)送至另一個(gè)應(yīng)用程序域。實(shí)現(xiàn)serializabel接口的作用是就是可以把對(duì)象存到字節(jié)流,然后可以恢復(fù),所以你想如果你的對(duì)象沒實(shí)現(xiàn)序列化怎么才能進(jìn)行持久化和網(wǎng)絡(luò)傳輸呢,要持久化和網(wǎng)絡(luò)傳輸就得轉(zhuǎn)為字節(jié)流,所以在分布式應(yīng)用中及設(shè)計(jì)數(shù)據(jù)持久化的場(chǎng)景中,你就得實(shí)現(xiàn)序列化。

第二個(gè)問題,是不是每個(gè)實(shí)體bean都要實(shí)現(xiàn)序列化,答案其實(shí)還要回歸到第一個(gè)問題,那就是你的bean是否需要持久化存儲(chǔ)媒體中以及是否需要傳輸給另一個(gè)應(yīng)用,沒有的話就不需要,例如我們利用fastjson將實(shí)體類轉(zhuǎn)化成json字符串時(shí),并不涉及到轉(zhuǎn)化為字節(jié)流,所以其實(shí)跟序列化沒有關(guān)系。

第三個(gè)問題,有的時(shí)候并沒有實(shí)現(xiàn)序列化,依然可以持久化到數(shù)據(jù)庫。這個(gè)其實(shí)我們可以看看實(shí)體類中常用的數(shù)據(jù)類型,例如Date、String等等,它們已經(jīng)實(shí)現(xiàn)了序列化,而一些基本類型,數(shù)據(jù)庫里面有與之對(duì)應(yīng)的數(shù)據(jù)結(jié)構(gòu),從我們的類聲明來看,我們沒有實(shí)現(xiàn)serializabel接口,其實(shí)是在聲明的各個(gè)不同變量的時(shí)候,由具體的數(shù)據(jù)類型幫助我們實(shí)現(xiàn)了序列化操作。

另外需要注意的是,在NoSql數(shù)據(jù)庫中,并沒有與我們Java基本類型對(duì)應(yīng)的數(shù)據(jù)結(jié)構(gòu),所以在往nosql數(shù)據(jù)庫中存儲(chǔ)時(shí),我們就必須將對(duì)象進(jìn)行序列化,同時(shí)在網(wǎng)絡(luò)傳輸中我們要注意到兩個(gè)應(yīng)用中javabean的serialVersionUID要保持一致,不然就不能正常的進(jìn)行反序列化。

Java 類對(duì)象的序列化代碼演示

到這里 Serializable 需要了解的基礎(chǔ)知識(shí)就都給大家梳理出來了,這塊屬于選讀,用 Java 編程寫序列化代碼的場(chǎng)景并不是太多,不過有興趣就再接著往下看吧,有個(gè)印象,這樣以后寫代碼的時(shí)候,哪天用上了,還能快速想起來在哪看過,再回來翻看。

Java 對(duì)象序列化(寫入)由 ObjectOutputStream 完成,反序列化(讀?。┯?ObjectInputStream 完成。ObjectInputStream 和 ObjectOutputStream 是分別繼承了 java.io.InputStream 和 java.io.OutputStream 抽象的實(shí)體類。 ObjectOutputStream 可以將對(duì)象的原型作為字節(jié)流寫入 OutputStream。然后我們可以使用 ObjectInputStream 讀取這些流。 ObjectOutputStream 中最重要的方法是:

public final void writeObject(Object o) throws IOException;

這個(gè)方法接收一個(gè)可序列化對(duì)象(實(shí)現(xiàn)了 Serializable 接口的類的對(duì)象)并將其轉(zhuǎn)換為字節(jié)序列。同樣,在ObjectInputStream 中最重要的方法是:

public final Object readObject() throws IOException, ClassNotFoundException;

此方法可以讀取字節(jié)流并將其轉(zhuǎn)換回 Java 對(duì)象。然后我們可以再使用類型轉(zhuǎn)換(Type Cast)將其轉(zhuǎn)換回原始的類型對(duì)象。

下面我們使用文章示例里的Person類再給大家演示一下 Java 的序列化代碼。

public class Person implements Serializable {
    private static final long serialVersionUID = 1L;
    static String country = "ITALY";
    private int age;
    private String name;
    transient int height;
    // 省略 getter 和 setter
}

這里要注意一下, static 修飾的靜態(tài)屬性是類屬性,并不屬于對(duì)象,所以在序列化對(duì)象時(shí)不會(huì)把類中的靜態(tài)屬性序列化了,另外我們也可以使用 transient關(guān)鍵字修飾那些我們想在序列化過程中忽略調(diào)的對(duì)象屬性。

@Test 
public void serializingAndDeserializing_ThenObjectIsTheSame() () 
  throws IOException, ClassNotFoundException { 
    Person person = new Person();
    person.setAge(20);
    person.setName("Joe");
    // 用指定文件路徑--當(dāng)前目錄的 test_serialization.txt 文件創(chuàng)建 FileOutputStream。
    // 在寫入 FileOutputStream 時(shí), FileOutputStream 會(huì)在在項(xiàng)目目錄中創(chuàng)建文件
    // “test_serialization.txt”
    FileOutputStream fileOutputStream
      = new FileOutputStream("./test_serialization.txt");
    // 以 FileOutputStream 為底層輸出流創(chuàng)建對(duì)象輸出流 ObjectOutputStream
    ObjectOutputStream objectOutputStream 
      = new ObjectOutputStream(fileOutputStream);
    // 向 ObjectOutputStream 中寫入 person 對(duì)象
    objectOutputStream.writeObject(person);
    // 把數(shù)據(jù)從流中刷到磁盤上
    objectOutputStream.flush();
    objectOutputStream.close();
    // 用上面的文件路徑,創(chuàng)建文件輸入流
    FileInputStream fileInputStream
      = new FileInputStream("./test_serialization.txt");
    // 以文件輸入流創(chuàng)建對(duì)象輸入流 ObjectInputStream
    ObjectInputStream objectInputStream
      = new ObjectInputStream(fileInputStream);
    // 用對(duì)象輸入流讀取到文件中保存的序列化對(duì)象,反序列化成 Java Object 再轉(zhuǎn)換成 Person 對(duì)象
    Person p2 = (Person) objectInputStream.readObject();
    objectInputStream.close(); 
    assertTrue(p2.getAge() == person.getAge());
    assertTrue(p2.getName().equals(person.getName()));
}

上面這個(gè)單元測(cè)試?yán)锏拇a演示了,怎么把 Person 類的對(duì)象進(jìn)行 Java 序列化保存到文件中,再從文件中讀取對(duì)象被序列化后的字節(jié)序列,然后還原成Person類的對(duì)象。

因?yàn)槲覀兊膶谶€沒有設(shè)計(jì)到 Java IO 這塊的內(nèi)容,所以各種輸入輸出流就不過多進(jìn)行講解了,為了方便大家閱讀時(shí)理解上面的程序,我在上面程序注釋里已經(jīng)詳細(xì)注釋了每一步完成的操作,這些輸入輸出流我們等到講到 Java IO 體系的時(shí)候再詳細(xì)進(jìn)行講解。

總結(jié)

今天給大家梳理了 Java Serializable 接口的一些必須要了解的知識(shí),Serializable 接口在我們用 Java 編程的時(shí)候經(jīng)常見,但是很多人并不了解它的作用,因?yàn)樗闹饕饔眠€是用于標(biāo)記類是否是可序列化類,這樣 Java 的 ObjectOutputStream 和 ObjectInputStream 才能對(duì)類的對(duì)象進(jìn)行序列化和反序列化。

下一篇我們分享 Iterable 和 Iterator 這兩個(gè)名字看起差不多的 Java 內(nèi)置接口,請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Spring boot2X負(fù)載均衡和反向代理實(shí)現(xiàn)過程解析

    Spring boot2X負(fù)載均衡和反向代理實(shí)現(xiàn)過程解析

    這篇文章主要介紹了Spring boot2X負(fù)載均衡和反向代理實(shí)現(xiàn)過程解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-12-12
  • Spring Bean的屬性注入方式

    Spring Bean的屬性注入方式

    這篇文章主要介紹了Spring Bean的屬性注入方式的相關(guān)資料,需要的朋友可以參考下
    2018-02-02
  • IntelliJ Idea SpringBoot 數(shù)據(jù)庫增刪改查實(shí)例詳解

    IntelliJ Idea SpringBoot 數(shù)據(jù)庫增刪改查實(shí)例詳解

    SpringBoot 是 SpringMVC 的升級(jí),對(duì)于編碼、配置、部署和監(jiān)控,更加簡(jiǎn)單。這篇文章主要介紹了IntelliJ Idea SpringBoot 數(shù)據(jù)庫增刪改查實(shí)例,需要的朋友可以參考下
    2018-02-02
  • java連接zookeeper實(shí)現(xiàn)zookeeper教程

    java連接zookeeper實(shí)現(xiàn)zookeeper教程

    這篇文章主要介紹了java連接zookeeper實(shí)現(xiàn)zookeeper教程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • JAVA多線程編程實(shí)例詳解

    JAVA多線程編程實(shí)例詳解

    這篇文章主要介紹了JAVA多線程編程,結(jié)合實(shí)例形式總結(jié)分析了多線程、鎖、線程池等相關(guān)原理及使用技巧,需要的朋友可以參考下
    2019-09-09
  • 查看Spring容器中bean的五種方法小結(jié)

    查看Spring容器中bean的五種方法小結(jié)

    近期在寫Spring項(xiàng)目的時(shí)候,需要通過注解的形式去替代之前直接將Bean存放在Spring容器這種方式,以此來簡(jiǎn)化對(duì)于Bean對(duì)象的操作,這篇文章主要給大家介紹了關(guān)于如何查看Spring容器中bean的五種方法,需要的朋友可以參考下
    2024-05-05
  • Java中的Semaphore信號(hào)量使用方法代碼實(shí)例

    Java中的Semaphore信號(hào)量使用方法代碼實(shí)例

    這篇文章主要介紹了Java中的Semaphore信號(hào)量使用方法代碼實(shí)例,Semaphore是一種基于計(jì)數(shù)的信號(hào)量,它可以設(shè)定一個(gè)閾值,基于此,多個(gè)線程競(jìng)爭(zhēng)獲取許可信號(hào),做自己的申請(qǐng)后歸還,超過閾值后,線程申請(qǐng)?jiān)S可信號(hào)將會(huì)被阻塞,需要的朋友可以參考下
    2023-11-11
  • java實(shí)現(xiàn)微信掃碼支付功能

    java實(shí)現(xiàn)微信掃碼支付功能

    這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)微信掃碼支付功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-07-07
  • 解決使用stream將list轉(zhuǎn)map時(shí),key重復(fù)導(dǎo)致報(bào)錯(cuò)的問題

    解決使用stream將list轉(zhuǎn)map時(shí),key重復(fù)導(dǎo)致報(bào)錯(cuò)的問題

    這篇文章主要介紹了解決使用stream將list轉(zhuǎn)map時(shí),key重復(fù)導(dǎo)致報(bào)錯(cuò)的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-06-06
  • springcloud整合到項(xiàng)目中無法啟動(dòng)報(bào)錯(cuò)Failed to start bean 'eurekaAutoServiceRegistration'

    springcloud整合到項(xiàng)目中無法啟動(dòng)報(bào)錯(cuò)Failed to start bean&n

    這篇文章主要介紹了springcloud整合到項(xiàng)目中無法啟動(dòng)報(bào)錯(cuò)Failed to start bean 'eurekaAutoServiceRegistration'問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-01-01

最新評(píng)論