一盟广、理解Java序列化和反序列化
Serialization(序列化):將java對象以一連串的字節(jié)保存在磁盤文件中的過程掸宛,也可以說是保存java對象狀態(tài)的過程泪幌。序列化可以將數(shù)據(jù)永久保存在磁盤上(通常保存在文件中)惊豺。
deserialization(反序列化):將保存在磁盤文件中的java字節(jié)碼重新轉(zhuǎn)換成java對象稱為反序列化呀非。
二坚俗、序列化和反序列化的應(yīng)用
兩個進(jìn)程在遠(yuǎn)程通信時镜盯,可以發(fā)送多種數(shù)據(jù),包括文本猖败、圖片速缆、音頻、視頻等恩闻,這些數(shù)據(jù)都是以二進(jìn)制序列的形式在網(wǎng)絡(luò)上傳輸艺糜。
java是面向?qū)ο蟮拈_發(fā)方式,一切都是java對象幢尚,想要在網(wǎng)絡(luò)中傳輸java對象破停,可以使用序列化和反序列化去實現(xiàn),發(fā)送發(fā)需要將java對象轉(zhuǎn)換為字節(jié)序列尉剩,然后在網(wǎng)絡(luò)上傳送真慢,接收方收到字符序列后,會通過反序列化將字節(jié)序列恢復(fù)成java對象理茎。
java序列化的優(yōu)點(diǎn):
實現(xiàn)了數(shù)據(jù)的持久化黑界,通過序列化可以把數(shù)據(jù)持久地保存在硬盤上(磁盤文件)。
利用序列化實現(xiàn)遠(yuǎn)程通信皂林,在網(wǎng)絡(luò)上傳輸字節(jié)序列园爷。
三、序列化和反序列化地實現(xiàn)
1.JDK類庫提供的序列化API:
java.io.ObjectOutputStream
表示對象輸出流式撼,其中writeObject(Object obj)方法可以將給定參數(shù)的obj對象進(jìn)行序列化童社,將轉(zhuǎn)換的一連串的字節(jié)序列寫到指定的目標(biāo)輸出流中。
java.io.ObjectInputStream
該類表示對象輸入流著隆,該類下的readObject(Object obj)方法會從源輸入流中讀取字節(jié)序列扰楼,并將它反序列化為一個java對象并返回。
序列化要求:
實現(xiàn)序列化的類對象必須實現(xiàn)了Serializable類或Externalizable類才能被序列化美浦,否則會拋出異常弦赖。
實現(xiàn)java序列化和反序列化的三種方法:
現(xiàn)在要對student類進(jìn)行序列化和反序列化,遵循以下方法:
方法一:若student類實現(xiàn)了serializable接口浦辨,則可以通過objectOutputstream和objectinputstream默認(rèn)的序列化和反序列化方式蹬竖,對非transient的實例變量進(jìn)行序列化和反序列化。
方法二:若student類實現(xiàn)了serializable接口流酬,并且定義了writeObject(objectOutputStream out)和
readObject(objectinputStream in)方法币厕,則可以直接調(diào)用student類的兩種方法進(jìn)行序列化和反序列化。
方法三:若student類實現(xiàn)了Externalizable接口芽腾,則必須實現(xiàn)readExternal(Objectinput in)和writeExternal(Objectoutput out)方法進(jìn)行序列化和反序列化旦装。
JDK類庫中的序列化步驟:
第一步:創(chuàng)建一個輸出流對象,它可以包裝一個輸出流對象摊滔,如:文件輸出流阴绢。
ObjectOutputStream out = new ObjectOutputStream(new fileOutputStream("E:\\JavaXuLiehua\\Student\\Student1.txt"));
第二步:通過輸出流對象的writeObject()方法寫對象
out.writeObject("hollo word");
out.writeObject("happy")
JDK中反序列化操作:
第一步:創(chuàng)建文件輸入流對象
ObjectInputStream in = new ObjectInputStream(new fileInputStream("E:\\JavaXuLiehua\\Student\\Student1.txt"));
第二步:調(diào)用readObject()方法
String obj1 = (String)in.readObject();
String obj2 = (String)in.readObject();
為了保證正確讀取數(shù)據(jù)店乐,對象輸出流寫入對象的順序與對象輸入流讀取對象的順序一致。
Student類序列化和反序列化演示:
1.先創(chuàng)建一個繼承了serializable類的student類
import java.io.Serializable; //導(dǎo)入io包下的序列化類
//創(chuàng)建實現(xiàn)序列化接口的學(xué)生類
public class Student implements Serializable {
//私有化成員變量
private String name;
private char sex;
private int year;
private double gpa;
public Student(){ //無參構(gòu)造
}
public Student(String name,char sex,int year,double gpa){
//參數(shù)給屬性賦值
this.name = name;
this.sex = sex;
this.year = year;
this.gpa = gpa;
}
//重寫set和get
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public char getSex() {
return sex;
}
public void setSex(char sex) {
this.sex = sex;
}
public int getYear() {
return year;
}
public void setYear(int year) {
this.year = year;
}
public double getGpa() {
return gpa;
}
public void setGpa(double gpa) {
this.gpa = gpa;
}
}
把Student類的對象序列化到txt文件(E:\JavaXuLiehua\Student\Student1.txt)中呻袭,并對文件進(jìn)行反序列化:
import java.io.*;
import java.io.Externalizable;
/*
把student類對象序列化到文件E:\\JavaXuLiehua\\Student\\Student1.txt
*/
public class UserStudent {
public static void main(String[] args) throws IOException {
Student st = new Student("Tom",'M',20,3.6); //實例化student類
//判斷Student1.txt是否創(chuàng)建成功
File file = new File("E:\\JavaXuLiehua\\Student\\Student1.txt");
if(file.exists()) {
System.out.println("文件存在");
}else{
//否則創(chuàng)建新文件
file.createNewFile();
}
try {
//Student對象序列化過程
FileOutputStream fos = new FileOutputStream(file);
ObjectOutputStream oos = new ObjectOutputStream(fos);
//調(diào)用 ObjectOutputStream 中的 writeObject() 方法 寫對象
oos.writeObject(st);
oos.flush(); //flush方法刷新緩沖區(qū)眨八,寫字符時會用,因為字符會先進(jìn)入緩沖區(qū)左电,將內(nèi)存中的數(shù)據(jù)立刻寫出
fos.close();
oos.close();
//Student對象反序列化過程
FileInputStream fis = new FileInputStream(file);
//創(chuàng)建對象輸入流
ObjectInputStream ois = new ObjectInputStream(fis);
//讀取對象
Student st1 = (Student) ois.readObject(); //會拋出異常(類找不到異常)
System.out.println("name = " + st1.getName());
System.out.println("sex = " + st1.getSex());
System.out.println("year = " + st1.getYear());
System.out.println("gpa = " + st1.getGpa());
ois.close();
fis.close();
}catch (ClassNotFoundException e){
e.printStackTrace();
}
}
}
查看txt文件廉侧,結(jié)果如下:
sr JavaxulieHua.Studentd9Q藿Hf D gpaC sexI yearL namet Ljava/lang/String;xp@ 燙燙掏 M t Tom
可以看出其中的內(nèi)容是不容易閱讀的,只能通過反序列化讀取券腔。
四、transient關(guān)鍵字
transient關(guān)鍵字表示有理的拘泞,被修飾的數(shù)據(jù)不能進(jìn)行序列化
這里不做詳細(xì)介紹纷纫,修改情況如下:
private transient char sex; //被transient關(guān)鍵字修飾,不參與序列化
運(yùn)行結(jié)果如下:
文件存在
name = Tom
sex =
year = 20
gpa = 3.6
此時可以看見陪腌,被transient關(guān)鍵字修飾的變量sex并沒有被序列化辱魁,返回了空值。
五诗鸭、Externalizable接口實現(xiàn)序列化與反序列化
Externalizable接口繼承Serializable接口染簇,實現(xiàn)Externalizable接口需要實現(xiàn)readExternal()方法和writeExternal()方法,這兩個方法是抽象方法强岸,對應(yīng)的是serializable接口的readObject()方法和writeObject()方法锻弓,可以理解為把serializable的兩個方法抽象出來。Externalizable沒有serializable的限制蝌箍,static和transient關(guān)鍵字修飾的屬性也能進(jìn)行序列化青灼。
具體代碼實現(xiàn)如下:
復(fù)制對象student命名為student1,在里面重寫writeExternal()方法和readExternal()方法妓盲,如下:
@Override
//對抽象方法進(jìn)行重寫
public void writeExternal(ObjectOutput out) throws IOException{
out.writeObject(name);
out.writeObject(sex);
out.writeObject(year);
out.writeObject(gpa);
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
name = (String) in.readObject();
sex = (char) in.readObject();
year = (int) in.readObject();
gpa = (double) in.readObject();
}
相應(yīng)的測試方法里面調(diào)用這兩種方法的時候杂拨,直接調(diào)用writeObject()方法和readObject()方法即可,重寫的writeExternal()和readExternal()方法會自動執(zhí)行悯衬。
FileOutputStream fos1 = new FileOutputStream(file1);
ObjectOutputStream oos1 = new ObjectOutputStream(fos1);
//調(diào)用 ObjectOutputStream 中的 writeObject() 方法 寫對象
oos1.writeObject(st); //會自動執(zhí)行重寫的writeExternal()方法
FileInputStream fis1 = new FileInputStream(file1);
//創(chuàng)建對象輸入流
ObjectInputStream ois1 = new ObjectInputStream(fis1);
//讀取對象
//會自動執(zhí)行readExternal()方法
Student1 st1 = (Student1) ois1.readObject(); //會拋出異常(類找不到異常)
雖然student1類里的sex屬性被static或transient修飾弹沽,但依舊被序列化,結(jié)果如下:
文件存在
name = Tom
sex = M
year = 20
gpa = 3.6