1.序列化和反序列化
序 列 化:指把堆內存中的Java對象數據,通過某種方式把對象存儲到磁盤文件中或者傳遞給其他網絡的節點(在網絡上傳輸).我們把這個過程稱之爲序列化.
反序列化:把磁盤文件中的對象數據或者把網絡節點上的對象數據,恢復成Java對象的過程.
爲什麼要做序列化:
1):在分佈式系統中,需要共享的數據的JavaBean對象,都得做序列化,此時需要把對象再網絡上傳輸,此時就得把對象數據轉換爲二進制形式.以後存儲在HttpSession中的對象,都應該實現序列化接口(只有實現序列化接口的類,才能做序列化操作).
2):服務鈍化:如果服務發現某些對象好久都沒有活動了,此時服務器就會把這些內存中的對象,持久化在本地磁盤文件中(Java對象-->二進制文件).如果某些對象需要活動的時候,現在內存中去尋找,找到就使用,找不到再去磁盤文件中,反序列化我們得對象數據,恢復成Java對象.
需要做序列化的對象的類,必須實現序列化接口:java.io.Serializable接口(標誌接口[沒有抽象方法]).底層會判斷,如果當前對象是Serializable的實例,才允許做序列化. boolean ret = Java對象 instanceof Serializable;
在Java中大多數類都已經實現Serializable接口.
2.使用對象流來完成序列化和反序列化操作
ObjectOutputStream: 通過writeObject方法做序列化操作的.ObjectInputStream: 通過readObject方法做反序列化操作的.
做反序列化操作必須存在對象的字節碼對象.
下面我們來對一個User對象做序列化和反序列化
首先創建一個User對象,就是我們平時使用的javaBean
public class User{
private int id;
private String nameString;
private int age;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getNameString() {
return nameString;
}
public void setNameString(String nameString) {
this.nameString = nameString;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public User(int id, String nameString, int age) {
super();
this.id = id;
this.nameString = nameString;
this.age = age;
}
@Override
public String toString() {
return "User [id=" + id + ", nameString=" + nameString + ", age=" + age
+ "]";
}
}
測試:
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
public class IODemo7 {
public static void main(String[] args) throws Exception {
writeObject();
}
//使用對象流對User對象進行序列化操作
private static void writeObject() throws Exception {
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("user.txt"));
out.writeObject(new User(1,"張三",18));
out.close();
}
}
此時會以下報錯,爲什麼?這是因爲我們自己定義的User類還沒有實現序列化接口
這時我們就應該修改User類,讓它實現Serializable接口
import java.io.Serializable;
public class User implements Serializable{
private int id;
private String nameString;
private int age;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getNameString() {
return nameString;
}
public void setNameString(String nameString) {
this.nameString = nameString;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public User(int id, String nameString, int age) {
super();
this.id = id;
this.nameString = nameString;
this.age = age;
}
@Override
public String toString() {
return "User [id=" + id + ", nameString=" + nameString + ", age=" + age
+ "]";
}
}
這時運行就能成功將User對象寫入到文件中,然後我們打開肯定看不懂的,需要對象流輸入流來進行反序列化操作
下面我們進行反序列化
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class IODemo7 {
public static void main(String[] args) throws Exception {
writeObject();//序列化
readObject();//反序列化
}
//使用對象流對User對象進行序列化操作
private static void writeObject() throws Exception {
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("user.txt"));
out.writeObject(new User(1,"張三",18));
out.close();
}
//使用對象流對User對象進行反序列化操作
private static void readObject() throws Exception {
ObjectInputStream in = new ObjectInputStream(new FileInputStream("user.txt"));
User user = (User)in.readObject();
System.out.println(user);//User [id=1, nameString=張三, age=18]
}
}
打印結果:User [id=1, nameString=張三, age=18]3.序列化的細節序列化的版本
1):如果某些數據不需要做序列化,比如密碼,此時怎麼辦?理論上說,靜態的字段和瞬態的字段是不能做序列化操作的.
下面我們的User類添加一個password字段,然後在字段前面加上一個修飾符transient,看代碼
import java.io.Serializable;
public class User implements Serializable{
private int id;
transient private int password;//加上了transient修飾符
private String nameString;
private int age;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public void setPassword(int password) {
this.password = password;
}
public int getPassword() {
return password;
}
public String getNameString() {
return nameString;
}
public void setNameString(String nameString) {
this.nameString = nameString;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public User(int id, int password, String nameString, int age) {
super();
this.id = id;
this.password = password;
this.nameString = nameString;
this.age = age;
}
@Override
public String toString() {
return "User [id=" + id + ", password=" + password + ", nameString="
+ nameString + ", age=" + age + "]";
}
}
這時候的password就不會被序列化
2):序列化的版本問題:
反序列化Java對象時必須提供該對象的class文件,現在問題是,隨着項目的升級,系統的class文件也會升級(增加一個字段/刪除一個字段),如何保證兩個class文件的兼容性? Java通過serialVersionUID(序列化版本號)來判斷字節碼是否發生改變.如果不顯示定義serialVersionUID類變量,該類變量的值由JVM根據類相關信息計算,而修改後的類的計算方式和之前往往不同.從而造成了對象反序列化因爲版本不兼容而失敗的問題.
此時我們需要給我們的User加上一個序列化ID就可以了
那麼以後我們無論添加或者修改字段之後都不會在報錯,要保證序列化ID相同
總結:其實在開發中,我們只需要對我們自己定義的類加上序列化ID就行了,至於序列化和反序列化這個對象,框架會幫我們完成!!!