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

Java中的序列化機(jī)制詳細(xì)解讀

 更新時(shí)間:2023年11月23日 08:58:37   作者:我會(huì)努力變強(qiáng)的  
這篇文章主要介紹了Java中的序列化機(jī)制詳細(xì)解讀,序列化:將對(duì)象的狀態(tài)信息轉(zhuǎn)換為可以存儲(chǔ)或傳輸?shù)臄?shù)據(jù)形式(比如二進(jìn)制)的過程,反序列化:與序列化相對(duì),把序列化轉(zhuǎn)換成的可以存儲(chǔ)或傳輸?shù)臄?shù)據(jù)形式轉(zhuǎn)化為對(duì)象的狀態(tài)信息的過程,需要的朋友可以參考下

序列化與反序列化

  • 序列化:將對(duì)象的狀態(tài)信息轉(zhuǎn)換為可以存儲(chǔ)或傳輸?shù)臄?shù)據(jù)形式(比如二進(jìn)制)的過程。
  • 反序列化:與序列化相對(duì),把序列化轉(zhuǎn)換成的可以存儲(chǔ)或傳輸?shù)臄?shù)據(jù)形式轉(zhuǎn)化為對(duì)象的狀態(tài)信息的過程。

java序列化與反序列化

  • 序列化:把對(duì)象轉(zhuǎn)換為二進(jìn)制流。
  • 反序列化:把二進(jìn)制流數(shù)據(jù)轉(zhuǎn)化為對(duì)象。

使用場景

  1. 永久保存數(shù)據(jù)。把序列化后的數(shù)據(jù)保存到磁盤等存儲(chǔ)設(shè)備中。使用時(shí)可以反序列化。比如某些對(duì)象不想隨著JVM關(guān)閉而消失,可以序列化到磁盤中。
  2. 用于網(wǎng)絡(luò)傳輸。把對(duì)象序列化為二進(jìn)制數(shù)據(jù),傳輸?shù)竭h(yuǎn)程計(jì)算機(jī)。
  3. java序列化還可以實(shí)現(xiàn)對(duì)象深拷貝。

使用方式

java實(shí)現(xiàn)序列化與反序列化需要ObjectInputStrem(反序列化)和ObjectOutputStream(序列化)。并且序列化對(duì)象需要實(shí)現(xiàn)Serializable接口。

代碼:

//實(shí)現(xiàn)Serializable接口
public class Person implements Serializable{
    private static final long serialVersionUID = 2063917965595163411L;
    private String name;
    private Integer age;
    private String Sex;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
    public String getSex() {
        return Sex;
    }
    public void setSex(String sex) {
        Sex = sex;
    }
    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age='" + age + '\'' +
                ", Sex='" + Sex + '\'' +
                '}';
    }
}
public class JavaSerializableDemo {
    //初始化一個(gè)Person對(duì)象
    public static Person initPerson(){
        Person person = new Person();
        person.setAge(18);
        person.setName("Mike");
        person.setSex("man");
        return person;
    }
//序列化
    public static Person serialize(){
        Person person = initPerson();
	//創(chuàng)建一個(gè)文件輸出流,把序列化的數(shù)據(jù)寫到文件中。
	//創(chuàng)建一個(gè)ObjectOutputStream 用于序列化對(duì)象,將序列化的數(shù)據(jù)寫到fileOutputStream 流中。
        try (FileOutputStream fileOutputStream = new FileOutputStream("person");
             ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);){
		//序列化對(duì)象
            objectOutputStream.writeObject(person);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return person;
    }
//反序列
    public static Person deSerialize(){
        Person person = null;
        //創(chuàng)建一個(gè)文件輸入流,用于讀取序列化文件。
        //創(chuàng)建一個(gè)objectInputStream ,用于反序列化對(duì)象,通過讀取fileInputStream 流數(shù)據(jù)進(jìn)行反序列化
        try (FileInputStream fileInputStream = new FileInputStream("person");
        ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);){
			//反序列化
            person = (Person) objectInputStream.readObject();
           return person;
        }catch (IOException e){
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return person;
    }
    public static void main(String[] args) {
        Person person = serialize();
        Person person1 = deSerialize();
        System.out.println("person==>" + person);
        System.out.println("person1==>" +person1);
        //判斷兩個(gè)對(duì)象是否是用一個(gè)對(duì)象
        System.out.println("person == person1 ==> " + (person == person1));
        //分別修改屬性,看看會(huì)不會(huì)影響另外一個(gè)對(duì)象
        person.setAge(20);
        person1.setName("john");
        System.out.println("person==>" + person);
        System.out.println("person1==>" +person1);
    }
}

