一、什么是序列化與反序列化?
Java 序列化是指把 Java 對象轉換為字節序列的過程;
Java 反序列化是指把字節序列恢復為 Java 對象的過程;
二、為什么要用序列化與反序列化?
在 為什么要用序列化與反序列化 之前我們先了解一下對象序列化的兩種用途:
1、把對象的字節序列永久地保存到硬盤上,通常存放在一個文件中;
2、在網絡上傳送對象的字節序列。
我們可以想想如果沒有序列化之前,又是怎樣一種情景呢?
舉例:
Web 服務器中的 Session 會話對象,當有10萬用戶並發訪問,就有可能出現10萬個 Session 對象,顯然這種情況內存可能是吃不消的。
於是 Web 容器就會把一些 Session 先序列化,讓他們離開內存空間,序列化到硬盤中,當需要調用時,再把保存在硬盤中的對象還原到內存中。
我們知道,當兩個進程進行遠程通信時,彼此可以發送各種類型的數據,包括文本、圖片、音頻、視頻等, 而這些數據都會以二進制序列的形式在網絡上傳送。
同樣的序列化與反序列化則實現了 進程通信間的對象傳送,發送方需要把這個Java對象轉換為字節序列,才能在網絡上傳送;接收方則需要把字節序列再恢復為Java對象。
初步總結:Java 序列化和反序列化,其一,實現了數據的持久化,通過序列化可以把數據永久的保存在硬盤上;其二,利用序列化實現遠程通信,即在網絡上傳遞對象的字節序列。
三、如何實現序列化與反序列化?
3.1 JDK 類庫中序列化 API
使用到JDK中關鍵類 ObjectOutputStream(對象輸出流) 和ObjectInputStream(對象輸入流)
ObjectOutputStream 類中:通過使用 writeObject(Object object) 方法,將對象以二進制格式進行寫入。
ObjectInputStream 類中:通過使用 readObject()方法,從輸入流中讀取二進制流,轉換成對象。
3.2 目標對象實現 Serializable 接口
我們創建一個 User 類,實現 Serializable 接口,並生成一個版本號 :
public class User implements Serializable {
private static final long serialVersionUID = 3604972003323896788L;
private transient int age;
private String name;
private String sex;
public int getAge() {
return age;
}
public String getName() {
return name;
}
public String getSex() {
return sex;
}
public void setAge(int age) {
this.age = age;
}
public void setName(String name) {
this.name = name;
}
public void setSex(String sex) {
this.sex = sex;
}
首先:
1、Serializable 接口的作用只是用來標識我們這個類是需要進行序列化,並且 Serializable 接口中並沒有提供任何方法。
2、SerialVersionUid 序列化版本號的作用是用來區分我們所編寫的類的版本,用於判斷反序列化時類的版本是否一直,如果不一致會出現版本不一致異常。
3、transient 關鍵字,主要用來忽略我們不希望進行序列化的變量
由於第一種形式太不常見,顧不再啰嗦演示,直接來看第二種實現 Serializable 接口的寫入方式:
定義一個Person類,實現Serializable接口
public class Person implements Serializable {
private static final long serialVersionUID = -5809452578272945389L;
private int age;
private String name;
private String sex;
get..
set...
}
序列化和反序列化Person類對象
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.text.MessageFormat;
/**
* <p>Description: 測試對象的序列化和反序列<p>
*/
public class TestObjSerializeAndDeserialize {
public static void main(String[] args) throws Exception {
/**序列化Person對象**/
SerializePerson();
/**反序列Perons對象**/
Person p = DeserializePerson();
System.out.println(MessageFormat.format("name={0},age={1},sex={2}",p.getName(), p.getAge(), p.getSex()));
}
/**
* Description: 序列化Person對象
*/
private static void SerializePerson() throws FileNotFoundException,
IOException {
Person person = new Person();
person.setName("gacl");
person.setAge(25);
person.setSex("男");
/** ObjectOutputStream 對象輸出流,將Person對象存儲到E盤的Person.txt文件中,完成對Person對象的序列化操作 **/
ObjectOutputStream oo = new ObjectOutputStream(new FileOutputStream(
new File("E:/Person.txt")));
oo.writeObject(person);
System.out.println("Person對象序列化成功!");
oo.close();
}
/**
* Description: 反序列Perons對象
*/
private static Person DeserializePerson() throws Exception, IOException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(
new File("E:/Person.txt")));
Person person = (Person) ois.readObject();
System.out.println("Person對象反序列化成功!");
return person;
}
}
代碼運行結果:
疑問:Person 實體中的 serialVersionUID 是個什么鬼?
答:序列化版本號,取值是 Java 運行時環境根據類的內部細節自動生成的。如果對類的源代碼作了修改,再重新編譯,新生成的類文件的 serialVersionUID 的取值有可能也會發生變化。。
既然程序幫我門自動生成,那為何還要去定義該屬性?
序列化和反序列化就是通過對比其 SerialversionUID 來進行的,我們修改一個實現 Serializable 接口的實體類,重新編譯后,顯然程序會重新會生成新值,那么一旦SerialversionUID 跟之前不匹配,反序列化就無法成功。
在實際的生產環境中,我們可能會建一系列的中間 Object 來反序列化我們的 pojo,為了解決這個問題,我們就需要在實體類中自定義 SerialversionUID,就像上方示例,不管我們序列化之后如何更改我們的 實體(不刪除原有字段),最終都可以反序列化成功。。
四、總結
什么是序列化,如何實現序列化?
Java 中對象的序列化就是將對象轉換成二進制序列,反序列化則是將二進制序列轉換成對象。
采用Java序列化與反序列化技術:
- 一是可以實現數據的持久化,在MVC模式中很是有用;
- 二是可以對象數據的遠程通信。
Java 實現序列化的多種方式
1、首先需要使用到工具類 ObjectInputStream 和ObjectOutputStream 兩個IO類
2、實現 Serializable 接口
3、實現 Externalizable 接口