名词解释

序列化:将Java对象转化成字节的过程

反序列化:将字节转化成Java对象的过程

字节:1字节(byte)= 8bit,bit就是计算机认识的二进制

序列化的作用

Java对象是在Java虚拟机中使用的,一旦Java进程结束,对象就会消失,要将只有虚拟机才认识的对象,保存在磁盘中,必须将对象转化成字节。

  1. 在RPC中的用处:序列化将对象转换为字节流,然后通过网络传输进行发送
  2. 保存对象的状态:当Java进程需要重启时,可以将对象序列化后保存在文件中,对象的状态不会因为进程的关闭而丢失

如何进行序列化

基本数据类型转为字节的思路

对于有多个字节的数据,用移位运算符,将每8位进行移位,用一个字节保存

  • Int类型:一个int有4个字节,可以创建一个长度为4的字节数组进行保存(short,long类似)
  • char类型:一个char有2个字节,用相应长度的字节数组保存后,反序列化时再强制转化为char
  • String类型:String的值主要是一个char数组,创建一个大小为char数组两倍的字节数组进行保存,反序列化时再转化为String
  • Double和Float类型:过程比较复杂(没学会),建议直接调用工具类

一个字节和其他类型的转换工具类

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;
} }

如何序列化对象

其实序列化就是为了反序列化,保存对象之后必然要读取对象,所以站在反序列化的角度去研究序列化

得到一串字节流之后,要如何转换成对象

  • 要通过反射创建对象,首先要知道对象的类名称,然后调用类的默认构造函数

    • 要识别类名称,得知道类名称是由哪些字节转换的
    • 要有一个值存放类名称的字节长度,长度前最好放一个标记
  • 给对象的字段赋值,各种类型都不一样,需要识别
    • 需要数据类型的字节长度,根据长度去识别字节数组中的一部分
    • 将字节转换成指定的数据类型
    • 通过反射给对象的字段赋值

序列化的代码实现如下

  • 这里没有将对象的类型写入字节数组,因为反序列化的方法会传入一个class参数,可以直接构造对象
 1 public static byte[] serialize(Object object) throws IllegalAccessException, ClassNotFoundException {
2
3 // 对象序列化后的字节数组
4 byte[] objectByte = new byte[1024];
5 // 通过移动的下标变量不断往数组添加数据
6 int index = 0;
7 // 标记后面的字节可以转化成对象
8 objectByte[index] = SerializationNum.SERIALIZATION_OBJECT_NUM;
9
10 Class clazz = object.getClass();
11 // 遍历所有字段,将字段的值转化为字节
12 Field[] fields = clazz.getDeclaredFields();
13 // 一开始的标记占了一个位置
14 int len = 1;
15 for (Field field : fields) {
16
17 // 将private属性设置为可访问
18 field.setAccessible(true);
19
20 // 每次移动下标,给后面的数据腾地方
21 index += len;
22 // 不同的类型,不同的转化方式
23 Class fieldClass = field.getType();
24 // 每种类型对应一个标记
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个字节
33 len = 4;
34 // 转化成一个长度为4的字节数组
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 // 将类型和长度都添加字节数组中
54 objectByte[index++] = magicNum;
55 objectByte[index++] = (byte) len;
56 // 转化后的字节复制到对象字节数组
57 System.arraycopy(valueByte,0,objectByte,index,len);
58 }
59
60 index += len;
61 // 对象已经整个被转化完毕的标记
62 objectByte[index] = SerializationNum.FINISH_NUM;
63
64 return objectByte;
65 }

反序列化过程实现

 1 public static Object deserialize(byte[] bytes,Class clazz) throws InstantiationException, IllegalAccessException {
2
3 int index = 0;
4 // 识别首部,确保是能够反序列化成对象的字节
5 if (bytes[index] != SerializationNum.SERIALIZATION_OBJECT_NUM) {
6 return null;
7 }
8
9 // 使用默认构造方法
10 Object obj = clazz.newInstance();
11 // 跳过最开始的魔数
12 byte len = 1;
13
14 // 一个个给对象的字段赋值
15 for (Field declaredField : clazz.getDeclaredFields()) {
16 declaredField.setAccessible(true);
17 // 每次移动下标
18 index += len;
19
20 // 不同的类型,不同的转化方式
21 Class fieldClass = declaredField.getType();
22
23 byte magicNum = SerializationNum.getNum(fieldClass);
24
25 // 防止数组越界
26 if (index >= bytes.length || bytes[index] != magicNum){
27 continue;
28 }
29
30 Object value = null;
31 // 先获取需要的字节长度
32 len = bytes[++index];
33 // 创建一个新数组去作为方法参数,如果不采用这种传参方式,也许能节约这个空间
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 }

对各种基本数据类型进行特殊标记的工具类

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;
} }

测试代码

 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("可以将对象序列号后的字节重新反序列化成对象");
11 byte[] objByte = serialize(entity);
12 Entity entity1 = (Entity) deserialize(objByte,Entity.class);
13 System.out.println(entity1);
14
15
16 System.out.println("保存在文件的字节,取出来后也可以反序列成对象");
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("类不能被构造");
29 e.printStackTrace();
30 }
31
32 }

测试结果:

