Serializable詳解

 

 

對象序列化是把一個對象轉變爲二進制數據流 的一種方法,而一個對象想要被序列化就須要實現Serializable接口。java

查看Serializable接口的源碼能夠看到,並無定義任何的方法,這是一個標識的接口:app

public interface Serializable {
}

下面咱們定義一個類ide

public class Person implements Serializable{
	/**
	 * 
	 */
	private static final long serialVersionUID = 1073458546087797538L;
	private String name;
	private int age;
	public Person(String name,int age){
		this.name=name;
		this.age=age;
	}
	public String toString(){
		return "姓名:"+this.name+":年齡"+this.age;
	}
}

這個類就實現了序列化的接口,能夠看到在這個類裏面咱們定義了一個ui

serialVersionUID = 1073458546087797538L

通常來講,由於使用者jdk的不一樣,序列化和反序列化的版本不一致時就會出現異常,所以就加了這麼一個常量來對版本一致性進行驗證。若是在沒有定義serialVersionUID時,java的序列化機制會默認的定義一個serialVersionUID值,下面是在serialVersion類中對serialVersionUID的詳細介紹:this

* This readResolve method follows the same invocation rules and
 * accessibility rules as writeReplace.<p>
 *
 * The serialization runtime associates with each serializable class a version
 * number, called a serialVersionUID, which is used during deserialization to
 * verify that the sender and receiver of a serialized object have loaded
 * classes for that object that are compatible with respect to serialization.
 * If the receiver has loaded a class for the object that has a different
 * serialVersionUID than that of the corresponding sender's class, then
 * deserialization will result in an {@link InvalidClassException}.  A
 * serializable class can declare its own serialVersionUID explicitly by
 * declaring a field named <code>"serialVersionUID"</code> that must be static,
 * final, and of type <code>long</code>:
 *
 * <PRE>
 * ANY-ACCESS-MODIFIER static final long serialVersionUID = 42L;
 * </PRE>
 *
 * If a serializable class does not explicitly declare a serialVersionUID, then
 * the serialization runtime will calculate a default serialVersionUID value
 * for that class based on various aspects of the class, as described in the
 * Java(TM) Object Serialization Specification.  However, it is <em>strongly
 * recommended</em> that all serializable classes explicitly declare
 * serialVersionUID values, since the default serialVersionUID computation is
 * highly sensitive to class details that may vary depending on compiler
 * implementations, and can thus result in unexpected
 * <code>InvalidClassException</code>s during deserialization.  Therefore, to
 * guarantee a consistent serialVersionUID value across different java compiler
 * implementations, a serializable class must declare an explicit
 * serialVersionUID value.  It is also strongly advised that explicit
 * serialVersionUID declarations use the <code>private</code> modifier where
 * possible, since such declarations apply only to the immediately declaring
 * class--serialVersionUID fields are not useful as inherited members. Array
 * classes cannot declare an explicit serialVersionUID, so they always have
 * the default computed value, but the requirement for matching
 * serialVersionUID values is waived for array classes.
 *

大體上就是介紹須要一致,不一致會出現異常,並且必須是static,final,long。而這個值一致與否會對下面形成什麼影響在下面咱們繼續寫code

而要完成對象的輸入和輸出,還須要使用對象輸出流ObjectOutputStream和對象輸入流ObjectInputStream,demo1進行輸入對象

public class Demo1 {

	/**
	 * @param args
	 */
	public static void main(String[] args) throws IOException{
		// TODO 自動生成的方法存根
		File file=new File("d:"+File.separator+"test.txt");
		ObjectOutputStream oos=null;
		OutputStream out=new FileOutputStream(file);
		oos=new ObjectOutputStream(out);
		oos.writeObject(new Person("張三", 30));
		oos.close();
	}

}

輸入數據之後,打開文件能夠看到,記錄的是一串二進制的亂碼接口

demo2輸出生命週期

public class Demo2 {

	/**
	 * @param args
	 */
	public static void main(String[] args) throws Exception{
		// TODO 自動生成的方法存根
		File file=new File("d:"+File.separator+"test.txt");
		InputStream input=new FileInputStream(file);
		ObjectInputStream objectInputStream=new ObjectInputStream(input);
		Object object=objectInputStream.readObject();
		objectInputStream.close();
		System.out.println(object);//姓名:張三:年齡30
	}

}

在兩邊的碼一致的狀況下,不會出現任何問題。咱們把上面的uid最後一位去掉,運行程序就會出現下面的異常:內存

Exception in thread "main" java.io.InvalidClassException: test.Person; local class incompatible: stream classdesc serialVersionUID = 1073458546087797538, local class serialVersionUID = 107345854608779753

Serializable中全部的對象都必須被序列化,若是想進行部分序列化的話該怎麼辦?Externalizable接口是能夠實現部分對象的序列化,這個接口定義了兩個方法,writeExternal(ObjectOutput out)用來保存信息,readExternal(ObjectInput in)用來讀取,反序列化對象。

public interface Externalizable extends java.io.Serializable {
    void writeExternal(ObjectOutput out) throws IOException;
    void readExternal(ObjectInput in) throws IOException, ClassNotFoundException;
}

