序列化:是指将Java对象转换为二进制数据。

反序列化:将二进制数据转换为Java对象。

与序列化功能相关的类有:

  • java.io.Serializable;
  • java.io.ObjectOutputStream(用于序列化)
  • java.io.ObjectInputStream(用于反序列化)

序列化对象的前提:

  • 该对象所属的类实现了 java.io.Serializable 接口
  • 该类的成员变量中有一个是序列化id

反序列化对象的前提:

  • 反序列化对象类也需要实现 java.io.Serializable 接口

  序列化端和反序列化端,序列化对象类和反序列化对象类

  • 两者的类名,包名需要保持一致。否则反序列化时会抛出java.lang.ClassCastException异常。
  • 两者的序列化id需要保持一致。否则反序列化时会抛出java.io.InvalidClassException异常。
  • 两者中的成员变量名保持一致。

  当然,反序列化对象类可以包含额外的成员变量,也可以不包含序列化对象类中的成员变量,只不过这样就无法读取到该成员变量的值。

序列化对象机制的特点

  • 序列化保存的是对象的状态,静态变量属于类的状态,因此 序列化并不保存静态变量。
  • 如果想父类对象也序列化,就需要让父类也实现 Serializable 接口。
  • 实现 Serializable 接口的类,Array,enum 都能能被序列化。

序列化对象加密传输

  服务器端给客户端发送序列化对象数据,对象中有一些数据是敏感的,比如密码字符串等,希望对该密码字段在序列化时,进行加密,而客户端如果拥有解密的密钥,只有在客户端进行反序列化时,才可以对密码进行读取,这样可以一定程度保证序列化对象的数据安全。
Java序列化提供的解决方案:
  在序列化过程中,虚拟机会试图调用对象类里的 writeObject 和 readObject 方法,进行用户自定义的序列化和反序列化,如果没有这样的方法,则默认调用是 ObjectOutputStream 的 defaultWriteObject 方法以及 ObjectInputStream 的 defaultReadObject 方法。用户自定义的 writeObject 和 readObject 方法可以允许用户控制序列化的过程,比如可以在序列化的过程中动态改变序列化的数值。

示例:

项目A:序列化对象类:

package com.java.serializable;

import java.io.ObjectInputStream;
import java.io.ObjectInputStream.GetField;
import java.io.ObjectOutputStream;
import java.io.ObjectOutputStream.PutField;
import java.io.Serializable; public class Class03 implements Serializable {
// 序列化 ID
private static final long serialVersionUID = 1L;
// 序列化时不加密
private String name;
// 序列化时加密
private String password="initValue";
// 测试temp是否也能被自动序列化
private String temp = "test value of temp";
// 以下省略setter、getter方法 private void writeObject(ObjectOutputStream oos) {
try {
PutField fields = oos.putFields();
fields.put("password", encrypt(this.password));
fields.put("name", this.name);
oos.writeFields();
} catch (Exception e) {
e.printStackTrace();
}
}
// 将参数加密
private String encrypt(String pwd) {
return "encryptValue";
}
}

项目A:序列化对象工具类

package com.java.serializable;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream; public class CtestA03 { public static void main(String[] args) {
serializeObjectToFile();
}
// 存放Java对象二进制数据的文件
private static final String PATH = "F:\\objFile.txt";
// 将Java对象序列化为二进制数据存储到文件objFile.txt
private static void serializeObjectToFile() {
ObjectOutputStream oos = null;
try {
oos = new ObjectOutputStream(new FileOutputStream(PATH));
Class03 classObj = new Class03();
classObj.setName("Class03.name");
oos.writeObject(classObj);
} catch (Exception e) {
e.printStackTrace();
} finally {
if(null != oos) {
try {
oos.close();
} catch (IOException e) {}
}
}
}
}

项目B:序列化对象类

package com.java.serializable;

import java.io.ObjectInputStream;
import java.io.ObjectInputStream.GetField;
import java.io.ObjectOutputStream;
import java.io.ObjectOutputStream.PutField;
import java.io.Serializable; public class Class03 implements Serializable {
// 序列化 ID
private static final long serialVersionUID = 1L;
// 昵称:序列化时不加密
private String name;
// 反序列化时需要解密
private String password="initValue";
// 测试temp是否能通过反序列化读取到值
private String temp;
// 以下省略setter、getter方法 private void readObject(ObjectInputStream ois) {
try {
GetField fields = ois.readFields();
String encryptedVar = (String) fields.get("password", "");
this.password = decrypt(encryptedVar);
this.name = (String) fields.get("name", "");
} catch (Exception e) {
e.printStackTrace();
}
}
// 解密参数
private String decrypt(String pwd) {
return "initValue-decrypted";
}
}

