java序列化与深度拷贝
【README】
- 1, 為啥要序列化或序列化的意義?
- 2,系統(tǒng)間調(diào)用的報文格式,大多數(shù)是Json字符串(或字節(jié)數(shù)組);接收方接收json;
- 3,但當(dāng)系統(tǒng)調(diào)用如RMI,客戶端請求服務(wù)器獲取一個 javabean對象的狀態(tài)信息,而不是json格式,這個時候 json格式就不合適了;當(dāng)然了,在發(fā)送時我們可以 把 javabean的狀態(tài)信息的屬性轉(zhuǎn)為json;接收時把json對象轉(zhuǎn)為 javabean;很顯然,這樣太復(fù)雜了,而且不利于維護(hù),一旦 javabean的屬性做修改,需要改的代碼太多;
- 4,基于此, 序列化出現(xiàn)了,序列化可以把對象信息轉(zhuǎn)換為字節(jié)數(shù)組,保存到媒介中(如磁盤,內(nèi)存,或傳輸?shù)骄W(wǎng)絡(luò)),后續(xù)需要用到的時候,可以直接把字節(jié)數(shù)組反序列化為java對象,而不需要做其他操作;也可以理解為是 java對象持久化;
- 這可以解決 系統(tǒng)調(diào)用間 javabean對象信息傳輸?shù)膯栴};即請求前,把對象序列化為字節(jié)數(shù)組;接收時,把字節(jié)數(shù)組反序列化(直接解析)為java bean對象(而不需要其他格式轉(zhuǎn)換);
- 5,序列化的另一個用途是 用于java對象的深拷貝;
【補(bǔ)充1】本文給出了3種實(shí)現(xiàn)序列化的方式,包括
- 1種自動方式(實(shí)現(xiàn) Serializable 接口);
- 2種手動方式(分別實(shí)現(xiàn) Externalizable, Serializable 接口);
【補(bǔ)充2】自動實(shí)現(xiàn)與手動實(shí)現(xiàn)
- 自動實(shí)現(xiàn): 程序員無需自定義字段序列化策略,由 java底層默認(rèn)實(shí)現(xiàn);
- 手動實(shí)現(xiàn):程序員需要自定義自定序列化策略,哪些需要序列化,哪些不需要;
【1】java序列化實(shí)現(xiàn)
1)java序列化定義: 把那些實(shí)現(xiàn)了 Serializable 接口的對象轉(zhuǎn)換成一個字節(jié)序列;并能夠在以后把這個字節(jié) 序列完全恢復(fù)到原來的對象;實(shí)現(xiàn)對 javabean狀態(tài)的持久化;
2)如何理解持久性? 持久性意味著一個對象的生命周期并不取決于 程序是否在執(zhí)行;就像數(shù)據(jù)庫的持久化一樣;但一個對象可以存在于 程序的調(diào)用之間,如遠(yuǎn)程方法調(diào)用 RMI;
3)通過序列化可以把java對象以字節(jié)格式保存到磁盤,內(nèi)存,網(wǎng)絡(luò)流上面;在以后需要的時候,把序列化后的字節(jié)恢復(fù)(或反序列化)成原來的 javabean的狀態(tài);
【2】 序列化實(shí)現(xiàn)
【2.1】實(shí)現(xiàn) Serializable 接口自動實(shí)現(xiàn)序列化
?javabean序列化
/*** @Description 實(shí)現(xiàn)Serializable自動實(shí)現(xiàn)序列化與反序列化* @author xiao tang* @version 1.0.0* @createTime 2021年11月20日*/ public class AutoSerializableClass implements Serializable {String name;Date date;Map<String, String> configs;// transient 表示不序列化agetransient int age;public AutoSerializableClass(String name) {this(name, Collections.emptyMap(), new Date(), 18);}public AutoSerializableClass(String name, Map<String, String> configs) {this(name, configs, new Date(), 18);}public AutoSerializableClass(String name, Map<String, String> configs, Date date, int age) {this.name = name;this.configs = configs;this.date = date;this.age = age;}public String getName() {return name;}public Map<String, String> getConfigs() {return configs;}/*** @description 基于序列化的深度拷貝* @return AutoSerializableClass* @author xiao tang* @date 2021/11/20*/public AutoSerializableClass deepClone() throws Exception {ByteArrayOutputStream buf = new ByteArrayOutputStream();// 把流放在 tyr資源塊中,程序結(jié)束java會自動關(guān)閉流資源try (ObjectOutputStream outputStream = new ObjectOutputStream(buf)) {outputStream.writeObject(this);}try (ObjectInputStream inputStream = new ObjectInputStream(new ByteArrayInputStream(buf.toByteArray()))) {return (AutoSerializableClass) inputStream.readObject();}} @Overridepublic String toString() {return "AutoSerializableClass{" +"name='" + name + '\'' +", date=" + date +", configs=" + configs +", age=" + age +'}';} }【2.1.1】序列化到磁盤與反序列
public static String path = "d://temp//"; /*** @description 測試用例-把 AutoSerializableClass 對象序列化到磁盤* @author xiao tang* @date 2021/11/20*/public static void main(String[] args) throws IOException, ClassNotFoundException {// 把 AutoSerializableClass 對象序列化到磁盤ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream(path + "my01.out"));outputStream.writeObject(new AutoSerializableClass("zhangsan"));outputStream.close();// 從磁盤讀取序列化對象ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(path + "my01.out"));AutoSerializableClass objParsed = (AutoSerializableClass) objectInputStream.readObject();System.out.println(objParsed.getName()); // zhangsan}【2.1.2】深度拷貝
/** * @description 測試用例-基于 Serializable接口自動實(shí)現(xiàn)序列化的深度拷貝* @author xiao tang* @date 2021/11/20 */public static void main(String[] args) throws Exception {Map<String, String> configs = new HashMap<>();configs.put("k1", "v1");AutoSerializableClass lisi = new AutoSerializableClass("lisi", configs);// 深度拷貝到lisi2 和 lisi3AutoSerializableClass lisi2 = lisi.deepClone();AutoSerializableClass lisi3 = lisi.deepClone();// 為lisi2添加配置lisi2.getConfigs().put("k2", "v2");System.out.println("\n lisi2==="); // configs={k1=v1, k2=v2}, age=0System.out.println(lisi2);System.out.println("\n lisi3==="); // configs={k1=v1}, age=0System.out.println(lisi3);// 為lisi3添加配置lisi3.getConfigs().put("k3", "v3");System.out.println("\n lisi2==="); // configs={k1=v1, k2=v2}, age=0System.out.println(lisi2);System.out.println("\n lisi3==="); // configs={k1=v1, k3=v3}, age=0 System.out.println(lisi3);}?lisi2===
AutoSerializableClass{name='lisi', date=Sat Nov 20 21:43:50 CST 2021, configs={k1=v1, k2=v2}, age=0}
?lisi3===
AutoSerializableClass{name='lisi', date=Sat Nov 20 21:43:50 CST 2021, configs={k1=v1}, age=0}
?lisi2===
AutoSerializableClass{name='lisi', date=Sat Nov 20 21:43:50 CST 2021, configs={k1=v1, k2=v2}, age=0}
?lisi3===
AutoSerializableClass{name='lisi', date=Sat Nov 20 21:43:50 CST 2021, configs={k1=v1, k3=v3}, age=0}
【代碼解說】
- 1,可以看到? lisi2 lisi3 是獨(dú)立的兩個對象,且反序列化后,lisi2,lisi3 定義的 configs 不是同一個 ;
- 2,age 本身初始化為18,但這里為0,是因?yàn)?age 被修改為 transient,表示不序列該字段,故age取int默認(rèn)值0;
- 3,接口 Serializable 沒有任何方法,僅一個標(biāo)識接口而已;
【2.2】實(shí)現(xiàn)? Externalizable 接口手動實(shí)現(xiàn)序列化
1,Externalizable接口繼承自? Serializable, 有2個方法需要實(shí)現(xiàn);
2,這兩個方法 writeExternal 和 readExternal 方法 會分別在序列化和反序列化還原的過程中被自動調(diào)用,以便執(zhí)行一些特殊操作(對某些字段進(jìn)行序列化與反序列化,某些不進(jìn)行如賬號密碼,private字段不序列化等) ;如 我們可以對age 字段進(jìn)行序列化和反序列化,即便age被 transient 修飾;
public interface Externalizable extends java.io.Serializable {void writeExternal(ObjectOutput out) throws IOException; void readExternal(ObjectInput in) throws IOException, ClassNotFoundException; }3,Externalizable 實(shí)現(xiàn)類
/*** @Description 實(shí)現(xiàn)Externalizable手動實(shí)現(xiàn)序列化與反序列化* @author xiao tang* @version 1.0.0* @createTime 2021年11月20日*/ public class ManualExternalizableClass implements Externalizable {String name;Date date;Map<String, String> configs;// transient 表示不序列化agetransient int age;public ManualExternalizableClass(){}public ManualExternalizableClass(String name) {this(name, new Date(), new HashMap<String, String>(), 18);}public ManualExternalizableClass(String name, Date date, Map<String, String> configs, int age) {this.name = name;this.date = date;this.configs = configs;this.age = age;}/** * @description 序列化過程自動調(diào)用* @param out 序列化書寫流* @author xiao tang* @date 2021/11/20 */@Overridepublic void writeExternal(ObjectOutput out) throws IOException {out.writeObject(name);out.writeObject(date);out.writeInt(age);}/** * @description 反序列化過程自動調(diào)用* @param in 反序列化讀入流* @author xiao tang* @date 2021/11/20 */@Overridepublic void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {this.name = String.valueOf(in.readObject());this.date = (Date) in.readObject();this.age = in.readInt();}/*** @description 深度拷貝 ManualExternalizableClass* @return MyExternalizableClass對象* @author xiao tang* @date 2021/11/20*/public ManualExternalizableClass deepClone() throws Exception {ByteArrayOutputStream buf = new ByteArrayOutputStream();try (ObjectOutputStream outputStream = new ObjectOutputStream(buf)) {outputStream.writeObject(this);}try (ObjectInputStream inputStream = new ObjectInputStream(new ByteArrayInputStream(buf.toByteArray()))) {return (ManualExternalizableClass) inputStream.readObject();}}public void setAge(int age) {this.age = age;}@Overridepublic String toString() {return "ManualExternalizableClass{" +"name='" + name + '\'' +", date=" + date +", configs=" + configs +", age=" + age +'}';} }【2.2.1】 測試用例,對 Externalizable 對象進(jìn)行深拷貝
/*** @Description 測試用例-實(shí)現(xiàn)Externalizable手動實(shí)現(xiàn)序列化與反序列化* @author xiao tang* @version 1.0.0* @createTime 2021年11月20日*/ public class ManualExternalizableClassTest {public static void main(String[] args) throws Exception {ManualExternalizableClass obj = new ManualExternalizableClass("zhangsan");// 深度拷貝ManualExternalizableClass newObj = obj.deepClone();newObj.setAge(19); // 對新對象的age賦值,不影響老對象System.out.println("obj = " + obj); // age=18System.out.println("newObj = " + newObj); // age=19} }obj = MyExternalizableClass{name='zhangsan', date=Sat Nov 20 20:32:15 CST 2021, configs={}, age=18}
newObj = MyExternalizableClass{name='zhangsan', date=Sat Nov 20 20:32:15 CST 2021, configs=null, age=19}
【代碼解說】
- 1,上述javabean MyExternalizableClass實(shí)現(xiàn)了 Externalizable 接口的方法 writeExternal() ,序列化字段name,date, age;實(shí)現(xiàn)了方法 readExternal(),反序列化字段 name, date, age;即便age 被 transient 修飾,但我們還可以序列化;
- 2,這種方式的序列化,需要指定序列化的字段,本文定義為 手動序列化方式;
【2.3】實(shí)現(xiàn)Serializable手動實(shí)現(xiàn)序列化
1)序列化javabean
/*** @Description 實(shí)現(xiàn) Serializable 手動實(shí)現(xiàn)序列化與反序列化* @author xiao tang* @version 1.0.0* @createTime 2021年11月20日*/ public class ManualSerializableClass implements Serializable {String name;Date date;Map<String, String> configs;// transient 表示不序列化agetransient int age;public ManualSerializableClass(String name) {this(name, new HashMap<>(), new Date(), 18);}public ManualSerializableClass(String name, Map<String, String> configs) {this(name, configs, new Date(), 18);}public ManualSerializableClass(String name, Map<String, String> configs, Date date, int age) {this.name = name;this.configs = configs;this.date = date;this.age = age;}public String getName() {return name;}public Map<String, String> getConfigs() {return configs;}/** * @description 序列化過程自動調(diào)用(注意這里并不是重寫 Serializable接口方法,即便沒有重寫,序列化過程也會自動調(diào)用,算是java一個缺陷,from thingking-in-java)* @param outputStream 書寫流* @author xiao tang* @date 2021/11/20 */private void writeObject(ObjectOutputStream outputStream) throws IOException {outputStream.defaultWriteObject(); // 先調(diào)用默認(rèn)序列化方式outputStream.writeInt(age); // 把a(bǔ)ge 進(jìn)行手動序列化}/** * @description 反序列化過程自動調(diào)用(注意這里并不是重寫 Serializable接口方法,即便沒有重寫,反序列化過程也會自動調(diào)用,算是java一個缺陷,from thingking-in-java)* @param inputStream 讀入流* @author xiao tang* @date 2021/11/20 */private void readObject(ObjectInputStream inputStream) throws IOException, ClassNotFoundException {inputStream.defaultReadObject(); // 先調(diào)用默認(rèn)反序列化方式age = inputStream.readInt(); // 把 age 手動反序列化}/*** @description 基于序列化的深度拷貝* @return AutoSerializableClass* @author xiao tang* @date 2021/11/20*/public ManualSerializableClass deepClone() throws Exception {ByteArrayOutputStream buf = new ByteArrayOutputStream();try (ObjectOutputStream outputStream = new ObjectOutputStream(buf)) {outputStream.writeObject(this);}try (ObjectInputStream inputStream = new ObjectInputStream(new ByteArrayInputStream(buf.toByteArray()))) {return (ManualSerializableClass) inputStream.readObject();}}@Overridepublic String toString() {return "AutoSerializableClass{" +"name='" + name + '\'' +", date=" + date +", configs=" + configs +", age=" + age +'}';} }2)測試用例
/*** @Description 測試用例-實(shí)現(xiàn) Serializable 手動實(shí)現(xiàn)序列化與反序列化* @author xiao tang* @version 1.0.0* @createTime 2021年11月20日*/ public class ManualSerializableClassTest {public static void main(String[] args) throws Exception {ManualSerializableClass oldObj = new ManualSerializableClass("zhangsan");ManualSerializableClass newObj = oldObj.deepClone();newObj.getConfigs().put("newK", "newV");System.out.println("oldObj=" + oldObj); // configs={}, age=18System.out.println("newObj=" + newObj); // configs={newK=newV}, age=18} }oldObj=MySerializableClass{name='zhangsan', date=Sat Nov 20 21:06:37 CST 2021, configs={}, age=18}
newObj=MySerializableClass{name='zhangsan', date=Sat Nov 20 21:06:37 CST 2021, configs={newK=newV}, age=18}
【小結(jié)】
?
總結(jié)
以上是生活随笔為你收集整理的java序列化与深度拷贝的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 龙珠电脑图标(龙珠电脑图标图片)
- 下一篇: java内部类小结