java 中的 Serializable 接口是用于實作對象 序列化和反序列化 的功能。那麼什麼是序列化和反序列化呢?
序列化: 就是将對象的目前狀态寫入位元組流,用于儲存或傳輸的過程
反序列化:則是從位元組流中恢複對象,是序列化的逆過程
通過序列化,能夠将目前 java 對象儲存至檔案,或通過網絡傳輸,并在需要時通過反序列化進行恢複。
序列化和反序列化
隻有實作了 Serializable 接口的類才能通過序列化功能進行儲存和恢複。下面進行示例說明:
下面定義了一個名為 UserInfo 的類,并實作 Serializable 接口
public class UserInfo implements Serializable {
private static final long serialVersionUID = -7701101573245777694L;
private String username;
private String password;
public UserInfo(String username, String password) {
this.username = username;
this.password = password;
}
@Override
public String toString() {
return "UserInfo [ username=" + username + ", password=" + password + "]";
}
}
通過 ObjectOutputStream 進行序列化輸出
public class SerializeDemo {
public static void main(String[] args) {
UserInfo userinfo = new UserInfo("eric", "eric_123456");
try (FileOutputStream fos = new FileOutputStream(new File("demo.txt"));
ObjectOutputStream oos = new ObjectOutputStream(fos)) {
oos.writeObject(userinfo);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
我們看下demo.txt檔案夾内容
��sr*com.eric.learning.java._serialize.UserInfo� 0脎{肉��Lpasswordt�Ljava/lang/String;Lusernameq~�xpt�eric_123456t�eric
序列化後的檔案無法直接檢視,但是檔案中可以看到上文設定的成員變量值 eric 和 eric_123456
我們再通過 ObjectInputStream 進行反序列化恢複:
public class SerializeDemo {
public static void main(String[] args) {
UserInfo userinfo = null;
try (FileInputStream fis = new FileInputStream(new File("demo.txt"));
ObjectInputStream ois = new ObjectInputStream(fis)) {
userinfo = (UserInfo) ois.readObject();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
System.out.println(userinfo);
}
}
輸出結果為:
UserInfo [username=eric, password=eric_123456]
serialVersionUID
在示例中,我們在實作 Serializable 接口時,生成了一個變量 serialVersionUID ,這個變量是幹嘛用的呢?
實際上這個變量是可序列化類的一個版本辨別。 在序列化對象時,對象的 serialVersionUID 會被一同寫入到位元組流,而反序列化時 JVM 會将位元組流中讀取的 serialVersionUID 同類的 serialVersionUID 比較,如果不相同則會反序列化失敗。
如果實作了 Serializable 接口,但不指定 serialVersionUID 的值,那麼編譯器在編譯時會自動生成一個 serialVersionUID.
示例:
假設我們增加了 Userinfo 中的字段:
public class UserInfo implements Serializable {
private static final long serialVersionUID = -7701101573245777694L;
private Long id;
private String username;
private String password;
}
更新後,我們希望舊 UserInfo 對象的序列化結果,無法再反序列化到新 UserInfo 類對象,這時我們可以重新生成 serialVersionUID 讓舊對象的序列化結果失效。
public class UserInfo implements Serializable {
// before
// private static final long serialVersionUID = -7701101573245777694L;
// now
private static final long serialVersionUID = -7701101573245777694L
private Long id;
private String username;
private String password;
}
這時如果反序列化舊 UserInfo 對象,将抛以下錯誤:
java.io.InvalidClassException: com.eric.learning.java._serialize.UserInfo; local class incompatible: stream classdesc serialVersionUID = -7701101573245777694, local class serialVersionUID = -7701101573245777695
at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:616)
at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1829)
at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1713)
transient
在 UserInfo 類中,有個用于存儲使用者密碼的 password 字段,密碼屬于私密資訊,我們不希望其被随意洩露。這時我們可以使用 transient 關鍵字。
用 transient 修飾的字段,在序列化時将被忽略:
public class UserInfo implements Serializable {
private static final long serialVersionUID = -7701101573245777694L;
private String username;
private transient String password; // transient 修飾
public UserInfo(String username, String password) {
this.username = username;
this.password = password;
}
}
再次序列化輸出到檔案:
public class SerializeDemo {
public static void main(String[] args) {
UserInfo userinfo = new UserInfo("eric", "eric_123456");
try (FileOutputStream fos = new FileOutputStream(new File("demo.txt"));
ObjectOutputStream oos = new ObjectOutputStream(fos)) {
oos.writeObject(userinfo);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
檔案内容中已經沒有 password 内容:
��sr*com.eric.learning.java._serialize.UserInfo� 0脎{肉��Lusernamet�Ljava/lang/String;xpt�eric