Java序列化與反序列化



Java序列化與反序列化


Java提供了兩種對象持久化的方式,分別為序列化和外部序列化

    • 序列化

      在分布式環境下,當進行遠程通信時,無論是何種類型的數據,都會以二進制序列的形式在網絡上傳輸。序列化是一種將對象以一連串的字節描述的過程,用於解決在對對象流進行讀寫操作時所引發的問題。序列化可以將對象的狀態寫在流里進行網絡傳輸,或者保存到文件、數據庫等系統中,並在需要時把該流讀取出來重新構造一個相同的對象。

      所有實現序列化的類都必須實現Serializable接口,Serializable接口位於java.lang包中,沒有任何實現方法,使用一個輸出流(例如FileOutputStream)來構造一個ObjectOutputStream對象,緊接着使用該對象的writeObject(Object obj)方法就可以將obj對象寫出,要恢復時可以使用其對應的輸入流ObjectInputStream.

序列化有以下兩個特點:

    1. 如果一個類能被序列化,那么它的子類也能夠被序列化。
    2. 由於static(靜態)代表類的成員,transient(Java關鍵字,如果用transient聲明一個實例變量,當對象存儲時,它的值不需要維持)代表對象的臨時數據,因此被聲明為這兩種類型的數據成員是不能夠被序列化的。

      如下是序列化的代碼,首先聲明一個Student類繼承Serializable接口,由代碼中的輸出(注釋即為輸出內容)可得靜態變量SCHOOLNAME和SCHOOLID的值發生了變化,難道是靜態變量也被序列化了嗎?其實不是的,因為在當前的運行程序中,Student類的靜態變量的值已經發生了變化,如果真的已經序列化了,那么我們將序列化的那個函數去掉,讓程序從SourceFile/Student文件中反序列化,那么得到的SCHOOLNAME應該為HeBei University,SCHOOLID的值為2,然而當我們去掉序列化代碼,直接從文件反序列化,輸出SCHOOLNAME是Beijing University of Post and Telecommunications,SCHOOLID為1,說明static變量並沒有被序列化。

public class Student implements Serializable{
    /**
     * 
     */
    private static final long serialVersionUID = 1L;
    public String name;
    public int id;
    public static String SCHOOLNAME = "Beijing University of Post and Telecommunications";
    private static int SCHOOLID = 1;
    public Student(){
        name = "zhangsan";
        id = 2018111846;
    }
    public Student(String name, int id){
        this.name = name;
        this.id = id;
    }
    public static void setSchool(String school_name, int school_id){
        SCHOOLID = school_id;
        SCHOOLNAME = school_name;
    }
    public static int getSchoolID(){
        return SCHOOLID;
    }
}


public class Test {

    /**
     * @param args
     */
    public static void main(String[] args) {
        Student student = new Student();
        student.setSchool("Hebei University",2);
        serialize(student);
        deserialize();
    }
    /**
     * 序列化student對象
     * @param student
     */
    private static void serialize(Student student){
        try {
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("SourceFile/Student"));
            oos.writeObject(student);
            oos.close();
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    
    /**
     * 從文件反序列化student對象
     */
    private static void deserialize(){
        try {
            FileInputStream fis = new FileInputStream("SourceFile/Student");
            Student student = (Student) new ObjectInputStream(fis).readObject();
            System.out.println(student.name);     //輸出zhangsan
            System.out.println(student.id);       //輸出2018111846
            System.out.println(student.SCHOOLNAME);    //輸出HeBei University
            System.out.println(student.getSchoolID()); //輸出2
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}
    • 外部序列化

      Java語言還提供了另外一種方式來實現對象持久化,即外部序列化。外部序列化與序列化的主要區別在於序列化是內置的API,只需要實現Serializable接口,開發人員不需要編寫任何代碼就可以實現對象的序列化,而是用外部序列化時,Externalizable接口中的方法必須有開發人員實現。因此與實現Serializable接口的方法相比,使用Externalizable編寫程序的難度更大,但是由於控制權交給了開發者,在編程時有更多的靈活性,對需要持久化的那些屬性可以進行控制,可能提高程序的性能。

如下是外部序列化的代碼,當把序列化部分的代碼注釋到之后,發現薪金的輸出還是10000,則可以確定當執行外部序列化時,static靜態變量也被序列化了,而且方法中沒有序列化id屬性,則反序列化后發現id並沒有發生變化:

public class Teacher implements Externalizable{
    private String name;
    private int id;
    private static int salary = 5000;
    public Teacher(){
        name = "zhangsan";
        id = 182566;
    }
    public static void setSalary(int s){
        salary =s;
    }
    public int getID(){
        return id;
    }
    public void setID(int id){
        this.id = id;
    }
    @Override
    public void readExternal(ObjectInput in) throws IOException,
            ClassNotFoundException {
        // TODO Auto-generated method stub
        Date date = (Date)in.readObject();
        name = (String)in.readObject();
        salary = (Integer)in.readObject();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        System.out.println(sdf.format(date));
    }

    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        // TODO Auto-generated method stub
        Date date = new Date();
        out.writeObject(date);
        out.writeObject(name);
        out.writeObject(salary);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        System.out.println(sdf.format(date));
    }
    public String toString(){
        return "教師名稱" + name + "  薪金" + salary;
    }
}


public class Test {

    /**
     * @param args
     */
    public static void main(String[] args) {
        Teacher teacher = new Teacher();
        teacher.setSalary(10000);
        teacher.setID(111111);
        System.out.println(teacher.getID());        //輸出111111
        serialize(teacher);
        Teacher teacher1 = deserialize();
        System.out.println(teacher1.toString());    //輸出教師名稱zhangsan  薪金10000
        System.out.println(teacher1.getID());       //輸出182566
    }
    /**
     * 外部序列化teacher對象
     * @param teacher
     */
    private static void serialize(Teacher teacher){
        try {
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("SourceFile/Teacher"));
            oos.writeObject(teacher);
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    private static Teacher deserialize(){
        try {
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream("SourceFile/Teacher"));
            return (Teacher)ois.readObject();
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return null;
    }
}

Java反序列化

與序列化相對的是反序列化,它將流轉換為對象,在序列化與反序列化的過程中,serialVersionUID起着重要的作用,每一個類都有一個特定的serialVersionUID,在反序列化的過程中通過serialVersionUID判定類的兼容性,自定義serialVersionUID主要由如下3個優點。

  1. 提高程序運行效率。如果在類中未顯示聲明serialVersionUID,那么在序列化時會通過計算得到一個serialVersionUID。通過顯示聲明serialVersionUID的方式省去了計算的過程,提高了程序效率。
  2. 提高程序不同平台上的兼容性。由於各個平台計算serialVersionUID的方式可能不同,通過顯示的方式可以完全避免該問題。
  3. 增強程序各個版本的可兼容性。在默認情況下每個類都有唯一的一個serialVersionUID,因此當后期對類進行修改時,類的serialVersionUID值將會發生變化,這將會導致類在修改前對象的文件在修改后無法進行反序列化操作。同樣通過顯示聲明serialVersionUID也會解決該問題。

最后想說明,反序列化也是Java創建對象的一種方式,其他的還有new 類名()、通過clone()創建對象、通過反射機制創建對象


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM