一、為什麼要序列化?
1、一般情況下,只有當 JVM 處於運行時,Java 對象才可能存在,即這些對象的生命週期不會比 JVM 的生命週期更長。但在現實應用中,就可能要求在 JVM 停止運行之後能夠保存(持久化)指定的對象,並在將來重新讀取被保存的對象。Java 對象序列化就能夠幫助我們實現該功能。
2、在網絡或者進程通信中傳遞對象時,我們都需要使用序列化將 Java 對象轉換為字節序列傳輸,具體表現為:發送數據前序列化對象,接收數據後反序列化對象。
二、序列化是什麼?
序列化指的是允許將實現序列化的 Java 對象轉換位字節序列,這些字節序列可以保存在磁盤上,或通過網絡傳輸,以達到以後恢復成原來的對象。序列化機制使得對象可以脫離程序的運行而獨立存在。
通俗易懂的講,Java 序列化是指把 Java 對象轉換為字節序列的過程,而 Java 反序列化是指把字節序列恢復為 Java 對象的過程。
三、Java 序列化機制
1.使用 Serializable 接口實現序列化
在 Java 中, 只要一個類實現了 java.io.Serializable 接口,那麼它就可以被序列化。
<code>@Data
public class UserA implements Serializable {
/**
* 序列化ID
*/
private static final long serialVersionUID = 1L;
private int age;
private static String name = "張三";
@Override
public String toString() {
return "User{age=" + age + ",name=" + name + "}";
}
}/<code>
2.使用 Externalizable 接口實現序列化
Externalizable 繼承自 Serializable 接口,需要我們重寫 writeExternal() 與 readExternal() 方法來決定要序列化哪些信息,並且必須要提供一個 public 的無參的構造器。
Externalizable 的性能要優於 Serializable,但也增加了複雜性。
<code>@Data
public class UserB implements Externalizable {
/**
* 序列化ID
*/
private static final long serialVersionUID = 1L;
private int age;
private static String name = "李四";
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeInt(age);
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
age = in.readInt();
}
@Override
public String toString() {
return "User{age=" + age + ",name=" + name + "}";
}
}/<code>
3.序列化和反序列化
通過實現 Serializable 接口或者 Externalizable 接口,Java 對象已經具備序列化的資質了,那如何進行序列化和反序列化呢?這裡利用了 ObjectOutputStream 和 ObjectInputStream 對對象進行序列化及反序列化。
<code> public static void main(String[] args) {
// 序列化
serialize();
// 反序列化
deserialize();
}
private static void serialize() {
UserA user = new UserA();
user.setAge(26);
//序列化對象到文件中
try (ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("D:\\\\\\userA"))) {
objectOutputStream.writeObject(user);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
private static void deserialize() {
try (ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("D:\\\\\\userA"))) {
UserA user = (UserA) objectInputStream.readObject();
System.out.println(user);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}/<code>
四、序列化分析
- serialVersionUID,即序列化ID。虛擬機能否進行反序列化,不僅取決於類路徑和功能代碼是否一致,一個非常重要的一點是兩個類的序列化 ID 是否一致,否則反序列化時會拋出 InvalidClassException 異常。serialVersionUID 默認是 1L,可以不顯示指定。
- 靜態變量不會被序列化。因為靜態變量存在於虛擬機方法區,屬於全局變量,所以在序列化或者反序列時,並不會保存靜態變量。
- transient 關鍵字。作用是控制變量的序列化,在變量聲明前加上該關鍵字,可以阻止該變量被序列化到文件中,在被反序列化後,transient 變量的值被設為初始值,如 int 型的是 0,對象型的是 null。
- 父子類的序列化。要想將父類對象也序列化,就需要讓父類也實現 Serializable(或 Externalizable) 接口。
- 引用類型成員變量的序列化。要想引用對象也序列化,就需要讓引用對象也實現 Serializable(或 Externalizable) 接口。
閱讀更多 碼農阿崔 的文章