-
因为编辑文章时不小心用了回退键,可能导致代码出错,请自行查错
- 1 Java序列化的作用
- 我们创建一个Building.class
-
-
public class Building { private String name; private String address; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } }
-
-
- 如果需要传输对象
-
public static void main(String[] args) { Building b = new Building(); b.setName("创业大厦"); b.setAddress("天宁兰陵兰陵路26号"); System.out.println(b); }
-
- 我们可以看到,传输的仅仅是类名
-
test.j3.c2.Building@2503dbd3
-
- 这时,我们就需要对Building进行序列化
- 序列化:把对象转换为字节序列的过程称为对象的序列化
- 序列化是将对象的数据和信息转换为可以存储或传输的形式过程,需要序列化的情况为:
- 当内存中的对象需要保存到一个文件中、数据库、缓存中的时候;
- 当对象需要传输的时候
- String类支持序列化
- 我们创建一个Building.class
- 2 序列化
- 对象实现了Serializable接口,就表示可以被序列化
- 该接口知识一个标记接口,也叫空接口,没有任何方法,所以实现了接口也不需要实现方法
- Building.java
-
import java.io.Serializable; public class Building implements Serializable { // 自定义serialVersionUID private static final long serialVersionUID = 8735132092273200831L; private String name; private String address; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } }
-
- Building.java中有一个serialVersionUID
- serialVersionUID主要是序列化和反序列化过程中,验证类的版本用的
- 例如:
- Building类有两个属性:name、address,如果先把Building对象序列化,然后修改Building类,再反序列化就会失败
- 为了避免这种情况,给serialVersionUIF赋一个固定的值,那么无论Building类增加几个属性,都能正确反序列化
- 自动生成serialVersionUID
- IDE工具可以方便的提供自动生成serialVersionUID值的功能
- 以IntelliJ IDEA为例:
Setting
->Inspections
->Serialization issues
->Serializable class without 'serialVersionUID'
- 点击”OK”后,打开代码。鼠标指到类名上
- 点开黄色灯泡提示按钮的小箭头,选择ADD ‘serialVersionUID’ field
- JSON
- JSON: javaScipt Object Notation(JavaScript对象表示法)是目前最常用的对象序列化方式
- JSON官方制定了一套标准,各种语言都支持这一套标砖,所以JSON也能作为一种跨语言的文本数据交换格式,无障碍地把数据传递到其它语言的程序
- JSON基本格式
- 必须是:对象
-
{ }
-
- 或:数组
-
[ ]
-
- 必须是:对象
- JSON语法规则
- 数据用 名称:值(也叫键值对〕表示
- 名称(键)必须是字符串
- 键、值之间用冒号”:”分隔。
- 多条数据之间,用逗号”,”分隔
- 符号都是半角
- 数据用 名称:值(也叫键值对〕表示
- JSON值的类型
- 数字(整数或浮点数)
- 字符串(在双引号中)
- 逻辑值(true或false)
- 数组(在中括号中)
- 对象(在大括号中)
- null
- 值可以是数组、对象,就意味着数据和对象可以任意嵌套、任意深度
- eg:
-
{ "name": "韦小宝", "age": 26, "height": 182.4, "birthday": "1670-7-28", "isRich": true, "wifes": ["阿珂", "双儿", "建宁公主", "苏荃", "沐剑屏", "曾柔", "方怡"], "firstMaster": { "name": "陈近南", "birthday": "1634-12-1" } }
-
- FastJSON
- FastJSON是一个Java语言编写的高性能、功能完善、完全支持官方标注你的JSON库
- 使用其来操作JSON以及完成对象序列化、反序列化的操作,会非常方便
- 对象序列化
- IDE中的设置
- 首先导入阿里的FaseJSON包
- 然后引入依赖
-
<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson --> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.59</version> </dependency>
-
- 最后在使用时导入
-
import com.alibaba.fastjson.JSON;
-
- 用FastJson打印对象
-
public static void main(String[] args) { Building b = new Building(); b.setName("创业大厦"); b.setAddress("天宁兰陵兰陵路26号 "); String content = JSON.toJSONString(b); System.out.println(content); }
-
- IDE中的设置
- 练习
- 使用序列化,在console上打印”研究生公寓’,位于”杭州市江干区学林
街628号,有’54″间房间,建筑面积”380平米”的公寓的完整信息 - 参考类图
- 代码
- Apartment.java
-
import java.io.Serializable; public class Apartment extends Building implements Serializable { private int totalRooms; private String builtupArea; public int getTotalRooms() { return totalRooms; } public void setTotalRooms(int totalRooms) { this.totalRooms = totalRooms; } public String getBuiltupArea() { return builtupArea; } public void setBuiltupArea(String builtupArea) { this.builtupArea = builtupArea; } }
- Building.java
-
import java.io.Serializable; public class Building { private String name; private String address; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } }
- ObjectPrinter.java
-
import com.alibaba.fastjson.JSON; public class ObjectPrinter { public static void main(String[] args) { Apartment apartment = new Apartment(); apartment.setName("研究生公寓"); apartment.setAddress("杭州市江干区学林街628号"); apartment.setTotalRooms(54); apartment.setBuiltupArea("380平米"); String content = JSON.toJSONString(apartment); System.out.println(content); } }
- 使用序列化,在console上打印”研究生公寓’,位于”杭州市江干区学林
- 3 反序列化
- 把字节序列恢复为对象的过程称为对象的反序列化
- 在具体案例中,就是将JSON格式字符串还原为对象的过程
- 反序列化代码示例
-
public static void main(String[] args) { Building b = new Building(); b.setName("创业大厦"); b.setAddress("天宁兰陵兰陵路26号 "); String content = JSON.toJSONString(b); // 转换为一个具体的对象 Building b2 = JSON.parseObject(content, Building.class); String name = b2.getName(); System.out.println(name); // 特殊情况下,java系统里没有具体对象的 class ,可以反序列化为 Map Map bInfo = JSON.parseObject(content, Map.class); String name2 = (String) bInfo.get("name"); System.out.println(name2); }
-
- 转化为一般对象
- 反序列化时,调用
-
JSON.parseObject(content, Building.class)
- 第一个参数是字符串内容,第二个参数是目标类
-
- 转换为具体的对象后,就可以使用对象的属性和方法了
- 反序列化时,调用
- 转化为Map
- 少数复杂的场景,程序需要把字符串转换为对象,但是系统中又没有依赖具体的目标,这时可以把字符串转换为Map对象,用get(“关键字”)的方法获取对象的具体属性
- 练习
- 使用序列化,在console上打印”研究生公寓’,位于”杭州市江干区学林
街628号,有’54″间房间,建筑面积”380平米”的公寓的完整信息 - 然后再反序列化为两种对象
- 反序列化为“公寓”对象,在console打印公寓的名称
- 反序列化为“Map”对象,在console打印公寓的名称
- 参考类图
- 代码
- Apartment.java
-
import java.io.Serializable; public class Apartment extends Building implements Serializable { private int totalRooms; private String builtupArea; public int getTotalRooms() { return totalRooms; } public void setTotalRooms(int totalRooms) { this.totalRooms = totalRooms; } public String getBuiltupArea() { return builtupArea; } public void setBuiltupArea(String builtupArea) { this.builtupArea = builtupArea; } }
- Building.java
-
package com.youkeda.test.j3.c2; import java.io.Serializable; public class Building { private String name; private String address; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } }
- ObjectAssembler.java
-
package com.youkeda.test.j3.c2; import java.util.*; import com.alibaba.fastjson.JSON; public class ObjectAssembler{ public static void main(String[] args) { Apartment apartment = new Apartment(); apartment.setName("研究生公寓"); apartment.setAddress("杭州市江干区学林街628号"); apartment.setTotalRooms(54); apartment.setBuiltupArea("380平米"); String content = JSON.toJSONString(apartment); System.out.println(content); Apartment b2 = JSON.parseObject(content, Apartment.class); String name = b2.getName(); System.out.println(name); // 特殊情况下,java系统里没有具体对象的 class ,可以反序列化为 Map Map bInfo = JSON.parseObject(content, Map.class); String name2 = (String) bInfo.get("name"); System.out.println(name2); } }
- 使用序列化,在console上打印”研究生公寓’,位于”杭州市江干区学林
- 4 序列化的例外情形
- 不想类的某些属性被序列化时,需要在序列化过程中排除某些字段
- Building.java
-
import com.alibaba.fastjson.annotation.JSONField; import java.io.Serializable; public class Building implements Serializable { // 自定义serialVersionUID private static final long serialVersionUID = 8735132092273200831L; private transient String name; @JSONField(serialize=false) private String address; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } }
-
- 常用排除字段的方式有两种
- 在字段上增加一个
@JSONField(serialize=false)
注解,这个注解是fastjson
库提供的 - 在声明字段时,增加一个
transient
修饰关键字,这个关键字是JDK
本身提供的
- 在字段上增加一个
- 对于一个属性,这两种方法任选其一,在执行序列化的时候,都可以实现忽略其值
- 需要特别注意的是:这两种方式只对“序列化”过程有效,对“反序列化”过程无效
- 反序列化时,不受影响,只要 JSON 字符串有内容,都会按规则反序列化为对象并为属性赋值,不会因为属性字段标记为排除而不赋值
- 练习
- 使用序列化,在console上打印”研究生公寓’,位于”杭州市江干区学林
街628号,有’54″间房间,建筑面积”380平米”的公寓的完整信息 - 然后再反序列化为两种对象
- 公寓的“建筑面积”、“地址”两个属性设置为不可序列化
- 反序列化为“公寓”对象,在console打印公寓的名称
- 反序列化为“Map”对象,在console打印公寓的名称
- 参考类图
- 代码
- Apartment.java
-
import java.io.Serializable; public class Apartment extends Building implements Serializable { private int totalRooms; private transient String builtupArea; public int getTotalRooms() { return totalRooms; } public void setTotalRooms(int totalRooms) { this.totalRooms = totalRooms; } public String getBuiltupArea() { return builtupArea; } public void setBuiltupArea(String builtupArea) { this.builtupArea = builtupArea; } }
- Building.java
-
package com.youkeda.test.j3.c2; import com.alibaba.fastjson.annotation.JSONField; import java.io.Serializable; public class Building { private String name; @JSONField(serialize=false) private String address; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } }
- ObjectAssembler2.java
-
package com.youkeda.test.j3.c2; import com.alibaba.fastjson.JSON; import java.util.Map; public class ObjectAssembler2 { public static void main(String[] args) { Apartment apartment = new Apartment(); apartment.setName("研究生公寓"); apartment.setAddress("杭州市江干区学林街628号"); apartment.setTotalRooms(54); apartment.setBuiltupArea("380平米"); String content = JSON.toJSONString(apartment); System.out.println("Apartment2 对象="+content); Apartment b2 = JSON.parseObject(content, Apartment.class); System.out.println("Apartment2 对象建筑面积="+b2.getBuiltupArea()); // 特殊情况下,java系统里没有具体对象的 class ,可以反序列化为 Map Map bInfo = JSON.parseObject(content, Map.class); System.out.println("Map.address 值=" + (String) bInfo.get("address")); } }
- 使用序列化,在console上打印”研究生公寓’,位于”杭州市江干区学林
- 5 持久化
- 对象经过处理,冉存储在介质上的过程,就叫做持久化(Persistence)
- 持久化包含对象的存储和读取两个过程
- 实际上,由于对象是存储在计算机的内存中,是临时性的,所以必须有一种方式,能够存储下来,不随着程序停止运行或计算机关机等异常况而丢失
- 常见的久化方式有
- 写入数据库
- 对象写入数据,一般来说数据库都有对应的字段,使用Mybatis等框架完成读写数据,这种信况一般不需要使用序列化。但是对于一些不会检索、需要有扩展性的字段来说,可以在数据库表结构的对应字段中,存储巧JSON格式的字符串,在Java程序中再次反序列化成一个对象,使用方便且具备扩展性
- 写入远程的缓存系统
- 这种信况下通用的方法,就是把对象序列化为JSON字符,存入缓存。使用起来方便而简单
- 写入文件
- 在云时代,这种情况不太多见了,偶尔有会有,把对象序列化为JSON字符串写入文件。需要的时候再读取文件内容反序列化成对象即可
- 写入数据库
- 本文前面节所举的案例,都是一次性的输入/输出,main函数只运行了一次
- 但是实际工作中,特别是互朕网时代,绝大多数情况都是客户端与服务端之间的通讯,实际上输入/输出的通信量非常大,常常是几万、几十万个客户端与服务端通讯,服务端就必须并发响应客户端的请求
- 那么服务端要如何做,才能并发响应呢?那么请看
我还没有写笔记的Java多线程编程