小白學(xué)做網(wǎng)站買什么書哪里能搜索引擎優(yōu)化
一.概念
Serialization(序列化)是一種將對(duì)象以一連串的字節(jié)描述的過程;反序列化deserialization是一種將這些字節(jié)重建成一個(gè)對(duì)象的過程。將程序中的對(duì)象,放入文件中保存就是序列化,將文件中的字節(jié)碼重新轉(zhuǎn)成對(duì)象就是反序列化
二.要求
只有實(shí)現(xiàn)了Serializable或Externalizable接口的類的對(duì)象才能被序列化,并且序列化對(duì)象的所有屬性都需是可序列化的。
三.實(shí)現(xiàn)
serializable接口
1.1方法
序列化:創(chuàng)建一個(gè)ObjectOutputStream輸出流,調(diào)用 ObjectOutputStream 對(duì)象的 writeObject() 輸出可序列化對(duì)象
? ?反序列化:創(chuàng)建一個(gè)ObjectInputStream輸出流,調(diào)用 ObjectInputStream 對(duì)象的 readObject()得到反序列化的對(duì)象
1.2代碼
import java.io.*;
import java.lang.reflect.Method;class User implements Serializable{private String name;private int age;@Overridepublic String toString(){return "User{" + "name=" +name + ", age="+age+"}";}public void setName(String name) {this.name = name;}public void setAge(int age) {this.age = age;}
}
public class Main {public static void main(String[] args) throws Exception {ObjectOutputStream out=new ObjectOutputStream(new FileOutputStream("E:\\tao.txt"));//創(chuàng)建一個(gè)ObjectOutputStream流,將序列化對(duì)象輸出到tao.txtUser user=new User();user.setName("tao");user.setAge(20);//實(shí)例化Userout.writeObject(user);ObjectInputStream in=new ObjectInputStream(new FileInputStream("E:\\tao.txt"));// 創(chuàng)建一個(gè) ObjectOutputStream 輸出流User tao=(User)in.readObject();//將readObject反序列化的結(jié)果轉(zhuǎn)化成User類,實(shí)例成taoSystem.out.println(tao);}
}
User{name=tao, age=20}
1.3注意
①如果實(shí)現(xiàn) Serializable 接口的類有父類,則父類也必須可以序列化,若父類沒有實(shí)現(xiàn)序列化接口,則父類必須有無參構(gòu)造函數(shù),否則會(huì)拋異常 java.io.InvalidClassException。因?yàn)樵诟割悰]有實(shí)現(xiàn) Serializable 接口時(shí),虛擬機(jī)是不會(huì)序列化父對(duì)象的,而一個(gè) Java 對(duì)象的構(gòu)造必須先有父對(duì)象,才有子對(duì)象,反序列化也不例外。所以反序列化時(shí),為了構(gòu)造父對(duì)象,只能調(diào)用父類的無參構(gòu)造函數(shù)作為默認(rèn)的父對(duì)象。因此當(dāng)我們?nèi)「笇?duì)象的變量值時(shí),它的值是調(diào)用父類無參構(gòu)造函數(shù)后的值。如果沒有在父類無參構(gòu)造函數(shù)中對(duì)父類變量進(jìn)行初始化的話,父類變量值都是默認(rèn)聲明的值,如 int 型的默認(rèn)是 0,string 型的默認(rèn)是 null。
②序列化不保存靜態(tài)變量,因?yàn)樾蛄谢4娴氖菍?duì)象的狀態(tài)而不是類的狀態(tài),靜態(tài)變量是類的狀態(tài)
③ 使用transient 關(guān)鍵字可以選擇不需要序列化的字段
如:
private transient String name;
private transient int age;
進(jìn)行序列化的時(shí)候,name和age都不會(huì)被保存
Externalizable接口
2.1注意
①Externalizable接口繼承Serializable 接口
②writeExternal()和readExternal()對(duì)應(yīng)writeObject()和readObject()兩個(gè)方法
③Externalizable序列化沒有屬性限制,靜態(tài)變量以及transient 關(guān)鍵字修飾的屬性都能被序列化
④必須提供public的無參構(gòu)造方法,因?yàn)樵诜葱蛄谢瘜?shí)現(xiàn) Externalizabale 接口的類的時(shí)需要通過反射創(chuàng)建對(duì)象。如果沒有無參數(shù)的構(gòu)造方法,在運(yùn)行時(shí)會(huì)拋出異常:java.io.InvalidClassException
2.2代碼
import java.io.*;
import java.lang.reflect.Method;class User implements Externalizable{private String name;private int age;public User(){}//加上public無參構(gòu)造器@Overridepublic String toString(){return "User{" + "name=" +name + ", age="+age+"}";}public void setName(String name) {this.name = name;}public void setAge(int age) {this.age = age;}@Override//重寫writeExternal()方法public void writeExternal(ObjectOutput out) throws IOException{out.writeObject(name);}@Override//重寫wreadExternal()方法public void readExternal(ObjectInput in) throws IOException,ClassNotFoundException{name=(String)in.readObject();}}
public class Main {public static void main(String[] args) throws Exception {ObjectOutputStream out=new ObjectOutputStream(new FileOutputStream("E:\\tao.txt"));//創(chuàng)建一個(gè)ObjectOutputStream流,將序列化對(duì)象輸出到tao.txtUser user=new User();user.setName("tao");user.setAge(20);//實(shí)例化Userout.writeObject(user);ObjectInputStream in=new ObjectInputStream(new FileInputStream("E:\\tao.txt"));// 創(chuàng)建一個(gè) ObjectOutputStream 輸出流User tao=(User)in.readObject();//將readObject反序列化的結(jié)果轉(zhuǎn)化成User類,實(shí)例成taoSystem.out.println(tao);}
}
result
User{name=tao, age=0}
age變成了0
因?yàn)槭褂肊xternalizable接口,需要重寫writeExternal() 與 readExternal() 方法,我只寫了name的實(shí)現(xiàn),沒有寫age,int型默認(rèn)值為0
四.安全
java反序列化會(huì)自動(dòng)觸發(fā)readObject()方法,類似于php反序列化的__destruct()函數(shù)
java支持自定義writeObject()和readObject()方法
如果某個(gè)類中自定義了readObject()方法,當(dāng)對(duì)其的一個(gè)實(shí)例化對(duì)象進(jìn)行反序列化,就會(huì)調(diào)用readObject()方法
import java.io.*;
import java.lang.reflect.Method;class User implements Serializable{private String name;private int age;@Overridepublic String toString(){return "User{" + "name=" +name + ", age="+age+"}";}public void setName(String name) {this.name = name;}public void setAge(int age) {this.age = age;}private void readObject(ObjectInputStream in){System.out.println("這是新的readObject!");}
}
public class Main {public static void main(String[] args) throws Exception {ObjectOutputStream out=new ObjectOutputStream(new FileOutputStream("E:\\tao.txt"));//創(chuàng)建一個(gè)ObjectOutputStream流,將序列化對(duì)象輸出到tao.txtUser user=new User();user.setName("tao");user.setAge(20);//實(shí)例化Userout.writeObject(user);ObjectInputStream in=new ObjectInputStream(new FileInputStream("E:\\tao.txt"));// 創(chuàng)建一個(gè) ObjectOutputStream 輸出流User tao=(User)in.readObject();//將readObject反序列化的結(jié)果轉(zhuǎn)化成User類,實(shí)例成taoSystem.out.println(tao);}
}
結(jié)果
這是新的readObject!
User{name=null, age=0}
可見在反序列化的時(shí)候?qū)崿F(xiàn)了新的readObject()
那么就可以命令執(zhí)行了
private void readObject(ObjectInputStream in) throws IOException{Runtime.getRuntime().exec("calc");}
彈計(jì)算器了!
這里初步了解java序列化與反序列話,后續(xù)構(gòu)造鏈會(huì)繼續(xù)學(xué)習(xí)。
參考博客
java基礎(chǔ)知識(shí)點(diǎn)2:序列化與反序列化詳解_java序列化和反序列化-CSDN博客
javasec/2.java序列化與反序列化.md at master · Maskhe/javasec (github.com)