結(jié)果:

在這里插入圖片描述

第一行和第二行對(duì)象的屬性值一樣說明了序列化會(huì)保存對(duì)象的狀態(tài)信息。 第三行返回false,說明java的序列化機(jī)制實(shí)現(xiàn)的是對(duì)象的copy,而不是對(duì)象引用的copy。 第四行和第五行,修改對(duì)象屬性不會(huì)影響另外一個(gè)對(duì)象,也說明了java的序列化機(jī)制實(shí)現(xiàn)的是對(duì)象的copy,而不是對(duì)象引用的copy。

如果被序列化的對(duì)象的類沒有實(shí)現(xiàn)Serializable接口,就會(huì)拋出異常。

在這里插入圖片描述

serialVersionUID

serialVersionUID,是序列化版本控制UID,其目的是序列化對(duì)象版本控制,有關(guān)各版本反序列化時(shí)是否兼容。如果在新版本中這個(gè)值修改了,新版本就不兼容舊版本,反序列化時(shí)會(huì)拋出InvalidClassException異常。如果修改較小,比如僅僅是增加了一個(gè)屬性,我們希望向下兼容,老版本的數(shù)據(jù)都能保留,那就不用修改;如果我們刪除了一個(gè)屬性,或者更改了類的繼承關(guān)系,必然不兼容舊數(shù)據(jù),這時(shí)就應(yīng)該手動(dòng)更新版本號(hào),即SerialVersionUid。

如果沒有顯示指定該版本號(hào),編譯器會(huì)在編譯類文件時(shí)自動(dòng)幫我們根據(jù)類的信息創(chuàng)建一個(gè)版本號(hào)。如果沒有指定版本號(hào)的情況下修改類文件信息,會(huì)導(dǎo)致編譯器生成的版本id不一樣,在反序列化時(shí)就會(huì)拋出異常。

步驟:

不顯示指定版本id。進(jìn)行序列化操作。修改Person類,比如新增一個(gè)fatherName屬性。然后用之前序列化的文件進(jìn)行反序列化為修改后的Person類。

結(jié)果:

在這里插入圖片描述

由于Person文件進(jìn)過了修改,編譯器生成的版本id就會(huì)變化。然后就會(huì)導(dǎo)致序列化時(shí)的版本id與反序列化時(shí)的版本id對(duì)不上而拋出異常。

指定版本id后,就算修改文件,也不會(huì)導(dǎo)致反序列化異常。

靜態(tài)變量的序列化

靜態(tài)變量不會(huì)被序列化,也就是序列化時(shí)會(huì)忽略靜態(tài)變量。因?yàn)殪o態(tài)變量是類級(jí)別的屬性,而java序列化機(jī)制保存的是對(duì)象的狀態(tài)信息。

代碼: person新增一個(gè)live的靜態(tài)屬性。

在這里插入圖片描述

 public static void main(String[] args) {
        //先序列化
        Person person = serialize();
        //修改靜態(tài)變量的值
        person.live = "宇宙";
        //反序列化
        Person person1 = deSerialize();


        System.out.println(person1.live);

    }

