Java的序列化與反序列化
前言
Java的序列化與反序列化是Java中比較重要的一個知識,本文將總結一下,怎麼使用序列化功能以及經常遇到的一些問題的解答.
什麼是Java的序列化
JDK提供給我們的,可以將某一個物件轉化為二進位制位元組流儲存,並從位元組流恢復物件的一種技術.
我們可以再網路傳輸物件,或者持久化物件時使用這項技術.
怎麼進行序列化與反序列化
Java中通過繼承Serializable介面來獲得序列化與反序列化的能力,使用ObjectInputStream和ObjectOutputStream來進行具體的物件序列化讀寫.
示例如下:
package daily;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ArrayList;
/**
* created by huyanshi on 2019/1/29
*/
public class SerializTest implements Serializable {
private static int staticValue = 10;
private int value;
SerializTest(int value) {
this.value = value;
}
public static void main(String[] args) {
try {
//初始化
SerializTest test = new SerializTest(100);
//序列化
ObjectOutputStream oos = new ObjectOutputStream(
new FileOutputStream("/Users/pfliu/Desktop/serialized.ser"));
System.out.println(test.value);
System.out.println(SerializTest.staticValue);
oos.writeObject(test);
SerializTest.staticValue = 250;
//反序列話
ObjectInputStream ois = new ObjectInputStream(
new FileInputStream("/Users/pfliu/Desktop/serialized.ser"));
SerializTest test1 = (SerializTest) ois.readObject();
System.out.println(test1.value);
System.out.println(SerializTest.staticValue);
} catch (Exception e) {
System.out.println("error");
}
}
}
在上面的程式碼中,我們new了一個物件,並將其進行了序列化與反序列化,並在序列化之前和反序列化之後列印了物件的值,結果為值相同.同時,在桌面上生成了Serialized.set
檔案.
為什麼必須要實現Serializable介面?
點開該介面的原始碼,我們可以發現,這是一個空的介面,即沒有任何的定義,那麼它是怎麼使用的呢?
在序列化的過程中,我們會呼叫ObjectOutputStream
的writeObject
方法,該方法,該方法呼叫writeObject0
方法,該方法中有如下程式碼:
if (obj instanceof String) {
writeString((String) obj, unshared);
} else if (cl.isArray()) {
writeArray(obj, desc, unshared);
} else if (obj instanceof Enum) {
writeEnum((Enum<?>) obj, desc, unshared);
} else if (obj instanceof Serializable) {
writeOrdinaryObject(obj, desc, unshared);
} else {
if (extendedDebugInfo) {
throw new NotSerializableException(
cl.getName() + "
" + debugInfoStack.toString());
} else {
throw new NotSerializableException(cl.getName());
}
}
可以看到,對傳入的物件進行了幾次判斷,分別判斷傳入物件是否為:String
,Array
,Enum
,Serializable
.
什麼意思呢?就是JDK規定了,只有字串
,陣列
,列舉
,Serializable
四種物件才允許序列化,其他的都會丟擲NotSerializableException
異常.
而這四種中,前面三種都是內定
的,只有最後一種是留給程式設計師的序列化通道,因此我們想要序列化某一個類,必須實現Serializable
介面.
序列化ID是幹什麼用的?
在看一些開源框架的程式碼時,發現他們的類都會有private static final long serialVersionUID = 8683452581122892189L;
這個屬性,這是用來幹什麼的呢?
序列化和反序列化的匹配是怎麼匹配的?總不能隨便來的吧,A類序列化後的二進位制檔案,B類能從哪裡讀出一個物件來嘛?
不能,類的路徑以及功能程式碼必須完全相同,而序列化ID也是用來補充這一判斷的.
試想一下,你在服務裡new了一個物件,並將其序列化使用網路傳輸,那麼收到這個二進位制流的人都能序列化嗎?不是的,他必須在自己的服務中有同樣的類路徑,同樣的類定義,同時,他的類中定義的序列化ID必須與你的一致才可以.算是一定程度上的安全性保證吧.
當然,日常開發中我們使用預設生成的1L
即可.
靜態變數的序列化
我在上面的程式碼中,定義了一個靜態變數,他也能被序列化嗎?
在序列化之後,對靜態變數重新賦值,那麼兩次列印的值相等嗎?
列印結果是:
10
250
為什麼呢?這個問題其實比較簡單,靜態變數是屬於類的,而我們是序列化了物件,因此不包含類的靜態變數是正常的.
transient 關鍵字
transient 關鍵字用於在序列化時,忽略某一個欄位,在反序列化後該欄位為初始值,比如int=0,物件引用為null.
ArrayList 的序列化
看了這麼多理論知識,我們來看一下常用類ArrayList
是怎麼序列化的吧.
ArrayList
實現了Serializable
自然不必多說,其中用來儲存資料的屬性定義為:
/**
* The array buffer into which the elements of the ArrayList are stored.
* The capacity of the ArrayList is the length of this array buffer. Any
* empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
* will be expanded to DEFAULT_CAPACITY when the first element is added.
*/
transient Object[] elementData; // non-private to simplify nested class access
為什麼會定義為transient
呢?我序列化一個ArrayList,你不給我儲存內部的值?我要你個空殼子幹啥!我摔!
穩住,我們可以實際測試一下,會發現在序列化及反序列化的過程中,是保留了list中的值的.
為什麼要定義為transient
呢?怎麼做到仍然保留資料的呢?
第一個問題
ArrayList內部是使用陣列實現的,雖然他是動態陣列,但是也是陣列.
也就是說,當你定義了長度為100的Arraylist,只放入了一個物件,剩下的99個就為空了.
序列化的時候有必要將這99個空也記錄下來嗎?沒有.因此定義為了transient
.
第二個問題
在序列化的過程中,虛擬機器會試圖呼叫被序列化類的writeObject和readObject
方法,呼叫不到才會去執行預設的這兩個方法,也就是對應的輸入輸出流中的方法.
在ArrayList中有如下程式碼:
private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException{
// Write out element count, and any hidden stuff
int expectedModCount = modCount;
s.defaultWriteObject();
// Write out size as capacity for behavioural compatibility with clone()
s.writeInt(size);
// Write out all elements in the proper order.
for (int i=0; i<size; i++) {
s.writeObject(elementData[i]);
}
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
/**
* Reconstitute the <tt>ArrayList</tt> instance from a stream (that is,
* deserialize it).
*/
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
elementData = EMPTY_ELEMENTDATA;
// Read in size, and any hidden stuff
s.defaultReadObject();
// Read in capacity
s.readInt(); // ignored
if (size > 0) {
// be like clone(), allocate array based upon size not capacity
int capacity = calculateCapacity(elementData, size);
SharedSecrets.getJavaOISAccess().checkArray(s, Object[].class, capacity);
ensureCapacityInternal(size);
Object[] a = elementData;
// Read in all elements in the proper order.
for (int i=0; i<size; i++) {
a[i] = s.readObject();
}
}
}
可以在程式碼中看到,ArrayList自定義的序列化方法,沒有序列化99個空值,只序列化了有意義的值.
總結
1.java的序列化需要實現Serializable
介面,之後使用ObjectOutputStream
及ObjectInputStream
進行讀寫.
2.必須實現Serializable
是因為JDK中進行了檢查,不屬於那四個類就會拋異常且不允許序列化.
3.序列化ID可以起到驗證是不是同一個類的作用,當然是在兩個類的程式碼完全一樣的基礎上.
4.transient關鍵字可以忽略一些欄位,使其不參與序列化.
5.靜態變數是不會序列化的,因為序列化的是物件
,而靜態變數屬於類
.
6.可以參考ArrayList的實現方法實現自己的自定義序列化,在這個自定義的過程中,可以做許多事情,比如對某些欄位加密(常用語密碼欄位).
參考連結
https://www.ibm.com/developerworks/cn/java/j-lo-serial/index.html
https://www.hollischuang.com/archives/1140
完.
ChangeLog
2019-01-28 完成
以上皆為個人所思所得,如有錯誤歡迎評論區指正。
歡迎轉載,煩請署名並保留原文連結。
聯絡郵箱:huyanshi2580@gmail.com
更多學習筆記見個人部落格——>呼延十
相關文章
- Java中的序列化與反序列化Java
- Java--序列化與反序列化Java
- java 序列化與反序列化例項Java
- 深入分析Java的序列化與反序列化Java
- Java安全基礎之Java序列化與反序列化Java
- Java序列化(Serializable)與反序列化詳解Java
- Java物件的序列化與反序列化-Json篇Java物件JSON
- Java的序列化和反序列化Java
- Java序列化、反序列化、反序列化漏洞Java
- Java物件流與序列化Java物件
- Java序列化與反序列化(原生方式與Jackson方式)Java
- 序列化與反序列化
- 【Java基礎】序列化與反序列化深入分析Java
- java的序列化Java
- 序列化與反序列化(GO)Go
- Java IO: 序列化與ObjectInputStream、ObjectOutputStreamJavaObject
- java序列化Java
- php中序列化與反序列化PHP
- Java物件的序列化和反序列化實踐Java物件
- 從java的序列化和反序列化說起Java
- Java序列化和hessian序列化的區別Java
- java的序列化SerializableJava
- java中的序列化Java
- [Java基礎]序列化和反序列化Java
- C#中物件的序列化與反序列化C#物件
- Serializable詳解(1):程式碼驗證Java序列化與反序列化Java
- 實體類與XML序列化與反序列化XML
- 什麼是Java序列化,如何實現java序列化Java
- c#序列化與反序列化概述C#
- .net序列化與反序列化總結
- Java——transient and 序列化Java
- java物件序列化Java物件
- Serializable java序列化Java
- java反序列化Java
- java 物件序列化要序列化那些內容Java物件
- Java序列化的狀態Java
- Flutter中JSON序列化與反序列化FlutterJSON
- Django REST framework 序列化與反序列化(4)DjangoRESTFramework