天天看點

IO(五)對象流和序列化反序列化

對象流:

有的時候,我們可能需要将記憶體中的對象持久化到硬碟下,或者将硬碟中的對象資訊讀到記憶體中,這個時候我們可能會用到流,java IO中提供了友善我們使用的流(對象流)友善我們進行對象的序列化和反序列化操作

序列化:

講對象轉換成一個位元組序列的過程,相當于寫入操作

反序列化:

将一個位元組序列轉換為對象的過程,相當于讀操作

java IO 也提供了一個接口來标志是否可以序列化----Serializable接口

該接口源碼裡邊什麼内容都沒有,這個接口隻是一個标記接口,用來告訴我們的JVM虛拟機,實作了這個接口的對象,可以序列化,類似我們的Cloneable接口,也是标記接口

public interface Serializable {
}

public interface Cloneable {
}
           

如果需要序列化操作的對象沒有實作該接口,操作的時候,會出現序列化失敗的錯誤

serialVersionUID (序列化的版本ID)相關問題

  • 1.serialVersionUID如何生成?
    • 1.一個是預設的1L,比如:private static final long serialVersionUID = 1L;
    • 2.一個是根據類名、接口名、成員方法及屬性等來生成一個64位的哈希字段,比如: private static final long serialVersionUID = xxxxL;
  • 2.Java的序列化機制流程?
    1. 在進行對象序列化時,系統會預設給每個對象生成一個serialVersionUID,系統預設生成的serialVersionUID是根據對象的類,超類,屬性和方法等資訊計算得到的,如果,對象資訊發生變化,例如增加或者減少一個屬性,增加或減少一個方法等,該serialVersionUID也會發生相應的變化,
    2. 每個序列化的對象都是采用一個序列化好來進行儲存的,當序列化一個對象時,程式會檢查該對象是否已經序列化過,如果沒有則使用位元組流中的資料建構序列化對象,并為該對象關聯一個serialVersionUID,可以是系統生成也可以指定,如果對象已經序列化過,則程式直接輸出該對象關聯的序列号在和本地serialVersionUID進行比較,一緻則反序列化輸出對象
    3. java序列化機制是通過JVM在運作時判斷傳進來的位元組流的serialVersionUID和本地相應實體類的serialVersionUID進行比較來驗證版本的一緻性。如果一緻,則可以執行反序列化,不一緻則會出現序列化版本不一緻的異常
      IO(五)對象流和序列化反序列化
  • 3.serialVersionUID作用?

    維持版本一緻性

transient關鍵字

在序列化的過程中,我們有時候并不想該對象所有的屬性都參與序列化,這時候就需要用到transient關鍵字,如果用transient聲明一個執行個體變量,當對象存儲時,它的值不需要維持。換句話來說就是,用transient關鍵字标記的成員變量不參與序列化過程。

當反序列化的時候,該屬性就會輸出他的預設值

public class Dog implements Serializable{

	/**
	 * 序列化版本ID
	 */
	private static final long serialVersionUID = 1L;
	private String name;
	private transient int id;
	private String age;
	private String address;
	//省略get/set
	public Dog(String name, int id,String age, String address) {
		super();
		this.name = name;
		this.id = id;
		this.age = age;
		this.address = address;
	}
           

IO 中的為我們提供了對象流用來操作對象的持久化

/**
	 * 寫入對象(序列化)
	 */
	public static void writeObject() {
		
		File file = new File("object.txt");
		if(!file.exists()) {
			try {
				file.createNewFile();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		try {
			OutputStream outputStream = new FileOutputStream(file);
			ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);
			
			Dog dog1 = new Dog("K", 12, "13","obejct");
			Dog dog2 = new Dog("o", 15, "21");
			Dog[] dogs = {dog1,dog2};
			objectOutputStream.writeObject(dog1);
			
			System.out.println("寫入完畢");
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
           
/**
	 * 讀對象(反序列化)
	 */
	public static void readObject() {
		File file = new File("object.txt");
		if(!file.exists()) {
			try {
				file.createNewFile();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		try {
			InputStream inputStream = new FileInputStream(file);
			ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
			//Dog[] dogs = (Dog[])objectInputStream.readObject();
			
			System.out.println("讀取完畢");
			
			//for(Dog dog : dogs) {
			//	System.out.println("結果:"+dog);
			//}
			Dog dog = (Dog)objectInputStream.readObject();
			System.out.println("結果:"+dog);
		} 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();
		}
	}
	
           
IO(五)對象流和序列化反序列化

id屬性沒有參與到序列化的過程,反序列化得到的就是他的預設值,引用類型為null

使用對象流也是非常簡單,因為JAVA IO中使用裝飾設計模式為我們解決了很多困難,我們隻需要重新包裝位元組流就可以擴充對象流操作功能。