結(jié)論: 首先把對(duì)象序列化,如果會(huì)把靜態(tài)變量的值序列化的話,那么反序列化出來的對(duì)象的靜態(tài)變量值就應(yīng)該是序列化前的值,也就是地球村。

如果會(huì)忽略靜態(tài)變量的話,那么反序列化出來的對(duì)象的靜態(tài)變量值就應(yīng)該是序列化后修改的值,也就是宇宙。

在這里插入圖片描述

transient

transient關(guān)鍵字用于修飾成員變量,被修飾的成員變量不會(huì)被java的序列化機(jī)制序列化。所以反序列化時(shí)得到的對(duì)象的該屬性會(huì)使用默認(rèn)值,對(duì)象引用默認(rèn)null,int 默認(rèn)為0等,或者如果該類里面初始化了,就使用初始化的值。

Person加上一個(gè)被transient修飾的屬性aliasName

在這里插入圖片描述

修改測試代碼和初始化代碼,初始化aliasName,序列化和反序列化代碼不變:

//初始化一個(gè)Person對(duì)象
    public static Person initPerson(){
        Person person = new Person();
        person.setAge(18);
        person.setName("Mike");
        person.setSex("man");
        person.setAliasName("Handsome Mike");
        return person;
    }

 public static void main(String[] args) {
        //先序列化
        Person person = serialize();
        //反序列化

        Person person1 = deSerialize();

        //輸出被transient修飾的屬性aliasName

        System.out.println(person.getAliasName());

        System.out.println(person1.getAliasName());

    }

結(jié)果:

在這里插入圖片描述

反序列化的對(duì)象Person的aliasName屬性為null。表名在序列化時(shí),該屬性被忽略了。

繼承關(guān)系的序列化

序列化某個(gè)類,如果該類有父類且父類沒有實(shí)現(xiàn)Serializable接口,則不會(huì)序列化父類的屬性,反序列化時(shí)父類的屬性會(huì)使用默認(rèn)值或者創(chuàng)建對(duì)象時(shí)就初始化的值。

//person類繼承一個(gè)父類。
//實(shí)現(xiàn)Serializable接口
public class Person extends SupperPerson implements Serializable{


    private String name;

    private Integer age;

    private String Sex;

    private transient String aliasName;

    public static  String live = "地球村";



    public String getAliasName() {
        return aliasName;
    }

    public void setAliasName(String aliasName) {
        this.aliasName = aliasName;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getSex() {
        return Sex;
    }

    public void setSex(String sex) {
        Sex = sex;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age='" + age + '\'' +
                ", Sex='" + Sex + '\'' +
                '}';
    }
}

//父類
public class SupperPerson{

    private String aaa;

    private String bbb;


    public String getAaa() {
        return aaa;
    }

    public void setAaa(String aaa) {
        this.aaa = aaa;
    }

    public String getBbb() {
        return bbb;
    }

    public void setBbb(String bbb) {
        this.bbb = bbb;
    }
}

//初始化:
//初始化一個(gè)Person對(duì)象
    public static Person initPerson(){
        Person person = new Person();
        person.setAge(18);
        person.setName("Mike");
        person.setSex("man");
        person.setAliasName("Handsome Mike");
        //初始化父類屬性
        person.setAaa("aaaaa");
        person.setBbb("bbbbb");
        return person;
    }

//測試
public static void main(String[] args) {
        //先序列化
        Person person = serialize();
        //反序列化

        Person person1 = deSerialize();

        //輸出被transient修飾的屬性aliasName

        System.out.println(person.getAaa());

        System.out.println(person.getBbb());


        System.out.println(person1.getAaa());

        System.out.println(person1.getBbb());

    }

結(jié)果:

在這里插入圖片描述

父類的屬性aaa和bbb沒有被序列化保存下來。

如果父類也實(shí)現(xiàn)了Serializable接口,就會(huì)序列化父類的屬性。

在這里插入圖片描述

其他代碼一樣。 結(jié)果:

在這里插入圖片描述

反序列化后父類的屬性不再是null。

通常序列化實(shí)現(xiàn)深度克隆

克隆一個(gè)對(duì)象就是保存原對(duì)象的狀態(tài)信息新建一個(gè)對(duì)象,實(shí)現(xiàn)對(duì)象的拷貝,新對(duì)象與原對(duì)象在堆上是兩個(gè)不同的對(duì)象。

public class Student implements Serializable,Cloneable{
    private String name;

