java中的序列化與反序列化
序列化與反序列化
一 、什麼是序列化與反序列化 把物件轉換為位元組序列的過程稱為物件的序列化。 把位元組序列恢復為物件的過程稱為物件的反序列化
- 一個物件只要實現了Serilizable介面,這個物件就可以被序列化,java的這種序列化模式為開發者提供了很多便利,我們可以不必關係具體序列化的過程,只要這個類實現了Serilizable介面,這個類的所有屬性和方法都會自動序列化。
為什麼需要實現Serilizable介面?
- 如果一個類想被序列化,需要實現Serializable介面。否則將丟擲NotSerializableException異常,這是因為,在序列化操作過程中會對型別進行檢查,要求被序列化的類必須屬於Enum、Array和Serializable型別其中的任何一種,這也是為什麼Serializable雖然是一個空介面,但是隻要實現了該介面就能序列化和反序列化。
二、為什麼需要序列化與反序列化?
- 當兩個程序進行遠端通訊時,可以相互發送各種型別的資料,包括文字、圖片、音訊、視訊等, 而這些資料都會以二進位制序列的形式在網路上傳送。當兩個Java程序進行通訊時,能否實現程序間的物件傳送呢?如何做到呢?此時就需要Java序列化與反序列化了!傳送方需要把這個Java物件轉換為位元組序列,然後在網路上傳送。接收方需要從位元組序列中恢復出Java物件。
序列化的好處
- 實現了資料的持久化,通過序列化可以把資料永久地儲存到硬碟上(通常存放在檔案裡)
- 用序列化實現遠端通訊,即在網路上傳送物件的位元組序列。
三、序列化特點
- 序列化時,只對物件的狀態進行儲存,而不管物件的方法。
- 當一個父類實現序列化時,子類自動實現序列化,不需要顯示實現Serializable介面
- 當一個物件的例項變數引用了其他物件時,序列化該物件時,也把引用物件序列化(可以理解成,在這裡引用物件只是當做類的屬性,無特殊說明自然會把屬性序列化)
- 物件中,被static或transient(物件的臨時資料)修飾的變數,不會被序列化。
四、如何序列化和反序列化 利用ObjectInputStream(物件輸入流)和ObjectOutputStream(物件輸出流)
- 序列化:ObjectInputStream(物件輸入流)
FileOutputStream fileOutputStream=new FileOutputStream(資料儲存目錄); ObjectOutputStream out=new ObjectOutputStream(fileOutputStream);//物件輸入流,得到的物件序列化,把得到的位元組序列寫到目標輸入流中 out.writeObject(序列化物件); //通過物件輸入流的writeObject方法寫物件
例子:Employee.java
//一個物件可序列化,需要實現Serilizable介面
public class Employee implements Serializable {
public String name;
public static String address;
public transient String password;
public int number;
public Employee(String name,String address,String password,int number)
{
this.name=name;
this.address=address;
this.password=password;
this.number=number;
}
}
序列化類 SerializeDemo.java
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
public class SerializeDemo {
public static void main(String[] args)
{
Employee employee=new Employee("xiao","shenqiu","12345",1);
try {
FileOutputStream fileOutputStream=new FileOutputStream("/object.txt");
ObjectOutputStream out=new ObjectOutputStream(fileOutputStream);
try {
out.writeObject(employee);
}
catch (Exception ee)
{
ee.printStackTrace();
}
out.close();
fileOutputStream.close();
System.out.println("序列化資料已儲存在檔案中");
}
catch (IOException i)
{
i.printStackTrace();
}
}
}
序列化類執行結果:
序列化資料已儲存在檔案中
Process finished with exit code 0
用UltraEdit 開啟obiect.txt檔案可以看到檔案內容。物件已經序列化成位元組序列儲存在檔案中了。其中***h代表行。
- 2.反序列化
利用物件輸入流ObjectInputStream
FileInputStream fileIn = new FileInputStream(資料讀取目錄);
ObjectInputStream in = new ObjectInputStream(fileIn);// 物件輸入流
e = (Employee) in.readObject();//通過物件輸入流的readObject()方法讀取物件
反序列化檔案 DeserializeDemo.java
import java.io.*;
public class DeserializeDemo {
public static void main(String [] args)
{
Employee e = null;
try
{
FileInputStream fileIn = new FileInputStream("/object.txt");
ObjectInputStream in = new ObjectInputStream(fileIn);
e = (Employee) in.readObject();
in.close();
fileIn.close();
}catch(Exception i)
{
i.printStackTrace();
return;
}
System.out.println("Deserialized Employee...");
System.out.println("Name: " + e.name);
System.out.println("Address: " + e.address);
System.out.println("Password: " + e.password);
System.out.println("Number: " + e.number);
}
}
反序列化執行結果:
Deserialized Employee...
Name: xiao
Address: null
Password: null
Number: 1
Process finished with exit code 0
這裡可以看到Employee 的屬性(static)Address和(transient)Password沒有被序列化,是null。 五、Serialversion 序列化版本號 (1)提高執行效率。如果在類中沒有顯式宣告Serialversion。那麼在序列化的時候回通過計算得到該值。顯式宣告可以省略計算。 (2)如果類沒有提供SerialversionUID,那麼編譯器會自動生成。SerialversionUID就是物件的hashcode。如果加入新的成員變數,那麼重新生成的SerialversionUID會變化。這樣反序列的時候crash,產生java.io.InvalidClassException異常,不能正常的反序列化。
例子: 我們在Employee.java中加一個屬性 i,再次執行反序列化檔案DeserializeDemo.java
Employee .java
import java.io.Serializable;
public class Employee implements Serializable {
public String name;
public static String address;
public transient String password;
public int number;
public int i=0;//新加屬性i
public Employee(String name,String address,String password,int number)
{
this.name=name;
this.address=address;
this.password=password;
this.number=number;
}
}
執行結果:
java.io.InvalidClassException: 序列化.Employee;local class incompatible:
stream classdesc serialVersionUID = -7599142119717411520,
local class serialVersionUID = 5676476655507316830
可以看到序列化版本號不一致,導致不能反序列化,所以最好指定serialVersionUID。 我們指定serialVersionUID,看一下執行結果。 我們在Employee .java中指定serialVersionUID,執行SerializeDemo.java序列化,然後在檔案Employee.java中新增屬性 i,看一下反序列化的結果.
- 指定serialVersionUID。即修改Employee .java
- 序列化。即執行SerializeDemo .java
- 修改物件屬性。即修改Employee.java
- 反序列化。DeserializeDemo.java
Employee .java
import java.io.Serializable;
public class Employee implements Serializable {
public String name;
public static String address;
public transient String password;
public int number;
public int i=0; //新加屬性i
private static final long serialVersionUID = -8976532647273106745L;//指定serialVersionUID
public Employee(String name,String address,String password,int number)
{
this.name=name;
this.address=address;
this.password=password;
this.number=number;
}
}
執行DeserializeDemo.java檔案
Deserialized Employee...
Name: xiao
Address: null
Password: null
Number: 1
可以看到此時反序列化正確。指定serialVersionUID ,那麼修改序列化物件屬性,反序列化時也不會出錯。