Java序列化和反序列化示例介紹
以前用序列化都是一些方法需要才實(shí)現(xiàn)的,后來(lái)業(yè)務(wù)需求要深拷貝才去研究。參閱了別人博客得出一些總結(jié)。
序列化是為了把Java對(duì)象轉(zhuǎn)化為字節(jié)序列(字節(jié)流)的過程。然后深拷貝是通過對(duì)流的操作來(lái)實(shí)現(xiàn)的,序列化后數(shù)據(jù)方便存儲(chǔ)和傳輸。反序列化則是把字節(jié)序列反序列化為Java對(duì)象
存儲(chǔ)方便:因?yàn)閷?duì)象會(huì)被回收,序列化后可以持續(xù)化存儲(chǔ)在磁盤中
傳輸方便:字節(jié)序列(二進(jìn)制形式)可以進(jìn)行網(wǎng)絡(luò)傳輸和傳播。
最好設(shè)置一個(gè)SerialversionUID,因?yàn)樾蛄谢头葱蛄谢菍?duì)比SerialversionUID來(lái)進(jìn)行的,雖然不設(shè)置接口也會(huì)默認(rèn)生成一個(gè),但是要知道序列化對(duì)象過程一般都是對(duì)象->序列化->存儲(chǔ)或傳輸->反序列化。
舉個(gè)例子:
先創(chuàng)建一個(gè)實(shí)體類Student
import lombok.Data;
import java.io.Serializable;
@Data
public class Student implements Serializable {
private Integer id;
private String name;
private String sex;
}
然后創(chuàng)建一個(gè)測(cè)試類SerializableTest
import serialization.entity.Student;
import java.io.*;
public class SerializableTest {
public static void main(String[] args) throws Exception {
serializeStudent();
Student student = deserializeStudent();
System.out.println("name:" + student.getName());
System.out.println("sex:" + student.getSex());
}
private static void serializeStudent() throws IOException {
Student student = new Student();
student.setId(1);
student.setName("張三");
student.setSex("male");
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(
new File("F:/student.txt")));
out.writeObject(student);
System.out.println("序列化成功");
out.close();
}
private static Student deserializeStudent() throws Exception {
ObjectInputStream in = new ObjectInputStream(new FileInputStream(new File("F:/student.txt")));
Student student = (Student) in.readObject();
System.out.println("反序列化成功");
return student;
}
}
執(zhí)行結(jié)果:
序列化成功 反序列化成功 name:張三 sex:male
這個(gè)時(shí)候沒有指定SerialversionUID也是可以成功的,但對(duì)象->序列化->存儲(chǔ)或傳輸->反序列化,咱們?cè)诜葱蛄谢僮髦皩?duì)Student類修改呢?
這個(gè)時(shí)候咱們修改一下代碼,先注釋掉反序列化代碼,先進(jìn)行序列化。
import serialization.entity.Student;
import java.io.*;
public class SerializableTest {
public static void main(String[] args) throws Exception {
serializeStudent();
// Student student = deserializeStudent();
// System.out.println("name:" + student.getName());
// System.out.println("sex:" + student.getSex());
}
private static void serializeStudent() throws IOException {
Student student = new Student();
student.setId(1);
student.setName("張三");
student.setSex("male");
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(
new File("F:/student.txt")));
out.writeObject(student);
System.out.println("序列化成功");
out.close();
}
// private static Student deserializeStudent() throws Exception {
// ObjectInputStream in = new ObjectInputStream(new FileInputStream(new File("F:/student.txt")));
// Student student = (Student) in.readObject();
// System.out.println("反序列化成功");
// return student;
// }
}
運(yùn)行結(jié)果:
序列化成功
修改Student類
import lombok.Data;
import java.io.Serializable;
@Data
public class Student implements Serializable {
private Integer id;
private String name;
private String sex;
private String address;
}
注釋掉序列化方法,進(jìn)行反序列化
import serialization.entity.Student;
import java.io.*;
public class SerializableTest {
public static void main(String[] args) throws Exception {
// serializeStudent();
Student student = deserializeStudent();
System.out.println("name:" + student.getName());
System.out.println("sex:" + student.getSex());
}
// private static void serializeStudent() throws IOException {
// Student student = new Student();
// student.setId(1);
// student.setName("張三");
// student.setSex("male");
//
// ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(
// new File("F:/student.txt")));
// out.writeObject(student);
// System.out.println("序列化成功");
// out.close();
// }
private static Student deserializeStudent() throws Exception {
ObjectInputStream in = new ObjectInputStream(new FileInputStream(new File("F:/student.txt")));
Student student = (Student) in.readObject();
System.out.println("反序列化成功");
return student;
}
}
執(zhí)行結(jié)果:
Exception in thread "main" java.io.InvalidClassException: serialization.entity.Student; local class incompatible: stream classdesc serialVersionUID = 3846952599709361171, local class serialVersionUID = -4606152942663467236 at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:699) at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1885) at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1751) at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2042) at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1573) at java.io.ObjectInputStream.readObject(ObjectInputStream.java:431) at serialization.demo.SerializableTest.deserializeStudent(SerializableTest.java:30) at serialization.demo.SerializableTest.main(SerializableTest.java:10) Process finished with exit code 1
可以看出兩次的執(zhí)行的SerialversionUID不匹配,導(dǎo)致產(chǎn)生java.io.InvalidClassException異常,所以只要指定了SerialversionUID就不會(huì)報(bào)異常。
//指定serialVersionUID正確寫法
private static final long serialVersionUID = 3846952599709361171L;
//如果已經(jīng)進(jìn)行序列化了不知道SerialversionUID,可以通過反射獲取
Object obj = Student.class.newInstance();
Field field = Student.class.getDeclaredField("serialVersionUID");
field.setAccessible(true);
System.out.println(field.getLong(obj));
最后需要知道的一點(diǎn)就是字節(jié)流和字符流的區(qū)別。
字節(jié)流:傳輸過程中,傳輸數(shù)據(jù)的最基本單位是字節(jié)的流。
字符流:傳輸過程中,傳輸數(shù)據(jù)的最基本單位是字符的流。
這樣講可能有點(diǎn)不知所云,字節(jié)其實(shí)就是Java的八大基本類型Byte(比特)單位,而字符通常是’A’、‘B’、’$’、’&'等,字節(jié)大小則取決于你是什么編碼(環(huán)境),如下:
ASCII 碼中,一個(gè)英文字母(不分大小寫)為一個(gè)字節(jié),一個(gè)中文漢字為兩個(gè)字節(jié)。
UTF-8 編碼中,一個(gè)英文字為一個(gè)字節(jié),一個(gè)中文為三個(gè)字節(jié)。
Unicode 編碼中,一個(gè)英文為一個(gè)字節(jié),一個(gè)中文為兩個(gè)字節(jié)。
符號(hào):英文標(biāo)點(diǎn)為一個(gè)字節(jié),中文標(biāo)點(diǎn)為兩個(gè)字節(jié)。例如:英文句號(hào) . 占1個(gè)字節(jié)的大小,中文句號(hào) 。
占2個(gè)字節(jié)的大小。UTF-16 編碼中,一個(gè)英文字母字符或一個(gè)漢字字符存儲(chǔ)都需要 2 個(gè)字節(jié)(Unicode 擴(kuò)展區(qū)的一些漢字存儲(chǔ)需要 4 個(gè)字節(jié))。
UTF-32 編碼中,世界上任何字符的存儲(chǔ)都需要 4 個(gè)字節(jié)。
到此這篇關(guān)于Java序列化和反序列化示例介紹的文章就介紹到這了,更多相關(guān)Java序列化內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java使用DateTimeFormatter格式化輸入的日期時(shí)間
這篇文章主要介紹了Java使用DateTimeFormatter格式化輸入的日期時(shí)間,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-01-01
SpringBoot啟動(dòng)后自動(dòng)執(zhí)行初始化任務(wù)的五種方法
在 Spring Boot 開發(fā)中,我們經(jīng)常需要在應(yīng)用啟動(dòng)后立即執(zhí)行初始化任務(wù),本文將深度解析 ??5 種主流實(shí)現(xiàn)方案??,大家可以根據(jù)自己的需求自行選擇2025-04-04
Java 實(shí)戰(zhàn)范例之進(jìn)銷存管理系統(tǒng)的實(shí)現(xiàn)
讀萬(wàn)卷書不如行萬(wàn)里路,只學(xué)書上的理論是遠(yuǎn)遠(yuǎn)不夠的,只有在實(shí)戰(zhàn)中才能獲得能力的提升,本篇文章手把手帶你用java+vue+Springboot+ssm+mysql+maven+redis實(shí)現(xiàn)一個(gè)前后端分離的進(jìn)銷存管理系統(tǒng),大家可以在過程中查缺補(bǔ)漏,提升水平2021-11-11
springbootAOP定義切點(diǎn)獲取/修改請(qǐng)求參數(shù)方式
這篇文章主要介紹了springbootAOP定義切點(diǎn)獲取/修改請(qǐng)求參數(shù)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-08-08
Java報(bào)錯(cuò)Java.text.ParseException的解決方法匯總
在Java開發(fā)的復(fù)雜世界中,錯(cuò)誤處理是開發(fā)者必須面對(duì)的關(guān)鍵挑戰(zhàn)之一,其中,Java.text.ParseException就像一個(gè)隱藏在代碼叢林中的陷阱,常常讓開發(fā)者們陷入困惑,本文給大家介紹了Java報(bào)錯(cuò)Java.text.ParseException的解決方法,需要的朋友可以參考下2024-10-10
spring中@Autowired自動(dòng)注入依賴項(xiàng)的使用
當(dāng)使用@Autowired注解時(shí),它可以自動(dòng)注入依賴項(xiàng),例如其他類的實(shí)例,本文就來(lái)詳細(xì)的介紹一下,具有一定的參考價(jià)值,感興趣的可以了解一下2023-09-09
java實(shí)現(xiàn)多人多牌數(shù)比較游戲
這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)多人多牌數(shù)比較游戲,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-01-01