    private Integer age;

    private String Sex;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getSex() {
        return Sex;
    }

    public void setSex(String sex) {
        Sex = sex;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", Sex='" + Sex + '\'' +
                '}';
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        //實(shí)現(xiàn)父類的clone方法。調(diào)用序列化和反序列化。
        return deSerialize(serialize(this));
    }

    /**
     * 序列化返回序列化的字節(jié)數(shù)組
     * @param student
     * @return
     */
    private  byte[] serialize(Student student){

        try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
             ObjectOutputStream objectOutputStream = new ObjectOutputStream(bos);){


            objectOutputStream.writeObject(student);
            return bos.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 通過序列化字節(jié)數(shù)組反序列化成Student對(duì)象。
     * @param studentByteArray
     * @return
     */
    private  Student deSerialize(byte[] studentByteArray){
        try (ByteArrayInputStream bis = new ByteArrayInputStream(studentByteArray);
             ObjectInputStream objectInputStream = new ObjectInputStream(bis);){

            Student student = (Student) objectInputStream.readObject();

            return student;

        }catch (IOException e){
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }
}


//測試
 public static void main(String[] args) {
        Student student = new Student();
        student.setAge(22);
        student.setName("Mike");
        student.setSex("man");

        try {
            Student studentCopy = (Student) student.clone();
            System.out.println(student);
            System.out.println(studentCopy);
            System.out.println(student == studentCopy);
            System.out.println(student.getName() == studentCopy.getName());
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }

    }

結(jié)果:

在這里插入圖片描述

第一行和第二行原對(duì)象與克隆對(duì)象的屬性一致,表示克隆成功。

第三行比較的是原對(duì)象與克隆對(duì)象是否是同一個(gè)對(duì)象,比較對(duì)象的地址,返回false,表名是兩個(gè)不同的對(duì)象。

第四行判斷的是對(duì)象的屬性是否也進(jìn)行了克隆,比較兩屬性對(duì)象的地址,返回false,表示也不是同一個(gè)對(duì)象,達(dá)到了深克隆。

總結(jié)

  1. 在java中,只要一個(gè)類實(shí)現(xiàn)了java.io.Serializable接口,那么他就可以被序列化。
  2. 通過ObjectInputStream和ObjectOutputStream對(duì)對(duì)象進(jìn)行序列化和反序列化。
  3. 對(duì)象能否被反序列化,不僅取決于對(duì)象代碼是否一直不變,還取決于UID。
  4. 序列化不保存靜態(tài)屬性。
  5. 要想父類的屬性也進(jìn)行序列化,父類也要實(shí)現(xiàn)java.io.Serializable接口。
  6. transient關(guān)鍵字控制屬性是否會(huì)被序列化,如果沒有被序列化的屬性反序列化后,會(huì)被設(shè)置成初始值。
  7. 通過序列化機(jī)制可以實(shí)現(xiàn)對(duì)象深拷貝。

到此這篇關(guān)于Java中的序列化機(jī)制詳細(xì)解讀的文章就介紹到這了,更多相關(guān)Java序列化機(jī)制內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 探討Java驗(yàn)證碼制作(下篇)

    探討Java驗(yàn)證碼制作(下篇)

    這篇文章主要介紹了探討Java驗(yàn)證碼制作(下篇)的相關(guān)資料,需要的朋友可以參考下
    2016-05-05
  • 詳解如何為SpringBoot項(xiàng)目中的自定義配置添加IDE支持

    詳解如何為SpringBoot項(xiàng)目中的自定義配置添加IDE支持

    這篇文章主要介紹了詳解如何為SpringBoot項(xiàng)目中的自定義配置添加IDE支持,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-02-02
  • feign 調(diào)用第三方服務(wù)中部分特殊符號(hào)未轉(zhuǎn)義問題

    feign 調(diào)用第三方服務(wù)中部分特殊符號(hào)未轉(zhuǎn)義問題

    這篇文章主要介紹了feign 調(diào)用第三方服務(wù)中部分特殊符號(hào)未轉(zhuǎn)義問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • Spring Boot+Nginx實(shí)現(xiàn)大文件下載功能

    Spring Boot+Nginx實(shí)現(xiàn)大文件下載功能

    相信很多小伙伴,在日常開放中都會(huì)遇到大文件下載的情況,大文件下載方式也有很多,比如非常流行的分片下載、斷點(diǎn)下載;當(dāng)然也可以結(jié)合Nginx來實(shí)現(xiàn)大文件下載,在中小項(xiàng)目非常適合使用,這篇文章主要介紹了Spring Boot結(jié)合Nginx實(shí)現(xiàn)大文件下載,需要的朋友可以參考下
    2024-05-05
  • Java Web項(xiàng)目部署在Tomcat運(yùn)行出錯(cuò)與解決方法示例

    Java Web項(xiàng)目部署在Tomcat運(yùn)行出錯(cuò)與解決方法示例

    這篇文章主要介紹了Java Web項(xiàng)目部署在Tomcat運(yùn)行出錯(cuò)與解決方法,結(jié)合具體實(shí)例形式分析了Java Web項(xiàng)目部署在Tomcat過程中由于xml配置文件導(dǎo)致的錯(cuò)誤問題常見提示與解決方法,需要的朋友可以參考下
    2017-03-03
  • Spring將MultipartFile轉(zhuǎn)存到本地磁盤的三種方式

    Spring將MultipartFile轉(zhuǎn)存到本地磁盤的三種方式

    在Java中處理文件向來是一種不是很方便的操作,然后隨著Spring框架的崛起,使用Spring框架中的MultipartFile來處理文件也是件很方便的事了,今天就給大家介紹Spring將MultipartFile轉(zhuǎn)存到本地磁盤的方式,需要的朋友可以參考下
    2024-10-10
  • java單鏈表逆序用法代碼示例

    java單鏈表逆序用法代碼示例

    這篇文章主要介紹了java單鏈表逆序用法代碼示例,小編覺得還是挺不錯(cuò)的,具有一定借鑒價(jià)值,需要的朋友可以參考下
    2018-01-01
  • Spring?Boot循環(huán)依賴原理、解決方案與最佳實(shí)踐(全解析)

    Spring?Boot循環(huán)依賴原理、解決方案與最佳實(shí)踐(全解析)

    循環(huán)依賴指兩個(gè)或多個(gè)Bean相互直接或間接引用,形成閉環(huán)依賴關(guān)系,這篇文章主要介紹了Spring?Boot循環(huán)依賴原理、解決方案與最佳實(shí)踐(全解析),需要的朋友可以參考下
    2025-04-04
  • 基于idea操作hbase數(shù)據(jù)庫并映射到hive表

    基于idea操作hbase數(shù)據(jù)庫并映射到hive表

    這篇文章主要介紹了用idea操作hbase數(shù)據(jù)庫,并映射到hive,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-03-03
  • 教你用springboot連接mysql并實(shí)現(xiàn)增刪改查

    教你用springboot連接mysql并實(shí)現(xiàn)增刪改查

    今天教各位小伙伴用springboot連接mysql并實(shí)現(xiàn)增刪改查功能,文中有非常詳細(xì)的步驟及代碼示例,對(duì)正在學(xué)習(xí)Java的小伙伴們有非常好的幫助,需要的朋友可以參考下
    2021-05-05

最新評(píng)論