项目B:反序列化工具类

package com.java.serializable;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream; public class CtestB03 { public static void main(String[] args) {
reverseSerializeFileToObject();
}
private static final String PATH = "F:\\objFile.txt";
// 反序列化
private static void reverseSerializeFileToObject() {
ObjectInputStream ois = null;
try {
ois = new ObjectInputStream(new FileInputStream(PATH));
Class03 classObj = (Class03) ois.readObject();
System.out.println("classObj.name="+classObj.getName());// classObj.name=Class03.name
System.out.println("classObj.password="+classObj.getPassword());// classObj.password=initValue-decrypted
System.out.println("classObj.temp="+classObj.getTemp());// classObj.temp=null
} catch (Exception e) {
e.printStackTrace();
} finally {
if(null != ois) {
try {
ois.close();
} catch (IOException e) {}
}
}
}
}

执行main方法的结果如下:

System.out.println("classObj.name="+classObj.getName());// classObj.name=Class03.name

未加密的成员变量name,反序列化后得到的仍是序列化之前的值。

System.out.println("classObj.temp="+classObj.getTemp());// classObj.temp=null

序列化对象的成员变量temp,执行writeObject()时,没有将该变量添加到fields中,所以没有被序列化,反序列化后得到的值为null。

System.out.println("classObj.password="+classObj.getPassword());// classObj.password=initValue-decrypted

加密后的成员变量password,会先解密。最后读到的是解密后的密码值initValue-decrypted。

禁止序列化对象的成员变量

  transient 关键字的作用是控制变量的序列化,在变量声明前加上该关键字,可以阻止该变量被序列化到文件中。
  在被反序列化后,transient修饰的变量的值为初始值,如 int 型的是 0,对象型的是 null。

示例:

项目A:序列化对象类

package com.java.serializable;

import java.io.Serializable;

public class Class04 implements Serializable {
// 序列化ID
private static final long serialVersionUID = 1L;
// 昵称
private String nickName;
// 关键字transient修饰,该变量无法被序列化
private transient int age = 26;
// 关键字transient修饰,该变量无法被序列化
private transient String sex = "man";
// 以下省略setter、getter方法
}

项目A:序列化对象工具类

package com.java.serializable;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream; public class CtestA04 {
public static void main(String[] args) {
serializeObjectToFile();
}
private static final String PATH = "F:\\objFile.txt";
// 序列化对象,转换成二进制数据存储到文件objFile.txt
private static void serializeObjectToFile() {
ObjectOutputStream oos = null;
try {
oos = new ObjectOutputStream(new FileOutputStream(PATH));
Class04 classObj = new Class04();
classObj.setNickName("nickName");
oos.writeObject(classObj);
} catch (Exception e) {
e.printStackTrace();
} finally {
if(null != oos) {
try {
oos.close();
} catch (IOException e) {}
}
}
}
}

项目B:反序列化对象

package com.java.serializable;

import java.io.Serializable;

public class Class04 implements Serializable {
private static final long serialVersionUID = 1L;
private String nickName;
private int age;
private String sex;
// 以下省略setter、getter方法
}

项目B:反序列化对象工具类

package com.java.serializable;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream; public class CtestB04 { public static void main(String[] args) {
reverseSerializeFileToObject();
}
// 存储Java对象二进制数据的文件
private static final String PATH = "F:\\objFile.txt";
/**
* 将二进制文件反序列化为java的object对象
*
* 反序列化条件:
* 1.java类的包名一致
* 2.java类中变量名,变量类型一致
* 3.序列化ID一致
*/
private static void reverseSerializeFileToObject() {
ObjectInputStream ois = null;
try {
ois = new ObjectInputStream(new FileInputStream(PATH));
Class04 classObj = (Class04) ois.readObject();
System.out.println("classObj.nickName="+classObj.getNickName());
System.out.println("classObj.age="+classObj.getAge());
System.out.println("classObj.sex="+classObj.getSex());
} catch (Exception e) {
e.printStackTrace();
} finally {
if(null != ois) {
try {
ois.close();
} catch (IOException e) {}
}
}
}
}

执行结果如下:

System.out.println("classObj.nickName="+classObj.getNickName());// classObj.nickName=nickName;

成员变量nickName,可以正常读取值。

System.out.println("classObj.age="+classObj.getAge());// classObj.age=0
System.out.println("classObj.sex="+classObj.getSex());// classObj.sex=null

使用transient 关键字修饰的成员变量age和sex,值为null,说明这两个变量并没有被序列化到二进制文件中。

序列化对象的存储机制

  Java 序列化机制为了节省磁盘空间,具有特定的存储规则,当写入文件的为同一对象时,并不会再将对象的内容进行存储,而只是再次存储一份引用。

