名詞解釋
序列化:將Java對象轉(zhuǎn)化成字節(jié)的過程
反序列化:將字節(jié)轉(zhuǎn)化成Java對象的過程
字節(jié):1字節(jié)(byte)= 8bit,bit就是計算機(jī)認(rèn)識的二進(jìn)制
序列化的作用
Java對象是在Java虛擬機(jī)中使用的盗扒,一旦Java進(jìn)程結(jié)束跪楞,對象就會消失,要將只有虛擬機(jī)才認(rèn)識的對象侣灶,保存在磁盤中甸祭,必須將對象轉(zhuǎn)化成字節(jié)。
- 在RPC中的用處:序列化將對象轉(zhuǎn)換為字節(jié)流褥影,然后通過網(wǎng)絡(luò)傳輸進(jìn)行發(fā)送
- 保存對象的狀態(tài):當(dāng)Java進(jìn)程需要重啟時池户,可以將對象序列化后保存在文件中,對象的狀態(tài)不會因為進(jìn)程的關(guān)閉而丟失
如何進(jìn)行序列化
基本數(shù)據(jù)類型轉(zhuǎn)為字節(jié)的思路
對于有多個字節(jié)的數(shù)據(jù)凡怎,用移位運算符校焦,將每8位進(jìn)行移位,用一個字節(jié)保存
- Int類型:一個int有4個字節(jié)统倒,可以創(chuàng)建一個長度為4的字節(jié)數(shù)組進(jìn)行保存(short寨典,long類似)
- char類型:一個char有2個字節(jié),用相應(yīng)長度的字節(jié)數(shù)組保存后房匆,反序列化時再強(qiáng)制轉(zhuǎn)化為char
- String類型:String的值主要是一個char數(shù)組耸成,創(chuàng)建一個大小為char數(shù)組兩倍的字節(jié)數(shù)組進(jìn)行保存,反序列化時再轉(zhuǎn)化為String
- Double和Float類型:過程比較復(fù)雜(沒學(xué)會)浴鸿,建議直接調(diào)用工具類
一個字節(jié)和其他類型的轉(zhuǎn)換工具類
import java.nio.ByteBuffer;
public class ByteUtils {
public static byte[] short2bytes(short v) {
byte[] b = new byte[4];
b[1] = (byte) v;
b[0] = (byte) (v >>> 8);
return b;
}
public static byte[] String2bytes(String str){
char[] chars = str.toCharArray();
byte[] charByte = new byte[chars.length*2];
for (int i = 0; i < chars.length; i++) {
charByte[i*2] = (byte) (chars[i] >>> 8);
charByte[i*2+1] = (byte) (chars[i]);
}
return charByte;
}
public static byte[] int2bytes(int v) {
byte[] b = new byte[4];
b[3] = (byte) v;
b[2] = (byte) (v >>> 8);
b[1] = (byte) (v >>> 16);
b[0] = (byte) (v >>> 24);
return b;
}
public static byte[] long2bytes(long v) {
byte[] b = new byte[8];
b[7] = (byte) v;
b[6] = (byte) (v >>> 8);
b[5] = (byte) (v >>> 16);
b[4] = (byte) (v >>> 24);
b[3] = (byte) (v >>> 32);
b[2] = (byte) (v >>> 40);
b[1] = (byte) (v >>> 48);
b[0] = (byte) (v >>> 56);
return b;
}
public static byte[] double2bytes(double d){
long lValue = Double.doubleToLongBits(d);
byte[] bytes = long2bytes(lValue);
return bytes;
}
public static int bytes2Int_BE(byte[] bytes) {
if(bytes.length < 4){
return -1;
}
int iRst = (bytes[0] << 24) & 0xFF;
iRst |= (bytes[1] << 16) & 0xFF;
iRst |= (bytes[2] << 8) & 0xFF;
iRst |= bytes[3] & 0xFF;
return iRst;
}
public static long bytes2long(byte[] b) {
ByteBuffer buffer = ByteBuffer.allocate(8);
buffer.put(b, 0, b.length);
buffer.flip();// need flip
return buffer.getLong();
}
public static String bytes2String(byte[] bytes){
char[] chars = new char[bytes.length/2];
for (int i = 0; i < chars.length; i++) {
chars[i] = (char) ((bytes[i*2] << 8) | bytes[i*2+1]);
}
return new String(chars);
}
public static float byte2Float(byte[] bytes){
Integer iValue = bytes2Int_BE(bytes);
float dValue = Float.intBitsToFloat(iValue);
return dValue;
}
public static double byte2Double(byte[] bytes){
Long lValue = bytes2long(bytes);
double dValue = Double.longBitsToDouble(lValue);
return dValue;
}
}
View Code
如何序列化對象
其實序列化就是為了反序列化井氢,保存對象之后必然要讀取對象,所以 站在反序列化的角度去研究序列化
得到一串字節(jié)流之后岳链,要如何轉(zhuǎn)換成對象
要通過反射創(chuàng)建對象花竞,首先要知道對象的類名稱,然后調(diào)用類的默認(rèn)構(gòu)造函數(shù)
要識別類名稱掸哑,得知道類名稱是由哪些字節(jié)轉(zhuǎn)換的
要有一個值存放類名稱的字節(jié)長度约急,長度前最好放一個標(biāo)記
給對象的字段賦值,各種類型都不一樣苗分,需要識別
需要數(shù)據(jù)類型的字節(jié)長度烤宙,根據(jù)長度去識別字節(jié)數(shù)組中的一部分
將字節(jié)轉(zhuǎn)換成指定的數(shù)據(jù)類型
通過反射給對象的字段賦值
序列化的代碼實現(xiàn)如下
- 這里沒有將對象的類型寫入字節(jié)數(shù)組,因為反序列化的方法會傳入一個class參數(shù)俭嘁,可以直接構(gòu)造對象
1 public static byte[] serialize(Object object) throws IllegalAccessException, ClassNotFoundException {
2
3 // 對象序列化后的字節(jié)數(shù)組
4 byte[] objectByte = new byte[1024];
5 // 通過移動的下標(biāo)變量不斷往數(shù)組添加數(shù)據(jù)
6 int index = 0;
7 // 標(biāo)記后面的字節(jié)可以轉(zhuǎn)化成對象
8 objectByte[index] = SerializationNum.SERIALIZATION_OBJECT_NUM;
9
10 Class clazz = object.getClass();
11 // 遍歷所有字段,將字段的值轉(zhuǎn)化為字節(jié)
12 Field[] fields = clazz.getDeclaredFields();
13 // 一開始的標(biāo)記占了一個位置
14 int len = 1;
15 for (Field field : fields) {
16
17 // 將private屬性設(shè)置為可訪問
18 field.setAccessible(true);
19
20 // 每次移動下標(biāo),給后面的數(shù)據(jù)騰地方
21 index += len;
22 // 不同的類型服猪,不同的轉(zhuǎn)化方式
23 Class fieldClass = field.getType();
24 // 每種類型對應(yīng)一個標(biāo)記
25 byte magicNum = SerializationNum.getNum(fieldClass);
26
27 byte[] valueByte = new byte[0];
28 switch (magicNum){
29 case SerializationNum.INTEGER_NUM:
30 // 反射獲取值
31 Integer iValue = (Integer) field.get(object);
32 // int類型是4個字節(jié)
33 len = 4;
34 // 轉(zhuǎn)化成一個長度為4的字節(jié)數(shù)組
35 valueByte = ByteUtils.int2bytes(iValue);
36 break;
37
38 case SerializationNum.LONG_NUM:
39 long longValue = field.getLong(object);
40 len = 8;
41 valueByte = ByteUtils.long2bytes(longValue);
42 break;
43
44 case SerializationNum.STRING_NUM:
45 String sValue = (String) field.get(object);
46 valueByte = ByteUtils.String2bytes(sValue);
47 len = valueByte.length;
48 break;
49
50 default:
51 break;
52 }
53 // 將類型和長度都添加字節(jié)數(shù)組中
54 objectByte[index++] = magicNum;
55 objectByte[index++] = (byte) len;
56 // 轉(zhuǎn)化后的字節(jié)復(fù)制到對象字節(jié)數(shù)組
57 System.arraycopy(valueByte,0,objectByte,index,len);
58 }
59
60 index += len;
61 // 對象已經(jīng)整個被轉(zhuǎn)化完畢的標(biāo)記
62 objectByte[index] = SerializationNum.FINISH_NUM;
63
64 return objectByte;
65 }
反序列化過程實現(xiàn)
1 public static Object deserialize(byte[] bytes,Class clazz) throws InstantiationException, IllegalAccessException {
2
3 int index = 0;
4 // 識別首部供填,確保是能夠反序列化成對象的字節(jié)
5 if (bytes[index] != SerializationNum.SERIALIZATION_OBJECT_NUM) {
6 return null;
7 }
8
9 // 使用默認(rèn)構(gòu)造方法
10 Object obj = clazz.newInstance();
11 // 跳過最開始的魔數(shù)
12 byte len = 1;
13
14 // 一個個給對象的字段賦值
15 for (Field declaredField : clazz.getDeclaredFields()) {
16 declaredField.setAccessible(true);
17 // 每次移動下標(biāo)
18 index += len;
19
20 // 不同的類型,不同的轉(zhuǎn)化方式
21 Class fieldClass = declaredField.getType();
22
23 byte magicNum = SerializationNum.getNum(fieldClass);
24
25 // 防止數(shù)組越界
26 if (index >= bytes.length || bytes[index] != magicNum){
27 continue;
28 }
29
30 Object value = null;
31 // 先獲取需要的字節(jié)長度
32 len = bytes[++index];
33 // 創(chuàng)建一個新數(shù)組去作為方法參數(shù)罢猪,如果不采用這種傳參方式近她,也許能節(jié)約這個空間
34 byte[] srcBytes = new byte[len];
35 System.arraycopy(bytes,++index,srcBytes,0,len);
36
37 switch (magicNum){
38 case SerializationNum.INTEGER_NUM:
39
40 value = ByteUtils.bytes2Int_BE(srcBytes);
41 break;
42
43 case SerializationNum.LONG_NUM:
44
45 value = ByteUtils.bytes2long(srcBytes);
46 break;
47
48 case SerializationNum.STRING_NUM:
49
50 value = ByteUtils.bytes2String(srcBytes);
51 break;
52
53 case SerializationNum.DOUBLE_NUM:
54 value = ByteUtils.byte2Double(srcBytes);
55 break;
56
57 default:
58 break;
59 }
60 // 將值賦給對象
61 declaredField.set(obj,value);
62 }
63 return obj;
64 }
對各種基本數(shù)據(jù)類型進(jìn)行特殊標(biāo)記的工具類
public class SerializationNum {
public static final byte BOOLEAN_NUM = 0x01;
public static final byte SHORT_NUM = 0x02;
public static final byte INTEGER_NUM = 0x03;
public static final byte LONG_NUM = 0x04;
public static final byte CHAR_NUM = 0x05;
public static final byte FLOAT_NUM = 0x06;
public static final byte DOUBLE_NUM = 0x07;
public static final byte STRING_NUM = 0x08;
public static final byte OBJECT_NUM = 0x09;
public static final byte SERIALIZATION_OBJECT_NUM = 0x10;
public static final byte FINISH_NUM = 0x30;
public static byte getNum(Class clazz){
if (clazz == String.class) {
return STRING_NUM;
}
if (clazz == Integer.class) {
return INTEGER_NUM;
}
if (clazz == Double.class) {
return DOUBLE_NUM;
}
if (clazz == Boolean.class) {
return BOOLEAN_NUM;
}
if (clazz == Float.class) {
return FINISH_NUM;
}
if (clazz == Long.class) {
return LONG_NUM;
}
return 0x77;
}
}
View Code
測試代碼
1 public static void main(String[] args) throws IOException {
2 Entity entity = new Entity();
3
4 entity.setName("name");
5 entity.setNum(5);
6 entity.setId(5L);
7 entity.setMoney(100.55);
8
9 try {
10 System.out.println("可以將對象序列號后的字節(jié)重新反序列化成對象");
11 byte[] objByte = serialize(entity);
12 Entity entity1 = (Entity) deserialize(objByte,Entity.class);
13 System.out.println(entity1);
14
15
16 System.out.println("保存在文件的字節(jié),取出來后也可以反序列成對象");
17 FileOutputStream outputStream = new FileOutputStream("text.out");
18 FileInputStream inputStream = new FileInputStream("text.out");
19 byte[] fileBytes = new byte[1024];
20
21 outputStream.write(objByte);
22 inputStream.read(fileBytes);
23
24 Entity entity2 = (Entity) deserialize(fileBytes,Entity.class);
25 System.out.println(entity2);
26
27 } catch (IllegalAccessException | ClassNotFoundException | InstantiationException e) {
28 System.out.println("類不能被構(gòu)造");
29 e.printStackTrace();
30 }
31
32 }
測試結(jié)果: