前言
通常在寫實體類時都默認繼承Serializable接口,但是有什麼用?今天就來深入瞭解下java序列化機制
序列化、反序列化的概念
序列化就是將java對象轉化成二進制保存到磁盤中去,反序列化就是從磁盤中讀取文件流然後轉成java對象
常用的方式及好處
- 網絡通訊傳輸java對象數據->利用序列化實現遠程通信,即在網絡上傳送對象的字節序列
- 永久保存java對象->實現了數據的持久化,通過序列化可以把數據永久地保存到硬盤上
Java序列化機制
- Serializable接口
- 創建StudentEntity 並實現Serializable 接口
public class StudentEntity implements Serializable {
private static final long serialVersionUID = 1L;
private String studentName;
//get set toString
serialVersionUID有兩種顯示的生成方式:
一是默認的1L,比如:private static final long serialVersionUID = 1L;
二是根據類名、接口名、成員方法及屬性等來生成一個64位的哈希字段,比如: private static final long serialVersionUID = xxxxL;
serialVersionUID的作用->目的是序列化對象版本控制,有關各版本反序列化時是否兼容
- 在Test類中去實現序列化和反序列化。
public class Test {
public static void main(String[] args) throws Exception, IOException {
SerializeStudent();
DeSerializeStudent();
}
//序列化方法
private static void SerializeStudent() throws FileNotFoundException, IOException {
StudentEntity studentEntity = new StudentEntity();
studentEntity.setStudentName("Kaleldo");
//序列化對象到文件中
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("C://Test/Student"));
oos.writeObject(studentEntity);
oos.close();
System.out.println("序列化對象成功");
}
//反序列化方法
private static void DeSerializeStudent() throws FileNotFoundException, IOException{
File file = new File("C://Test/Student");
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
StudentEntity studentEntity = null;
try {
studentEntity = (StudentEntity)ois.readObject();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
System.out.println("反序列化對象成功"+studentEntity.toString());
}
}
- 結果:在C://Test 目錄會發現一個Student文件 ,同時控制檯輸出以下內容
Connected to the target VM, address: '127.0.0.1:51227', transport: 'socket'
序列化對象成功
Disconnected from the target VM, address: '127.0.0.1:51227', transport: 'socket'
反序列化對象成功StudentEntity{studentName='Kaleldo'}
- Externalizable接口
- 實現Externalizable接口需注意3點
(1)Externalizable繼承自Serializable接口
(2)需重寫writeExternal()與readExternal()方法
(3)實現Externalizable接口的類必須要提供一個public的無參的構造器。
public class Student1Entity implements Externalizable {
private String studentName;
public Student1Entity() {
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeObject(studentName);
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
studentName = (String) in.readObject();
}
//get set toString
}
- 測試
public class Test {
public static void main(String[] args) throws Exception, IOException {
SerializeStudent();
DeSerializeStudent();
}
//序列化方法
private static void SerializeStudent() throws FileNotFoundException, IOException {
Student1Entity studentEntity = new Student1Entity();
studentEntity.setStudentName("Kaleldo");
//序列化對象到文件中
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("C://Test/Student1"));
oos.writeObject(studentEntity);
oos.close();
System.out.println("序列化對象成功");
}
//反序列化方法
private static void DeSerializeStudent() throws FileNotFoundException, IOException{
File file = new File("C://Test/Student1");
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
Student1Entity studentEntity = null;
try {
studentEntity = (Student1Entity)ois.readObject();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
System.out.println("反序列化對象成功"+studentEntity.toString());
}
}
-輸出
Connected to the target VM, address: '127.0.0.1:51328', transport: 'socket'
序列化對象成功
Disconnected from the target VM, address: '127.0.0.1:51328', transport: 'socket'
反序列化對象成功Student1Entity{studentName='Kaleldo'}
transient關鍵字及靜態變量的序列化
- 通過實現Externalizable接口可以自定義序列化的屬性,同樣,關鍵字transient也可以達到相同的效果
但是兩點區別
- transient修飾的變量,即使使用private修飾也會被序列化
- 如果讓private屬性不被序列化,則使用Externalizable
- 靜態變量不會被序列化
public class StudentEntity implements Serializable {
private static final long serialVersionUID = 1L;
private String studentName;
private static int studentNumber;
}
- 測試
public class Test {
public static void main(String[] args) throws Exception, IOException {
SerializeStudent();
StudentEntity.setStudentNumber(12);
DeSerializeStudent();
}
//序列化方法
private static void SerializeStudent() throws FileNotFoundException, IOException {
StudentEntity.setStudentNumber(13);
StudentEntity studentEntity = new StudentEntity();
studentEntity.setStudentName("Kaleldo");
//序列化對象到文件中
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("C://Test/Student1"));
oos.writeObject(studentEntity);
oos.close();
System.out.println("序列化對象成功");
}
//反序列化方法
private static void DeSerializeStudent() throws FileNotFoundException, IOException{
File file = new File("C://Test/Student");
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
StudentEntity studentEntity = null;
try {
studentEntity = (StudentEntity)ois.readObject();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
System.out.println("反序列化對象成功"+studentEntity.toString());
}
}
- 結果:即使在序列化之前修改了值爲13,但是還是輸出12,反序列輸出的靜態變量值變化了:說明靜態變量沒有被序列化了。
序列化對象成功
Disconnected from the target VM, address: '127.0.0.1:52125', transport: 'socket'
反序列化對象成功StudentEntity{studentName='Kaleldo'studentNumber='12'}
後話
1.實現序列化有兩種方式,一實現Serializable接口,二是實現Externalizable接口,Externalizable可以自定義序列化規則
2.static、transient修飾的字段不會被序列化
3.serialVersionUID相當於版本號,如果每次序列化的serialVersionUID不同,將會報序列化出錯