面试官:序列化和反序列化为什么要实现Serializable接口?
目錄結🐕
- 前言
- 1、什么是序列化和反序列化
- 2、什么時候需要進行序列化和反序列化
- 2.1、服務器和瀏覽器交互時用到了Serializable接口嗎?
- 2.2、Mybatis將數據持久化到數據庫中用到了Serializable接口嗎?
- 3、為什么實現了Serializable接口就能序列化和反序列化呢?
- 4、為什么實現類Serializable接口還要指定serialVersionUID的值呢?
- 4.1、如果不指定serialVersionUID會出現什么問題呢?
- 4.2、代碼演示
- 5、除了transient,static修飾的也不會被序列化
前言
最近在閱讀別人的源碼時,發現很多實體類都實現了Serializable接口,而且還指定了serialVersionUID,心中不免有些疑問(說實話是因為自己菜才有疑問😀)
Ⅰ.序列化和反序列化到底是什么?
Ⅱ.到底什么時候需要進行序列化和反序列化?
Ⅲ.為什么實現Serializable接口就能實現序列化和反序列化呢?
Ⅳ.為什么實現了Serializable接口還要指定serialVersionUID的值呢?
1、什么是序列化和反序列化
序列化:將Java對象轉換為字節序列的過程
反序列化:將字節序列恢復成Java對象的過程
2、什么時候需要進行序列化和反序列化
以下幾點:將內存中的數據持久化到磁盤或者數據庫時、瀏覽器與服務器交互時。
簡單了說就是當我們持久化內存中的數據或者進行網絡數據傳輸的時候需要進行序列化。(也就是保存數據),反序列化肯定就相反了。
2.1、服務器和瀏覽器交互時用到了Serializable接口嗎?
我們知道服務端在與瀏覽器交互時,返回的是一個json格式的數據,而json格式本質就是字符串類型;下面是String類的源碼
可以看出其String類源碼實現了Serializable接口,并定義了serialVersionUID
2.2、Mybatis將數據持久化到數據庫中用到了Serializable接口嗎?
我們來看mybatis映射文件中的插入這條語句
<insert id="addUser" parameterType="User">insert into user(username,sex,address)values(#{username},#{sex},#{address}) </insert>從表面我們看不出什么地方實現類序列化接口,其實并不是將User對象持久化到數據庫,而是將對象中的屬性持久化到數據庫中,而這些屬性都實現了Serializable接口。
3、為什么實現了Serializable接口就能序列化和反序列化呢?
查看Serializable接口源代碼
可以發現接口中什么也沒有,這其實是Java中的標識語義,用來告訴JVM幫我在底層進行序列化和反序列化。如果不實現這個接口,就只能自己編寫序列化和反序列化代碼了唄,至于具體怎么寫,找度娘唄😁
4、為什么實現類Serializable接口還要指定serialVersionUID的值呢?
1->2(序列化->反序列化)
1.序列化時:如果不指定,JVM會幫我們根據類中的屬性生成一個serialVersionUID,然后進行序列化,最后進行持久化或者數據傳輸;
2.反序列化時:JVM會根據屬性生成一個新的serialVersionUID,然后將這個新的serialVersionUID與序列化時的serialVersionUID進行比對,如果相同,則反序列化成功,否則失敗!
當我們指定了serialVersionUID時,JVM也會生成一個serialVersionUID,但它會將我們指定的serialVersionUID賦給生成的,這樣就保證了UID一致性。
4.1、如果不指定serialVersionUID會出現什么問題呢?
1.假設某個項目,實體類User中實現了Serializable接口,沒有指定serialVersionUID;
2.假設JVM根據User類屬性生成的UID為A;
3.當公司進行版本升級時,如果需要加入新的屬性,那么此時JVM根據屬性生成的UID為B,那么此時A和B不相同,就會導致出現反序列化失敗。
類屬性不同,那么生成的serialVersionUID肯定就不一樣了😀
4.2、代碼演示
首先不得不提一下ObjectOutputStream和ObjectInputStream類(對象流),主要使用writeObject和readObject方法,完成流的寫入與讀取;
1.要求該對象的所以屬性是可序列化的,如果是不可序列化的,必須是瞬態的,使用transient修飾;
2.該類的屬性是可訪問的(public、protected)或者有set和get方法用來恢復狀態。
1.User類
package com.zsh;import java.io.Serializable;/*** @author:抱著魚睡覺的喵喵* @date:2021/3/31* @description:*/ public class User implements Serializable {private String username;private String password;@Overridepublic String toString() {return "User{" +"username='" + username + '\'' +", password='" + password + '\'' +'}';}public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;} }2.測試類
package com.zsh;import java.io.*;/*** @author:抱著魚睡覺的喵喵* @date:2021/3/31* @description:*/ public class TestSerializable {public static void main(String[] args) throws IOException, ClassNotFoundException {User user = new User();user.setUsername("Ronin");user.setPassword("123");serialize(user);User user2 = deserialize();System.out.println(user2);}public static void serialize(User user) throws IOException {ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream(new File("F:\\a.txt")));os.writeObject(user);os.close();}public static User deserialize() throws IOException, ClassNotFoundException {ObjectInputStream oi = new ObjectInputStream(new FileInputStream(new File("F:\\a.txt")));return (User)oi.readObject();} }3.結果:
重點來了
當我在User類中新增一個屬性title
package com.zsh;import java.io.Serializable;/*** @author:抱著魚睡覺的喵喵* @date:2021/3/31* @description:*/ public class User implements Serializable {private String username;private String password;private String title;public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}public String getTitle() {return title;}public void setTitle(String title) {this.title = title;}@Overridepublic String toString() {return "User{" +"username='" + username + '\'' +", password='" + password + '\'' +", title='" + title + '\'' +'}';} }關掉測試類中的序列化方法
package com.zsh;import java.io.*;/*** @author:抱著魚睡覺的喵喵* @date:2021/3/31* @description:*/ public class TestSerializable {public static void main(String[] args) throws IOException, ClassNotFoundException { // User user = new User(); // user.setUsername("Ronin"); // user.setPassword("123"); // serialize(user);User user2 = deserialize();System.out.println(user2);} // public static void serialize(User user) throws IOException { // ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream(new File("F:\\a.txt"))); // os.writeObject(user); // os.close(); // }public static User deserialize() throws IOException, ClassNotFoundException {ObjectInputStream oi = new ObjectInputStream(new FileInputStream(new File("F:\\a.txt")));return (User)oi.readObject();} }
發現出現了序列化和反序列化serialVersionUID不同的情況,由于我們加入了屬性,導致反序列化時JVM根據屬性生成的UID和序列化時的不同。
當我加入自定義serialVersionUID時,去掉title屬性
package com.zsh;import java.io.Serializable;/*** @author:抱著魚睡覺的喵喵* @date:2021/3/31* @description:*/ public class User implements Serializable {private static final long serialVersionUID = 1L;private String username;private String password;public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}@Overridepublic String toString() {return "User{" +"username='" + username + '\'' +", password='" + password + '\'' +'}';} } public class TestSerializable {public static void main(String[] args) throws IOException, ClassNotFoundException {User user = new User();user.setUsername("Ronin");user.setPassword("123");serialize(user);User user2 = deserialize();System.out.println(user2);}public static void serialize(User user) throws IOException {ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream(new File("F:\\a.txt")));os.writeObject(user);os.close();}public static User deserialize() throws IOException, ClassNotFoundException {ObjectInputStream oi = new ObjectInputStream(new FileInputStream(new File("F:\\a.txt")));return (User)oi.readObject();} }結果:
當我加入新的屬性title時
package com.zsh;import java.io.Serializable;/*** @author:抱著魚睡覺的喵喵* @date:2021/3/31* @description:*/ public class User implements Serializable {private static final long serialVersionUID = 1L;private String username;private String password;private String title;public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}public String getTitle() {return title;}public void setTitle(String title) {this.title = title;}@Overridepublic String toString() {return "User{" +"username='" + username + '\'' +", password='" + password + '\'' +", title='" + title + '\'' +'}';} } package com.zsh;import java.io.*;/*** @author:抱著魚睡覺的喵喵* @date:2021/3/31* @description:*/ public class TestSerializable {public static void main(String[] args) throws IOException, ClassNotFoundException { // User user = new User(); // user.setUsername("Ronin"); // user.setPassword("123"); // serialize(user);User user2 = deserialize();System.out.println(user2);} // public static void serialize(User user) throws IOException { // ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream(new File("F:\\a.txt"))); // os.writeObject(user); // os.close(); // }public static User deserialize() throws IOException, ClassNotFoundException {ObjectInputStream oi = new ObjectInputStream(new FileInputStream(new File("F:\\a.txt")));return (User)oi.readObject();} }結果:
由于我自定義了serialVersionUID,那么序列化和反序列化UID肯定就一樣了😀
當我使用transient關鍵字修飾title屬性時
package com.zsh;import java.beans.Transient; import java.io.Serializable;/*** @author:抱著魚睡覺的喵喵* @date:2021/3/31* @description:*/ public class User implements Serializable {private static final long serialVersionUID = 1L;private String username;private String password;private transient String title;public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}public String getTitle() {return title;}public void setTitle(String title) {this.title = title;}@Overridepublic String toString() {return "User{" +"username='" + username + '\'' +", password='" + password + '\'' +", title='" + title + '\'' +'}';} }測試類如下
package com.zsh;import java.io.*;/*** @author:抱著魚睡覺的喵喵* @date:2021/3/31* @description:*/ public class TestSerializable {public static void main(String[] args) throws IOException, ClassNotFoundException {User user = new User();user.setUsername("Ronin");user.setPassword("123");user.setTitle("序列化與反序列化transient關鍵字的作用");serialize(user);User user2 = deserialize();System.out.println(user2);}public static void serialize(User user) throws IOException {ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream(new File("F:\\a.txt")));os.writeObject(user);os.close();}public static User deserialize() throws IOException, ClassNotFoundException {ObjectInputStream oi = new ObjectInputStream(new FileInputStream(new File("F:\\a.txt")));return (User)oi.readObject();} }結果:
可以發現title屬性并沒有被序列化
所以transient關鍵字的作用是:讓屬性不被序列化
5、除了transient,static修飾的也不會被序列化
因為序列化是針對對象而言的,而static修飾的屬性是早于對象存在的,所以不會被序列化
一起加油呀😀😀
總結
以上是生活随笔為你收集整理的面试官:序列化和反序列化为什么要实现Serializable接口?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 进程和线程的基本概念
- 下一篇: 你是否真正理解了泛型、通配符、类型擦除