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