序列化: 将对象的内容进行流化,将数据分解成字节流,以便存储在文件中或在网络上传输。对象->字节序列
反序列化: 数据转化为对象。字节序列->对象
一、序列化简介
1、进行序列化的原因:
(1)网络传输
网络传输的数据都必须是二进制数据,但是在Java中都是对象,是没有办法在网络中传输的,所以就需要对Java对象进行序列化,并且要求这个序列化是可逆的,也就是说要可以进行反序列化,否则人家都不知道你传递的是啥信息
(2)对象持久化
将内存中的对象状态保存到文件或者数据库中
(3)实现分布式对象
如RMI(远程方法调用)要利用对象序列化运行远程主机上的服务,就像在本地机上运行对象时一样
2、实现序列化的方式
(1)Java原生序列化
需要被序列化的类实现Serializable接口
具体实现:
1)序列化
ObjectOutputStream#writeObject(Object obj)方法
2)反序列化
ObjectInputStream#readObject()方法
缺点:
效率较低,序列化后的流数据比较大
(2)使用第三方的序列化方式
如JSON、Hessian等
3、注意
(1)transient修饰的属性,是不会被序列化的
(2)静态static修饰的属性,是不会被序列化的
二、JSON
JSON(JavaScript Object Notation)是一种轻量级的数据交换格式。易于人阅读和编写,同时也易于机器解析和生成。
注意:
1)虽然 JSON 名称中有 JavaScript,但是 JSON 和 JavaScript 的关系仅仅只是 在设计JSON的时候参照了JavaScript的一些语法而已,两者无必然联系。
2)JSON常用于网络数据的交互,以及将某些数据按照JSON的格式写入到文件进行持久化记录
三、GSON
Github:https://github.com/google/gson
GSON是Google提供的用来在Java对象和JSON数据之间进行映射的Java类库。可以将一个JSON字符串转成一个Java对象(反序列化),或反过来(序列化)
在项目中引用GSON:
Gradle:
dependencies{
implementation 'com.google.code.gson:gson:2.9.1'
}
1、将对象转换为JSON字符串
在需要转换JSON字符串的位置编写代码:
String json = new Gson().toJSON(要转换的对象);
2、将JSON字符串转换为对象
在需要转换Java对象的位置编写代码:
对象 = new Gson().fromJson(JSON字符串, 对象类型.class);
接下来,将从以下几个方面来介绍 GSON 的使用:
1、Java对象序列化与反序列化
public class User {
@Expose
private String userName;
@Expose
private String password;
@Expose
private int age;
@Expose
private boolean isStudent;
@Expose
private Job job;
//serialize,是否参与序列化;deserialize,是否参与反序列化
@Expose(serialize=false, deserialize=false)
private int test1;
private transient int test2;
//无法以class作为字段名
@SerializedName("class")
private int cls;
public User(String userName, String password, int age, boolean isStudent){
this.userName = userName;
this.password = password;
this.age = age;
this.isStudent = isStudent;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public boolean isStudent() {
return isStudent;
}
public void setStudent(boolean student) {
isStudent = student;
}
public Job getJob() {
return job;
}
public void setJob(Job job) {
this.job = job;
}
public int getTest1() {
return test1;
}
public void setTest1(int test1) {
this.test1 = test1;
}
public int getTest2() {
return test2;
}
public void setTest2(int test2) {
this.test2 = test2;
}
public int getCls() {
return cls;
}
public void setCls(int cls) {
this.cls = cls;
}
@NonNull
@Override
public String toString() {
return "userName=" + userName + ", " +
"password=" + password + ", " +
"age=" + age + ", " +
"isStudent=" + isStudent + ", " +
"job=" + job + ", " +
"test1=" + test1 + ", " +
"test2=" + test2 + ", " +
"cls=" + cls;
}
public class ObjectUnitTest {
public void testObject(){
User u1 = new User("Lance", "123", 18, false);
Gson gson = new Gson();
//序列化
String json = gson.toJson(u1);
System.out.println("序列化:" + json);
//反序列化
User u2 = gson.fromJson(json, User.class);
System.out.println("反序列化:" + u2.getUserName());
System.out.println("反序列化:u2: " + u2);
}
public static void main(String args[]){
ObjectUnitTest out = new ObjectUnitTest();
out.testObject();
}
}
结果:
序列化:{"userName":"Lance","password":"123","age":18,"isStudent":false,"test1":0,"class":0}
反序列化:u2: userName=Lance, password=123, age=18, isStudent=false, job=null, test1=0, test2=0, cls=0
Java嵌套对象序列化与反序列化
public class Job {
private String name;
private int salary;
public Job(String name, int salary){
this.name = name;
this.salary = salary;
}
@Override
public String toString(){
return "Job{" +
"name='" + name + '\'' +
", salary = " + salary +
'}';
}
}
public void testNestedObject(){
User u1 = new User("Lance", "123", 18, false);
Job job = new Job("工人", 10000);
u1.setJob(job);
Gson gson = new Gson();
String json = gson.toJson(u1);
System.out.println("序列化::" + json);
User u2 = gson.fromJson(json, User.class);
System.out.println("反序列化::" +u2);
}
结果:
序列化::{"userName":"Lance","password":"123","age":18,"isStudent":false,"job":{"name":"工人","salary":10000},"test1":0,"class":0}
反序列化::userName=Lance, password=123, age=18, isStudent=false, job=Job{name='工人', salary = 10000}, test1=0, test2=0, cls=0
2、Array 和 List 的序列化/反序列化
(1)Array数组的序列化与反序列化
数组类型对象和普通对象一样,使用toJson/fromJson即可完成序列化与返序列化
public void testArray(){
User[] users1 = new User[3];
users1[0] = new User("Lance", "123", 18, false);
users1[1] = new User("Alex", "123", 88, true);
Gson gson = new Gson();
String json = gson.toJson(users1);
System.out.println("序列化:" + json);
User[] users2 = gson.fromJson(json, User[].class);
System.out.println("反序列化:" + users2[0]);
System.out.println("反序列化:" + users2[1]);
System.out.println("反序列化:" + users2[2]);
}
结果:
序列化:[{"userName":"Lance","password":"123","age":18,"isStudent":false,"test1":0,"class":0},{"userName":"Alex","password":"123","age":88,"isStudent":true,"test1":0,"class":0},null]
反序列化:userName=Lance, password=123, age=18, isStudent=false, job=null, test1=0, test2=0, cls=0
反序列化:userName=Alex, password=123, age=88, isStudent=true, job=null, test1=0, test2=0, cls=0
反序列化:null
(2)List集合的序列化和反序列化
public void testListObject(){
List<User> list1 = new ArrayList<>();
list1.add(new User("Lance", "123", 18, false));
list1.add(new User("Alex", "123", 88, true));
list1.add(null);
Gson gson = new Gson();
//序列化
String json = gson.toJson(list1);
System.out.println("List序列化:" + json);
//反序列化
Type type = new TypeToken<List<User>>(){
}.getType();
List<User> list2 = gson.fromJson(json, type); //注意,这里是type
System.out.println("List反序列化:" + list2.get(0));
System.out.println("List反序列化:" + list2.get(1));
System.out.println("List反序列化:" + list2.get(2));
}
结果:
List序列化:[{"userName":"Lance","password":"123","age":18,"isStudent":false,"test1":0,"class":0},{"userName":"Alex","password":"123","age":88,"isStudent":true,"test1":0,"class":0},null]
List反序列化:userName=Lance, password=123, age=18, isStudent=false, job=null, test1=0, test2=0, cls=0
List反序列化:userName=Alex, password=123, age=88, isStudent=true, job=null, test1=0, test2=0, cls=0
List反序列化:null
注意:
fromJson如果第二个参数为List.class的话,Gson不知道List中的泛型类型是什么,不知道接收数据是哪种类。
TypeToken将泛型指定为List中存储的类型。
3、Map 和 Set 的序列化/反序列化
(1)Map的序列化与反序列化
Map集合类型对象在反序列化时与List一样,需要使用TypeToken完成反序列化
public void testMapObject(){
Map<String, User> map1 = new HashMap<>();
//java对象
map1.put("1", new User("Lance","123", 18, false));
map1.put("2", new User("Alex","123", 88, true));
map1.put("3",null);
map1.put(null, null);
//Gson提供的Gson对象
Gson gson = new Gson();
//序列化
String json = gson.toJson(map1);
System.out.println("Map序列化:" + json);
Type type = new TypeToken<Map<String, User>>(){}.getType();
Map<String, User> map2 = gson.fromJson(json, type);
System.out.println("Map反序列化:" + map2.get(null));
System.out.println("Map反序列化:" + map2.get("1"));
}
结果:
Map序列化:{"1":{"userName":"Lance","password":"123","age":18,"isStudent":false,"test1":0,"class":0},"2":{"userName":"Alex","password":"123","age":88,"isStudent":true,"test1":0,"class":0}}
Map反序列化:null
Map反序列化:userName=Lance, password=123, age=18, isStudent=false, job=null, test1=0, test2=0, cls=0
(2)Set的序列化与反序列化
Set在反序列化时同样需要使用TypeToken完成反序列化
public void testSetObject(){
Set<User> set1 = new HashSet<>();
//java对象
set1.add( new User("Lance","123", 18, false));
set1.add(new User("Alex","123", 88, true));
set1.add(null);
//Gson提供的Gson对象
Gson gson = new Gson();
//序列化
String json = gson.toJson(set1);
System.out.println("Set序列化:" + json);
反序列化
// Type type = new TypeToken<List<User>>(){}.getType();
// //如果是 HashSet 类型,则完全可以使用反序列为List,因为ArrayList和HashSet 两者序列化后的 Json 数据一致。
// List<User> set2 = gson.fromJson(json, type);
// System.out.println("Set反序列化:" + set2.get(0));
// System.out.println("Set反序列化:" + set2.get(1));
// System.out.println("Set反序列化:" + set2.get(2));
//反序列化
Type type = new TypeToken<Set<User>>(){}.getType();
//Set需要使用迭代器,不能直接get
Set<User> set2 = gson.fromJson(json, type);
Iterator<User> iterator = set2.iterator();
while(iterator.hasNext()){
User next = iterator.next();
System.out.println("Set反序列化:" + next);
}
}
结果:
Set序列化:[null,{"userName":"Lance","password":"123","age":18,"isStudent":false,"test1":0,"class":0},{"userName":"Alex","password":"123","age":88,"isStudent":true,"test1":0,"class":0}]
Set反序列化:null
Set反序列化:userName=Lance, password=123, age=18, isStudent=false, job=null, test1=0, test2=0, cls=0
Set反序列化:userName=Alex, password=123, age=88, isStudent=true, job=null, test1=0, test2=0, cls=0
4、变量值为null时的序列化/反序列化
(1)如果一个变量为NULL,那么按照 GSON 默认的处理为忽略这个字段
public void testVarNull(){
User u1 = new User("Lance", "123", 18, false); //job字段没有赋值,默认为NULL
//Gson提供的Gson对象
Gson gson = new Gson();
//序列化
String json = gson.toJson(u1);
System.out.println(json);
//反序列化
User u2 = gson.fromJson(json, User.class);
System.out.println(u2);
}
结果:
{"userName":"Lance","password":"123","age":18,"isStudent":false,"test1":0,"class":0}
userName=Lance, password=123, age=18, isStudent=false, job=null, test1=0, test2=0, cls=0
当某个属性为null时,Gson在序列化时会忽略这个属性
(2)如果一个集合中的存储的数据为NULL,那么按照GSON默认的处理为不会忽略
List<User> list1 = new ArrayList<>();
list1.add(new User("Lance", "123", 18, false));
list1.add(null);
Gson gson = new Gson();
//序列化
String json = gson.toJson(list1);
System.out.println("序列化:" + json);
结果:
序列化:[{"userName":"Lance","password":"123","age":18,"isStudent":false,"test1":0,"class":0},null]
5、控制序列化/反序列化的变量名称
(1)如果希望JSON字符串字段名不用变量名作为Key(比如User类的userName被作为Key),又或者希望JSON字符串中的Key存在Java中的关键字时(如 “class”:18),可以借助 @SerializedName 注解控制 JSON 字段中 Key 的命名。
class User{
...
@SerializedName("class")
private int cls;
}
(2)如果希望指定GSON对某些字段配置是否参与序列化与反序列化,可以使用 @Expose 注解控制,同时使用 GsonBuilder 创建 Gson 对象。
可以点进去“@Expose”看下源码,可以发现可以设置变量是否参加 序列化/反序列化
默认情况下,变量是可以参加序列化/反序列化的
要使得 “@Expose” 生效,就不是 new Gson() 了,而是new GsonBuilder().excludeFieldWithoutExposeAnnotation().create();
(3)transient关键字的作用:使得变量不参加任何序列化/反序列化