1. 程式人生 > >Netty學習7-序列化原理

Netty學習7-序列化原理

1 概述
序列化:把Java物件轉化為byte陣列的過程。便於網路傳輸,儲存等。反序列化:把byte流還原為Java物件的過程。現有很多成熟的序列化框架如JDK原生序列化框架、google的protobuf等。本文來探究一下序列化的原理

2 自定義序列化框架
現在需要轉化的是一個int物件,由4個位元組構成。那麼序列化過程就是把int物件這4個位元組,按照某種順序,分別寫到位元組陣列。本示例採取的是大端序列化方式,相關概念請參看本部落格的《大小端模式》和《Java中的位運算》兩篇文章。
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Arrays;

public class Test0 {

	public static void main(String[] args) throws IOException {
		// 高->低 [00000000 00000000 00000000 00000100]
		int id = 8;
		// 序列化
		byte[] array = serial(id);
		// 反序列化
		deSerial(array);
	}

	// 序列化
	public static byte[] serial(int id) throws IOException {
		ByteArrayOutputStream arrayOutputStream = new ByteArrayOutputStream();
		arrayOutputStream.write(int2bytes(id));
		
		byte[] byteArray = arrayOutputStream.toByteArray();
		System.out.println(Arrays.toString(byteArray));
		return byteArray;
	}

	// 反序列化
	public static void deSerial(byte[] byteArray) throws IOException {
		ByteArrayInputStream arrayInputStream = new ByteArrayInputStream(
				byteArray);
		byte[] idBytes = new byte[4];
		arrayInputStream.read(idBytes);
		System.out.println("id:" + bytes2int(idBytes));
	}

	// 大端位元組序列
	public static byte[] int2bytes(int i) {

		// 先寫高位,再寫低位
		// 高->低 [00000000 00000000 00000000 00000100]
		// 步驟1:把最高位移到最右邊,轉換為byte
		// 步驟2:把第二高位移到最右邊,轉為byte
		// 步驟3:把第三高位移到最右邊,轉為byte
		// 步驟4:把最後一位移到最右邊,轉為byte
		byte[] bytes = new byte[4];
		// 8的右移結果[00000000 00000000 00000000 00000000] 轉byte值為[00000000]
		bytes[0] = (byte) (i >> 3 * 8);
		// 8的右移結果[00000000 00000000 00000000 00000000] 轉byte值為[00000000]
		bytes[1] = (byte) (i >> 2 * 8);
		// 8的右移結果[00000000 00000000 00000000 00000000] 轉byte值為[00000000]
		bytes[2] = (byte) (i >> 1 * 8);
		// 8的右移結果[00000000 00000000 00000000 00000100] 轉byte值為[00000100]
		bytes[3] = (byte) (i >> 0 * 8);
		return bytes;
	}

	// 大端位元組反序列
	public static int bytes2int(byte[] bytes) {
		// [00000000]左移,轉為int結果為[00000000 00000000 00000000 00000000]
		int b0 = (int) (bytes[0] << 3 * 8);
		// [00000000]左移,轉為int結果為[00000000 00000000 00000000 00000000]
		int b1 = (int) (bytes[1] << 2 * 8);
		// [00000000]左移,轉為int結果為[00000000 00000000 00000000 00000000]
		int b2 = (int) (bytes[2] << 1 * 8);
		// [00000100]左移,轉為int結果為[00000000 00000000 00000000 00000100]
		int b3 = (int) (bytes[3] << 0 * 8);
		// 位或結果[00000000 00000000 00000000 00000100]
		return b0 | b1 | b2 | b3;
	}
}
註釋非常清楚,主要是利用了位運算進行序列化操作。若有兩個物件同時序列化,參看下例:
public class Test1 {
	public static void main(String[] args) throws IOException {

		// 測試資料
		int id = 8;
		int age = 10;

		// 序列化
		ByteArrayOutputStream arrayOutputStream = new ByteArrayOutputStream();
		arrayOutputStream.write(int2bytes(id));
		arrayOutputStream.write(int2bytes(age));
		byte[] byteArray = arrayOutputStream.toByteArray();
		System.out.println(Arrays.toString(byteArray));

		// 反序列化
		ByteArrayInputStream arrayInputStream = new ByteArrayInputStream(byteArray);
		// 讀取第一個4位元組
		byte[] idBytes = new byte[4];
		arrayInputStream.read(idBytes);
		System.out.println("id:" + bytes2int(idBytes));
		// 讀取第二個4位元組
		byte[] ageBytes = new byte[4];
		arrayInputStream.read(ageBytes);
		System.out.println("age:" + bytes2int(ageBytes));
	}
}

3 原生NIO序列化
NIO封裝了相關方法,可以非常優雅得通過putInt和getInt獲取值。不足之處在於ByteBuffer.allocate指定的位元組陣列長度是固定的。
import java.nio.ByteBuffer;
import java.util.Arrays;
public class Test2 {
	public static void main(String[] args) {
		
		// 測試資料
		int id = 8;
		int age = 10;

		// 序列化
		ByteBuffer buffer = ByteBuffer.allocate(8);
		buffer.putInt(id);
		buffer.putInt(age);
		byte[] array = buffer.array();
		System.out.println(Arrays.toString(buffer.array()));

		// 反序列化
		ByteBuffer buffer2 = ByteBuffer.wrap(array);
		System.out.println("id:" + buffer2.getInt());
		System.out.println("age:" + buffer2.getInt());
	}
}

4 Netty中的channelBuffer(ByteBuf)
netty3.X是channelBuffer
netty4.X是ByteBuf
import java.util.Arrays;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
public class Test3 {
	public static void main(String[] args) {
		// 長度可動態擴充套件
		ChannelBuffer buffer = ChannelBuffers.dynamicBuffer();
		buffer.writeInt(8);
		buffer.writeInt(20);

		// 序列化
		byte[] bytes = new byte[buffer.writerIndex()];
		// 從channelBuffer讀取至二進位制陣列
		buffer.readBytes(bytes);
		System.out.println(Arrays.toString(bytes));

		// 反序列化
		ChannelBuffer wrappedBuffer = ChannelBuffers.wrappedBuffer(bytes);
		System.out.println(wrappedBuffer.readInt());
		System.out.println(wrappedBuffer.readInt());
	}
}