Java:对一个对象序列化和反序列化的简单实现的更多相关文章

  1. 【Java基础】序列化与反序列化深入分析

    一.前言 复习Java基础知识点的序列化与反序列化过程,整理了如下学习笔记. 二.为什么需要序列化与反序列化 程序运行时,只要需要,对象可以一直存在,并且我们可以随时访问对象的一些状态信息,如果程序终 ...

  2. Java对象的序列化与反序列化

    序列化与反序列化 序列化 (Serialization)是将对象的状态信息转换为可以存储或传输的形式的过程.一般将一个对象存储至一个储存媒介,例如档案或是记亿体缓冲等.在网络传输过程中,可以是字节或是 ...

  3. Java对象的序列化和反序列化[转]

    Java基础学习总结--Java对象的序列化和反序列化 一.序列化和反序列化的概念 把对象转换为字节序列的过程称为对象的序列化.把字节序列恢复为对象的过程称为对象的反序列化. 对象的序列化主要有两种用 ...

  4. java中的序列化与反序列化,还包括将多个对象序列化到一个文件中

    package Serialize; /** * Created by hu on 2015/11/7. */ //实现序列化必须实现的接口,这就是一个空接口,起到标识的作用 import java. ...

  5. 【Java】Java原生的序列化和反序列化

    写一个Java原生的序列化和反序列化的DEMO. 需序列化的类: package com.nicchagil.nativeserialize; import java.io.Serializable; ...

  6. Java对象的序列化与反序列化-Json篇

    说到Java对象的序列化与反序列化,我们首先想到的应该是Java的Serializable接口,这玩意在两个系统之间的DTO对象里面可能会用到,用于系统之间的数据传输.或者在RPC(远程方法调用)时可 ...

  7. JAVA基础之——序列化和反序列化

    1 概念 序列化,将java对象转换成字节序列的过程. 反序列化,将字节序列恢复成java对象的过程. 2 为什么要序列化? 2.1 实现数据持久化,当对象创建后,它就会一直在,但是在程序终止时,这个 ...

  8. java对象的序列化与反序列化使用

    1.Java序列化与反序列化  Java序列化是指把Java对象转换为字节序列的过程:而Java反序列化是指把字节序列恢复为Java对象的过程. 2.为什么需要序列化与反序列化 我们知道,当两个进程进 ...

  9. K:java中的序列化与反序列化

    Java序列化与反序列化是什么?为什么需要序列化与反序列化?如何实现Java序列化与反序列化?以下内容将围绕这些问题进行展开讨论. Java序列化与反序列化 简单来说Java序列化是指把Java对象转 ...

随机推荐

  1. Linux:$?,$n,$#,$0

    $? 获取执行上一个指令的返回值(0为成功,非零为失败) $n 获取当前执行的shell脚本的第n个参数值,n=1...9,当n=0的时表示脚本的文件名,如果n大于9,大括号括起来${10} $# 获 ...

  2. 【C#】【MySQL】C#连接MySQL数据库(二)解析

    C# MySQL 实现简单登录验证 后端代码解析 Visual Studio中使用MySQL的环境配置 下文所有到的代码(前端后端) 请查阅这篇博文 C#连接MySQL数据库(一)代码 获取前端数据 ...

  3. 【C++】使用VS2022开发可以在线远程编译部署的C++程序

    前言: 今天没有前言. 一.先来一点C++的资源分享,意思一下. 1.c++类库源码以及其他有关资源.站点是英文的,英文不好的话可以谷歌浏览器在线翻译.http://www.cplusplus.com ...

  4. ios self.和_区别

    - "self."调用该类的setter或getter方法,"_"直接获取自己的实例变量.property 和 instance variable 是有区别的. ...

  5. 位置式PID讲解

    table { margin: auto } 一.公式拆解 \(PID\)公式展示: \[u(t)=K_p(e(t)+\frac{1}{T_t } ∫_0^te(t)dt+T_D \frac {de( ...

  6. [BUUCTF]REVERSE——[GKCTF2020]BabyDriver

    [GKCTF2020]BabyDriver 附件 步骤: 例行检查,64位程序,无壳 64位ida载入,检索程序里的字符串,看到提示flag是md5(input),下方还看到了类似迷宫的字符串 找到关 ...

  7. [BUUCTF]PWN——inndy_rop

    inndy_rop 附件 步骤: 例行检查,32位,开启了nx保护 本地调试运行没看出个啥,直接上ida,一开始f5会报错, 找到报错提示的位置,点击option–>general调出如图的界面 ...

  8. 筛选Table.SelectRows-文本与数值(Power Query 之 M 语言)

    数据源: 包含文本与数值的任意数据 目标: 对文本和数值进行筛选 M公式: = Table.SelectRows( 表, 筛选条件) 筛选条件: 等于:each [指定列] = "指定值&q ...

  9. CF793A Oleg and shares 题解

    Content 有 \(n\) 支股票,第 \(i\) 支股票原价为 \(a_i\) 卢布.每秒钟可能会有任意一支股票的价格下降 \(k\) 卢布,以至于降到负数.求所有股票的价格均变得相同所要经过的 ...

  10. LuoguB2044 有一门课不及格的学生 题解

    Content 给出一名学生的语数英三门成绩,请判断该名学生是否恰好有一门不及格(成绩小于 \(60\) 分). 数据范围:成绩在 \(0\sim 100\) 之间. Solution 强烈建议先去做 ...