一:前期准备工作
1.要实现对象的序列化,先要了解一下java的8种基本数据类型及相关的储存字节大小
8中类型所占字节和位数如下:
类型 | 占用字节 | 占用位数 | 说明 |
byte | 1 | 8 | |
short | 2 | 16 | |
int | 4 | 32 | |
long | 8 | 64 | |
float | 4 | 32 | |
double | 8 | 64 | |
char | 2 | 16 | |
boolean | 1 | 8 | 实际上占用一个字节还是一个位,在或者是四个字节?
|
参阅:
https://blog.csdn.net/YuanMxy/article/details/74170745
是否通过验证方式:
1.通过查看生成class文件的内部结构来验证了 ,可以用jdk 自带的工具: javap -verbose TestBoolean -> NO:自码文件中的boolean 类型只是用Z来标注,而并没有转化成其它的数据类型
2,hexdump -C filename可以查看二进制文件 -> hexdump -C Test.class
下载 hexdump https://www.di-mgt.com.au/hexdump-for-windows.html
二进制:
十进制:
十六进制: 0x就是代表十六进制,0x00 - 0xFF
对于二进制来说,8位二进制我们称之为一个字节,二进制的表达范围值是从0b00000000~0b11111111,
而我们程序中用十六进制表示的时候就是从0x00到0xFF,这里教大家一个二进制转换十进制和十六进制的方法,
二进制4位一组,遵循8,4,2,1的规律比如 1010,那么从最高位开始算,数字大小是8*1+4*0+2*1+1*0 = 10,
那么十进制就是10,十六进制就是0xA。尤其二进制转十六进制的时候,十六进制一位刚好是和二进制的4位相互对应的
2.负数在计算机内是如何表示的
原码:
反码:
补码:
3.字节序列化模式:大端模式与小端模式
大端模式:高位在前
小端模式:低位在前
示例:
/**
* 以大端模式将int转成byte[]
*/
public static byte[] intToBytesBig(int value) {
byte[] src = new byte[4];
src[0] = (byte) ((value >> 24) & 0xFF);
src[1] = (byte) ((value >> 16) & 0xFF);
src[2] = (byte) ((value >> 8) & 0xFF);
src[3] = (byte) (value & 0xFF);
return src;
}
/**
* 以小端模式将int转成byte[]
*
* @param value
* @return
*/
public static byte[] intToBytesLittle(int value) {
byte[] src = new byte[4];
src[3] = (byte) ((value >> 24) & 0xFF);
src[2] = (byte) ((value >> 16) & 0xFF);
src[1] = (byte) ((value >> 8) & 0xFF);
src[0] = (byte) (value & 0xFF);
return src;
}
/**
* 以大端模式将byte[]转成int
*/
public static int bytesToIntBig(byte[] src, int offset) {
int value;
value = (int) (((src[offset] & 0xFF) << 24)
| ((src[offset + 1] & 0xFF) << 16)
| ((src[offset + 2] & 0xFF) << 8)
| (src[offset + 3] & 0xFF));
return value;
}
/**
* 以小端模式将byte[]转成int
*/
public static int bytesToIntLittle(byte[] src, int offset) {
int value;
value = (int) ((src[offset] & 0xFF)
| ((src[offset + 1] & 0xFF) << 8)
| ((src[offset + 2] & 0xFF) << 16)
| ((src[offset + 3] & 0xFF) << 24));
return value;
}
二:基本数据类型的序列化操作
查看示例代码
package custserialize;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.nio.ByteBuffer;
import java.util.Arrays;
/**
* 基本数据类型是如何序列化的
* 首先也就是要确定基本数据类型是如何转化成字节的。通常有两种方式
* 大端:高位在前
* 小端:低位在前
*/
public class TypeSerialize {
public static void main(String[] args) throws Exception{
short age=120;
int sales=123456;
//1:字节输出流
System.out.println("************************JDK api*******************************");
ByteArrayOutputStream outputStream=new ByteArrayOutputStream();
outputStream.write(shortToBytes(age));
outputStream.write(intToBytes(sales));
byte[] bytes=outputStream.toByteArray();
System.out.println("result bytes="+Arrays.toString(bytes));
//解析,按照写入的顺序读出相应的字节大小
ByteArrayInputStream inputStream=new ByteArrayInputStream(bytes);
byte[] ageByte=new byte[2];
byte[] saleByte=new byte[4];
inputStream.read(ageByte);
inputStream.read(saleByte);
System.out.println("age="+bytesToShort(ageByte));
System.out.println("sales="+bytesToInt(saleByte));//bytesToInt bytes2Int
//2.Nio的ByteBuffer,缺点就是必须先确定要分配的字节数组的大小,不能动态的进行扩容
System.out.println("**********************JDK NIO Api**************************************");
ByteBuffer buffer = ByteBuffer.allocate(4);
buffer.putInt(sales);
byte[] b1=buffer.array();
System.out.println("int byte="+Arrays.toString(b1));
System.out.println("reback ="+bytes2Int(b1));
//3.Netty中提供的ChannelBuffer
System.out.println("****************************Netty Api************************************");
ChannelBuffer channelBuffer = ChannelBuffers.dynamicBuffer();
channelBuffer.writeInt(10101);
channelBuffer.writeFloat(9999.99f);
channelBuffer.writeChar('A');
byte[] channelBufferBytes=new byte[channelBuffer.writerIndex()];//channelbuffer 写的位置
channelBuffer.readBytes(channelBufferBytes);
System.out.println("channel buffer bytes="+Arrays.toString(channelBufferBytes));
ChannelBuffer readChannelBuffer=ChannelBuffers.wrappedBuffer(channelBufferBytes);
System.out.println("netty int="+readChannelBuffer.readInt());
System.out.println("netty float="+readChannelBuffer.readFloat());
System.out.println("netty char="+readChannelBuffer.readChar());
}
/**
* 采用大端字节序列(即是先写高位,在写低位)
* 返回的字节数组中,byte[0]对应的是原数据位表示的最高位
* @param param
* @return
*/
public static byte[] shortToBytes(short param){
// short 在jdk的规范说明中占用2个字节
byte[] bytes=new byte[2];
bytes[0]=(byte)(param >> 1*8);
bytes[1]=(byte)(param >> 0*8);
return bytes;
}
/**
* 采用大端字节序列(即是先写高位,在写低位)
* 返回的字节数组中,byte[0]对应的是原数据位表示的最高位
* @param bytes
* @return
*/
public static short bytesToShort(byte[] bytes){
short result=(short)( (bytes[0] << 1*8 ) // 第一痊为高位
|
(bytes[1] << 0*8)
);
return result;
}
/**
* 将int 类型转化为字节数组
* @param param
* @return
*/
public static byte[] intToBytes(int param){
byte[] fbyte=new byte[4];//int 占用四个字节
fbyte[0] = (byte)(param >> 3*8);
fbyte[1] = (byte)(param >> 2*8);
fbyte[2] = (byte)(param >> 1*8);
fbyte[3] = (byte)(param >> 0*8);
return fbyte;
}
/**
*
* @param bytes
* @return
*/
public static int bytesToInt(byte[] bytes){
return ( (bytes[0] & 0xFF) << 3*8) |
( (bytes[1] & 0xFF)<< 2*8) |
( (bytes[2] & 0xFF) << 1*8) |
( (bytes[3] & 0xFF) << 0*8);
}
/*****************************************************/
/**
* 大端方式
* @param bytes
* @return
*/
private static int bytes2Int(byte[] bytes) {
int addr;
if (bytes.length == 1) {
addr = bytes[0] & 0xFF;
} else {
addr = bytes[0] & 0xFF;
addr = (addr << 8) | (bytes[1] & 0xff);
addr = (addr << 8) | (bytes[2] & 0xff);
addr = (addr << 8) | (bytes[3] & 0xff);
}
return addr;
}
/**
* 大端方式
* @param bytes 数组大端
* @return float
*/
private float bytes2Float(byte[] bytes) {
int l;
l = bytes[0];
l &= 0xff;
l |= ((long) bytes[1] << 8);
l &= 0xffff;
l |= ((long) bytes[2] << 16);
l &= 0xffffff;
l |= ((long) bytes[3] << 24);
return Float.intBitsToFloat(l);
}
}