花裏胡哨的Java序列化機制

前言

通常在寫實體類時都默認繼承Serializable接口,但是有什麼用?今天就來深入瞭解下java序列化機制

序列化、反序列化的概念

序列化就是將java對象轉化成二進制保存到磁盤中去,反序列化就是從磁盤中讀取文件流然後轉成java對象

常用的方式及好處

  • 網絡通訊傳輸java對象數據->利用序列化實現遠程通信,即在網絡上傳送對象的字節序列
  • 永久保存java對象->實現了數據的持久化,通過序列化可以把數據永久地保存到硬盤上

Java序列化機制

  1. 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'}
  1. 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關鍵字及靜態變量的序列化

  1. 通過實現Externalizable接口可以自定義序列化的屬性,同樣,關鍵字transient也可以達到相同的效果
    但是兩點區別
  • transient修飾的變量,即使使用private修飾也會被序列化
  • 如果讓private屬性不被序列化,則使用Externalizable
  1. 靜態變量不會被序列化
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不同,將會報序列化出錯

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章