Java序列化與反序列化


Java序列化與反序列化


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

    • 序列化

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

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

序列化有如下兩個特色:數據庫

    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個優勢。ide

  1. 提升程序運行效率。若是在類中未顯示聲明serialVersionUID,那麼在序列化時會經過計算獲得一個serialVersionUID。經過顯示聲明serialVersionUID的方式省去了計算的過程,提升了程序效率。
  2. 提升程序不一樣平臺上的兼容性。因爲各個平臺計算serialVersionUID的方式可能不一樣,經過顯示的方式能夠徹底避免該問題。
  3. 加強程序各個版本的可兼容性。在默認狀況下每一個類都有惟一的一個serialVersionUID,所以當後期對類進行修改時,類的serialVersionUID值將會發生變化,這將會致使類在修改前對象的文件在修改後沒法進行反序列化操做。一樣經過顯示聲明serialVersionUID也會解決該問題。

最後想說明,反序列化也是Java建立對象的一種方式,其餘的還有new 類名()、經過clone()建立對象、經過反射機制建立對象函數

相關文章
相關標籤/搜索