示例:

项目A:序列化对象类

package com.java.serializable;

import java.io.Serializable;

public class Class05 implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
// 以下省略setter、getter方法
}

项目A:序列化对象工具类

package com.java.serializable;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream; public class CtestA05 { public static void main(String[] args) {
serializeObjectToFile();
}
private static final String PATH = "D:\\objFile.txt";
// 将 Java 对象序列化到文件objFile.txt中
private static void serializeObjectToFile() {
ObjectOutputStream oos = null;
try {
oos = new ObjectOutputStream(new FileOutputStream(PATH));
// 将对象两次写入文件
Class05 classObj = new Class05();
classObj.setName("classObj.name.1");
oos.writeObject(classObj);
oos.flush();
classObj.setName("classObj.name.2");
oos.writeObject(classObj);
} catch (Exception e) {
e.printStackTrace();
} finally {
if(null != oos) {
try {
oos.close();
} catch (IOException e) {}
}
}
}
}

项目B:反序列化对象

package com.java.serializable;

import java.io.Serializable;

public class Class05 implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
// 以下省略setter、getter方法
}

项目B:反序列化对象工具类

package com.java.serializable;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream; public class CtestB05 { public static void main(String[] args) {
reverseSerializeFileToObject();
}
private static final String PATH = "D:\\objFile.txt";
// 将二进制文件反序列化为java的object对象
private static void reverseSerializeFileToObject() {
ObjectInputStream ois = null;
try {
ois = new ObjectInputStream(new FileInputStream(PATH));
// 从文件依次读出两个文件
Class05 classObj1 = (Class05) ois.readObject();
Class05 classObj2 = (Class05) ois.readObject(); /**
* Java 序列化机制为了节省磁盘空间,具有特定的存储规则,当写入文件的为同一对象时,并不会再将对象的内容进行存储,而只是再次存储一份引用。
* 反序列化时,恢复引用关系,使得classObj1 和 classObj2 指向唯一的对象,二者相等,输出 true。该存储规则极大的节省了存储空间。
*/
System.out.println("classObj1 == classObj2 : "+(classObj1 == classObj2));
System.out.println("classObj1.name="+classObj1.getName());
System.out.println("classObj2.name="+classObj2.getName());
} catch (Exception e) {
e.printStackTrace();
} finally {
if(null != ois) {
try {
ois.close();
} catch (IOException e) {}
}
}
}
}

输出结果如下:

System.out.println("classObj1 == classObj2 : "+(classObj1 == classObj2));// classObj1 == classObj2 : true
System.out.println("classObj1.name="+classObj1.getName());// classObj1.name=classObj.name.1
System.out.println("classObj2.name="+classObj2.getName());// classObj1.name=classObj.name.1

  Java 序列化机制为了节省磁盘空间,具有特定的存储规则,当写入文件的为同一对象时,并不会再将对象的内容进行存储,而只是再次存储一份引用。反序列化时,恢复引用关系,使得classObj1 和 classObj2 指向唯一的对象,二者相等,输出 true。该存储规则极大的节省了存储空间。

最新文章

  1. JavaScript资源大全中文版(Awesome最新版--转载自张果老师博客)
  2. 字符串核对之Boyer-Moore算法
  3. Android HTTPS(4)直接用SSLSocket,黑名单,客户端证书
  4. Mysql在php5中的应用
  5. FZU2176---easy problem (树链剖分)
  6. MongoDB学习笔记-认识MongoDB
  7. 201621123062《java程序设计》第四周作业总结
  8. 一种DTO的规划方案
  9. window环境下使用filezilla server搭建ftp服务器
  10. sql 中 and 和 or的坑
  11. 2017 cinemachine
  12. Fair Candy Swap LT888
  13. 解析ArcGis拓扑——根据拓扑错误记录提取shp文件、导出Excel表格
  14. cocos2dx-lua控制台报错集合
  15. namenode做了ha后kylin出现错误No registered coprocessor service found for name CubeVisitService in region
  16. hibernate12--缓存
  17. python----特殊闭包
  18. Revit API根据链接文件开洞
  19. Javascript数组与类数组对象
  20. Java基础-日期格式化DateFormat类简介

热门文章

  1. Codeforces Round #352 (Div. 2) C. Recycling Bottles
  2. HDU2732 Leapin' Lizards —— 最大流、拆点
  3. HDU1693 Eat the Trees —— 插头DP
  4. Git 对比两分支中同一文件
  5. hdu 1711 Number Sequence 解题报告
  6. Map集合的几种遍历方式
  7. Git如何删除自己创建的项目
  8. 任务49:Identity MVC:Model前端验证
  9. NYOJ6——喷水装置(一)
  10. Node学习图文教程之express重写留言本案例