在Externalizable接口接口中是必需要定義無參構造的,在其進行反序列化的時候會調用,不然就會出現異常。

import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;

public class Person implements Externalizable {

	private static final long serialVersionUID = -842029427676826563L;

	public static String name;
	private int age;
	private transient int workDay = 5;
	private String fClub;

	public Person() {
        System.out.println("無參構造");
    }
	
	public Person(int age, String fClub) {
        this.age = age;
        this.fClub = fClub;
    }
	
	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public int getWorkDay() {
		return workDay;
	}

	public void setWorkDay(int workDay) {
		this.workDay = workDay;
	}

	public String getfClub() {
		return fClub;
	}

	public void setfClub(String fClub) {
		this.fClub = fClub;
	}

	private void writeObject(ObjectOutputStream out) throws IOException {
		out.defaultWriteObject();//執行默認的序列化機制
		out.writeInt(workDay);
		System.out.println("正在進行序列持久化");
	}

	private void readObject(ObjectInputStream in) throws IOException,
			ClassNotFoundException {
		in.defaultReadObject();
		workDay = in.readInt();
		System.out.println("讀取持久化對象");
	}

	@Override
	public void readExternal(ObjectInput arg0) throws IOException,
			ClassNotFoundException {
		// TODO Auto-generated method stub
	}

	@Override
	public void writeExternal(ObjectOutput arg0) throws IOException {
		// TODO Auto-generated method stub	
	} 
}
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class Hello {

	public static void main(String[] args) {
		Person person = new Person(26, "Juventus");
		person.setWorkDay(7);
		try {
			FileOutputStream fs = new FileOutputStream("foo.ser");
			ObjectOutputStream os = new ObjectOutputStream(fs);
			os.writeObject(person);
			os.close();

			Person.name = "Alex";

			FileInputStream in = new FileInputStream("foo.ser");
			ObjectInputStream s = new ObjectInputStream(in);
			Person p = (Person) s.readObject();
			System.out.println("name==" + Person.name + " age==" + p.getAge()
					+ " workDay==" + p.getWorkDay() + " fClub==" + p.getfClub());
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

 

 輸出結果

無參構造

name==Alex age==0 workDay==5 fClub==null  

能夠看到,在Person p = (Person) s.readObject();這一步調用了無參構造person方法。而由於在foo.ser文件中只有類的類型聲明,沒有任何實例變量,因此Person對象中任何一個字段都沒有被序列化,因此打印結果裏面,age爲0,fClub爲null,而workDay爲初始值5。writeExternal()與readExternal()方法未做任何處理,那麼該序列化行爲將不會保存/讀取任何一個字段。

修改person類

package test;

import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;

public class Person implements Externalizable {

	private static final long serialVersionUID = -842029427676826563L;

	public static String name;
	private int age;
	private transient int workDay = 5;
	private String fClub;

	public Person() {
        System.out.println("無參構造");
    }
	
	public Person(int age, String fClub) {
        this.age = age;
        this.fClub = fClub;
    }
	
	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public int getWorkDay() {
		return workDay;
	}

	public void setWorkDay(int workDay) {
		this.workDay = workDay;
	}

	public String getfClub() {
		return fClub;
	}

	public void setfClub(String fClub) {
		this.fClub = fClub;
	}

	/*private void writeObject(ObjectOutputStream out) throws IOException {
		out.defaultWriteObject();//執行默認的序列化機制
		out.writeInt(workDay);
		System.out.println("正在進行序列持久化");
	}

	private void readObject(ObjectInputStream in) throws IOException,
			ClassNotFoundException {
		in.defaultReadObject();
		workDay = in.readInt();
		System.out.println("讀取持久化對象");
	}*/

	 @Override  
	    public void writeExternal(ObjectOutput out) throws IOException {  
	        out.writeObject(fClub);  
	        out.writeInt(age);  
	        System.out.println("自定義序列化過程");  
	    }   
	      
	    @Override  
	    public void readExternal(ObjectInput in) throws IOException,  
	            ClassNotFoundException {  
	        fClub = (String) in.readObject();  
	        age = in.readInt();  
	        System.out.println("自定義反序列化");  
	    }   
}

結果:

自定義序列化過程
無參構造
自定義反序列化
name==Alex age==26 workDay==5 fClub==Juventus

當讀取對象時,會調用被序列化類的無參構造器去建立一個新的對象,而後再將被保存對象的字段的值分別填充到新對象中。這就是爲何輸出結果中會顯示調動了無參構造器。因爲這個緣由,實現Externalizable接口的類必需要提供一個無參的構造器,且它的訪問權限爲public。

實現Externalizable接口是能夠進行對象的部分序列化,可是其操做起來比serializable複雜麻煩,而使用serializable又會遇到例如在某些狀況下咱們會遇到密碼等咱們不但願被序列化的對象,這些信息對應的變量就能夠加上transient關鍵字。換句話說,這個字段的生命週期僅存於調用者的內存中而不會寫到磁盤裏持久化。

以爲不錯是能夠點個贊。

相關文章
相關標籤/搜索