Java基礎-數據序列化之序列化框架的使用

爲什麼要序列化?

現在開發過程中經常遇到多個進程多個服務間需要交互,或者不同語言的服務之間需要交互,這個時候,我們一般選擇使用固定的協議,將數據傳輸過去,但是在很多語言,比如java等jvm語言中,傳輸的數據是特有的類對象,而類對象僅僅在當前jvm是有效的,傳遞給別的jvm或者傳遞給別的語言的時候,是無法直接識別類對象的,那麼,我們需要多個服務之間交互或者不同語言交互,該怎麼辦?這個時候我們就需要通過固定的協議,傳輸固定的數據格式,而這個數據傳輸的協議稱之爲序列化,而定義了傳輸數據行爲的框架組件也稱之爲序列化組件(框架)。
序列化框架中會包含兩種操作,一種將對象或數據結構轉換爲特定格式(傳輸數據協議),我們稱之爲序列化;另一種是將特定格式轉換爲對象或數據結構的過程,我們稱之爲反序列化。

序列化的目的

  • 使數據可以在網絡中傳輸,或者可存儲在內存或文件中。
  • 使數據可以脫離應用程序而獨立存在。

序列化使用場景

不同服務間的交互,服務可以同語言也可以不同語言。

Java語言內置序列化

序列化的實現可以分爲文本序列化、語言內置序列化和跨語言序列化,本文主要講述Java語言內置序列化。
每種語言基本都會有實現的序列化框架,此序列化的方式我們稱之爲語言內置序列化,其優點是語言提供了序列化的框架(序列化協議,序列化和反序列化操作),方便使用,缺點是交互的應用程序必須是同一種語言。
下面將講述Java需要內置序列化框架的使用,Java序列化協議是字節序列(由jvm實現)。

序列化框架使用的核心

  • 序列化接口(Serializable和Externalizable)

    需要序列化的類對象需要繼承此接口,只有當前接口修飾定義的類對象纔可以按照指定的方式傳輸對象。

  • 序列化id-serialVersionUID

    用來作爲傳輸/讀取雙端進程(javaBean)的版本是否一致的,防止我們因爲版本不一致導致的序列化失敗。
    編譯器提供兩種序列化id的方式,一種是生成默認的versionID,這個值爲1L,還有一種方式是根據類名、接口名、成員方法及屬性等來生成一個 64 位的哈希字段,只要我們類名、方法名、變量有修改,或者有空格、註釋、換行等操作,計算出來的哈希字段都會不同,當然這裏需要注意,每次我們有以上的操作的時候儘量都要重新生成一次serialVerionUID(編譯器並不會給你自動修改)。
    默認固定值1L和64位哈希數值不需要自己計算,繼承了序列化接口的類,如果沒有序列化id字段,在類名下會顯示黃色的波浪線,鼠標放置在類名上,選擇需要的方式即可自動添加序列化id字段(如有變動,刪除後再根據提示添加即可)。

  • 對象序列化和數據寫入操作(輸出流java.io.ObjectOutputStream)

    用於執行序列化操作,並將序列化後的數據寫入文件或進行傳輸。

  • 數據讀取和對象反序列化操作(輸入流java.io.ObjectInputStream)

    用於執行反序列化操作,將文件或從網絡接收到的數據反序列化爲對象。

案例1-基礎序列化和反序列化Serializable

下面以序列化對象存儲入文件後,讀取文件再使用對象爲例,講解序列化框架的使用,此使用同序列化數據後進行傳輸,因爲無論是數據傳輸還是存取文件,都需要用到輸入和輸出流,只是獲取輸入輸出流的參數不一致。

Person類實現
public class Person implements Serializable {
	//64位哈希序列化ID
	private static final long serialVersionUID = 8247451571741032209L;
	private String name;
	private int age;
	private transient boolean isMan; // true=男,false=女

	public Person(String name, int age, boolean isMan) {
		this.name = name;
		this.age = age;
		this.isMan = isMan;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

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

	public boolean isMan() {
		return isMan;
	}

	public void setMan(boolean isMan) {
		this.isMan = isMan;
	}
	
	/*此方法只是用於打印,與序列化無關*/
    public String toString() {
        return "Person{" + "name=" + name  + ",age = " + age+",isMan = " + isMan + "}";
    }

}
序列化操作
public static void writeObject() {
		try {
			// 0. 創建一個ObjectOutputStream輸出流
			ObjectOutputStream oos = new ObjectOutputStream(
					new FileOutputStream("E:/object.txt"));
			// 1. 將對象序列化到文件s
			Person person = new Person("lilu", 18, true);
			oos.writeObject(person);
			System.out.println("Person對象已寫入object.txt文件");
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
反序列化操作
public static void readObject() {
		try {
			// 0. 創建ObjectInputStream
			ObjectInputStream ois = new ObjectInputStream(new FileInputStream(
					"E:/object.txt"));
			Person person1 = (Person) ois.readObject();
			System.out.println("讀取到的Person對象數據如下:");
			System.out.println(person1);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
main函數調用
public static void main(String[] args) {
		writeObject();
		readObject();
	}
運行結果

在這裏插入圖片描述

Java序列化使用的總結

  • Java 序列化只是針對對象的屬性(對象的類名,變量的類型、變量的數據)的傳遞,至於方法和序列化過程無關。
  • 類中的某個成員變量不需要序列化,使用transient聲明該字段。
  • 類靜態變量(static修飾的變量)不會被序列化。
  • 成員是引用的序列化,這個引用類型對應的類也必須是可序列化的。
  • 序列化具有傳遞性-當一個父類實現了序列化,那麼子類會自動實現序列化,不需要顯示實現序列化接口,反過來,子類實現序列化,而父類沒有實現序列化則序列化會失敗。 同一對象序列化多次,不會多次序列化同一對象,而是不管進行多少次序列化操作,只會得到同一個對象。

擴展

  • 自定義序列化,重寫writeObject 和 readObject
  • 繼承接口Externalizable
  • 序列化框架的源碼詳解
  • 跨語言序列化的實現

參考鏈接

java序列化,看這篇就夠了!
詳解JAVA序列化
菜鳥教程-Java序列化

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章