Java提供的序列化和反序列化
序列化:是指将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。该存储规则极大的节省了存储空间。
Java提供的序列化和反序列化的更多相关文章
- 【Java基础】序列化与反序列化深入分析
一.前言 复习Java基础知识点的序列化与反序列化过程,整理了如下学习笔记. 二.为什么需要序列化与反序列化 程序运行时,只要需要,对象可以一直存在,并且我们可以随时访问对象的一些状态信息,如果程序终 ...
- Java对象的序列化与反序列化
序列化与反序列化 序列化 (Serialization)是将对象的状态信息转换为可以存储或传输的形式的过程.一般将一个对象存储至一个储存媒介,例如档案或是记亿体缓冲等.在网络传输过程中,可以是字节或是 ...
- Java对象的序列化与反序列化-Json篇
说到Java对象的序列化与反序列化,我们首先想到的应该是Java的Serializable接口,这玩意在两个系统之间的DTO对象里面可能会用到,用于系统之间的数据传输.或者在RPC(远程方法调用)时可 ...
- 【Java】Java原生的序列化和反序列化
写一个Java原生的序列化和反序列化的DEMO. 需序列化的类: package com.nicchagil.nativeserialize; import java.io.Serializable; ...
- Java对象的序列化和反序列化[转]
Java基础学习总结--Java对象的序列化和反序列化 一.序列化和反序列化的概念 把对象转换为字节序列的过程称为对象的序列化.把字节序列恢复为对象的过程称为对象的反序列化. 对象的序列化主要有两种用 ...
- JAVA基础之——序列化和反序列化
1 概念 序列化,将java对象转换成字节序列的过程. 反序列化,将字节序列恢复成java对象的过程. 2 为什么要序列化? 2.1 实现数据持久化,当对象创建后,它就会一直在,但是在程序终止时,这个 ...
- java中的序列化与反序列化,还包括将多个对象序列化到一个文件中
package Serialize; /** * Created by hu on 2015/11/7. */ //实现序列化必须实现的接口,这就是一个空接口,起到标识的作用 import java. ...
- Java中的序列化与反序列化
序列化定义 将对象转换为字节流保存起来,并在以后还原这个对象,这种机制叫做对象序列化. 将一个对象保存到永久存储设备上称为持久化. 一个对象要想能够实现序列化,必须实现java.io.Serializ ...
- 在Java中进行序列化和反序列化
对象序列化的目标是将对象保存在磁盘中,或者允许在网络中直接传输对象. 对象序列化允许把内存中的Java对象转换成平台无关的二进制流,从而允许把这种二进制流持久保存在磁盘上或者通过网络将这种二进制流传输 ...
随机推荐
- 最小生成树,并查集的思想 nyoj1239
#include<stdio.h> #include<string.h> #include<algorithm> using namespace std; int ...
- 一个点亮屏幕的service
这个版本是只能点亮不能解锁的版本(注意很多句子都被注释掉了,那部分是用来实现解锁屏幕的),达到了预期的效果,特此纪念. 把代码贴出来: package com.larry.msglighter; im ...
- BZOJ_4311_向量_线段树按时间分治
BZOJ_4311_向量_CDQ分治+线段树按时间分治 Description 你要维护一个向量集合,支持以下操作: 1.插入一个向量(x,y) 2.删除插入的第i个向量 3.查询当前集合与(x,y) ...
- 英特尔、联发科、展讯等开始支持开源的物联网轻量化操作系统AliOS Lite
操作系统AliOS Lite Chaos 12-21 11:03 在 12 月 20 日的云栖大会北京峰会上,阿里宣布即将开源 AliOS Lite,此前面向 IoT 领域的轻量级物联网嵌入式操作系统 ...
- VMware Workstation安装centos 6.5详细步骤
转自“http://blog.51cto.com/12496630/2058386” 22.选择分区了,centos新版中使用lvm来分区,我不用过分去计算分区大小,这个模式可以允许用户以后动态调整分 ...
- 039--CSS
一.CSS定义 CSS 规则由两个主要的部分构成:选择器,以及一条或多条声明. ''' selector { property: value; property: value; ... propert ...
- Epoll简介以及例子
第一部分:Epoll简介 问题 : Select,Poll和Epoll的区别 答案 : Epoll和Select的区别 1. 遍历方式的区别.select判断是否有事件发生是遍历的,而epoll是事 ...
- Codeforces Round #324 (Div. 2)C. Marina and Vasya
A的万般无奈...后来跑了大牛的这份代码发现, 题意是求一个序列与给定的两个序列有t个不同. 只要保证...对应位置就行了.. 所以处理起来非常方便.............. 可是没有感觉是对应位置 ...
- 国产spfa瞎几把嗨
//在各种不利的因素下,我居然就这么水过了这题最短路,然而还wa了一次,因为路是双向的... //这题姿势很多啊,但自从会了国产spfa就是最短路能搞的就是spfa,优点太多了!!! //也是瞎几把打 ...
- 解决 CentOS 7 添加用户设置家目录出现 useradd cannot set SELinux context for home directory 问题
问题描述 直接贴下代码吧~ [root@localhost ~]# useradd -d /tmp/heheda4 heheda4 useradd: cannot set SELinux contex ...