1. 程式人生 > >Java的序列化機制

Java的序列化機制

1、java本身的序列化存在的問題

1、序列化資料結果比較大、傳輸效率比較低
2、不能跨語言對接
對於該問題出現的xml、json等方式成為了熱門技術,但是這種序列化技術還是存在佔用空間大、效能低等問題,也就出現了二進位制序列化框架MessagePack等。

2、序列化概念

把物件轉換為位元組序列的過程稱之為物件的序列化,反之則稱為反序列化

3、如何實現一個序列化操作

1、對一個類實現Serializable介面,同時生成一個serialVersionUID。

package com.wuyonghu.test4;

import java.io.Serializable;

public class Person implements Serializable {
	private static final long serialVersionUID = 2818521397469642897L;
	private String name;
	private String age;

	public String getName() {
		return name;
	}

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

	public String getAge() {
		return age;
	}

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

}

2、建立主方法來進行對物件的序列化:

package com.wuyonghu.test4;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;

public class MainTest {

	public static void main(String[] args) {
		// 序列化
		SerializableTest();
	}

	private static void SerializableTest() {
		try {
			// 使用ObjectOutputStream指定序列化的結果
			ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File("person")));
			// 建立需要序列化的物件
			Person person = new Person();
			person.setAge("17");
			person.setName("lufei");
			// 進行序列化
			oos.writeObject(person);
			System.out.println("序列化完成!");
			oos.close();
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}
}

3、執行SerializableTest方法後,會發現在工程的根目錄下生成了一個person檔案:
在這裡插入圖片描述
該檔案通過二進位制方式開啟的結果如下:
在這裡插入圖片描述

4、如何進行反序列化操作

以第三步得到的person檔案來進行反序列化
1、反序列化的方法如下:

	public static void SerializableTest2() {
		try {
			ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("person")));
			Person person = (Person) ois.readObject();
			System.out.println("反序列化完成,姓名為" + person.getName() + ",年齡為:" + person.getAge());
			ois.close();
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		}

	}

2、執行反序列化後的結果如下:
在這裡插入圖片描述
3、從結果可以知道,我們能從通過反序列化得到序列化前的物件資訊。

5、serialVersionUID的作用

在對物件進行序列化的時候,該對應對應的類是實現了序列化的介面,同時新增了serialVersionUID這個值的,那麼這個值的作用是什麼呢?
在這裡插入圖片描述
為了測試,首先將該值保留,進行序列化,得到結果person檔案。然後將該值註釋後進行反序列化。

此處注意一下,如果要驗證該功能,測試時最好自己隨意指定一個serialVersionUID值,否則jvm會自動生成一個UID,而反序列的時候自動生成的該UID會相同,從而會出現沒有報錯的情況。
從該現象,得出的結論如下:
檔案流中的class和classpath中的class,也就是修改過後的class,不相容了,處於安全機制考慮,程式丟擲了錯誤,並且拒絕載入。從錯誤結果來看,如果沒有為指定的class配置serialVersionUID,那麼java編譯器會自動給這個class進行一個摘要演算法,類似於指紋演算法,只要這個檔案有任何改動,得到的UID就會截然不同的,可以保證在這麼多類中,這個編號是唯一的。所以,由於沒有顯指定 serialVersionUID,編譯器又為我們生成了一個UID,當然和前面儲存在檔案中的那個不會一樣了,於是就出現了2個序列化版本號不一致的錯誤。因此,只要我們自己指定了serialVersionUID,就可以在序列化後,去新增一個欄位,或者方法,而不會影響到後期的還原,還原後的物件照樣可以使用,而且還多了方法或者屬性可以用。

6、關於序列化的其他概念

1、序列化並不儲存靜態變數的狀態
2、transient關鍵字表示指定屬性不參與序列化
3、如果父類沒有實現序列化,而子類實現列序列化。那麼父類中的成員沒辦法做序列化操作
4、對同一個物件進行多次寫入,打印出的第一次儲存結果和第二次儲存結果,只多了5個位元組的引用關係。
並不會導致檔案累加