对象的序列化(Serialize):将内存中的Java对象转换为与平台无关的二进制流(字节序列),然后存储在磁盘文件中,或通过网络传输给另一个网络节点。

对象的反序列化(Deserialize):获取序列化的二进制流(不管是通过网络,还是通过读取磁盘文件),将之恢复为原来的Java对象。

要实现对象的序列化,该对象所属的类必须要是可序列化的,即该类必须实现以下2个接口之一:

  • Serializable     这只是一个标记接口,此接口只是表明该类是可序列化的,不用实现任何方法。Java自带的类基本都已implement  Serializable,不用我们操心,我们自定义的类                         要实现序列化,必须要手写implement  Serializable。
  • Externalizable      用于实现自定义序列化

要通过网络传输的Java对象、要保存到磁盘的Java对象必须要是可序列化的,不然程序会出现异常。

JavaEE的分布式应用往往要跨平台、跨网络,通过网络传输的Java对象必须要是可序列化的,HttpSession、ServletContext等Java  Web对象都已实现序列化。如果我们要通过网络传输自定义的Java对象,该类(一般是JavaBean)要是可序列化的。通常建议:把所有的JavaBean都写成是可序列化的。

ObjectOutputStream是一个处理流,提供了void  writeObject(Object  obj)方法用于对象的序列化(对象输出流,输出到文件/网络)。

ObjectInputStream也是一个处理流,提供了Object  readObject()方法用于反序列化(对象输入流,读取文件/网络中的对象到内存)。

示例:

 //要实现Serializable接口(并不用实现任何方法)
class Student implements Serializable{
private int id;
private String name;
private int age; public Student(int id,String name,int age){
this.id=id;
this.name=name;
this.age=age;
} public int getId(){
return this.id;
} public void setId(int id){
this.id=id;
} public String getName(){
return this.name;
} public void setName(String name){
this.name=name;
} public int getAge(){
return this.age;
} public void setAge(int age){
this.age=age;
}
  //序列化
ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("./obj.txt")); //处理流,要建立在节点流之上
Student zhangsan=new Student(1,"张三",20);
oos.writeObject(zhangsan); //writeObject(Object obj)用于序列化一个对象(输出到文件/网络节点) //反序列化
ObjectInputStream ois=new ObjectInputStream(new FileInputStream("./obj.txt"));
Student student=(Student)ois.readObject(); //返回的是Object类型,所以往往要强制类型转换 System.out.println("学号:"+student.getId());
System.out.println("姓名:"+student.getName());
System.out.println("年龄:"+student.getAge());

ObjectInputStream、ObjectOutputStream也包含了其他IO方法,可输入/输出多种类型的数据,即可以字符为单位进行操作,又可以字节为单位进行操作。

writeObject(Object  obj)一次只能写出一个对象,可多次调用,向同一文件中写入多个对象;

readObject()一次只能读一个对象,读取一个对象后,指针自动后移,指向下一个对象。读的顺序和写的顺序是一一对应的。

示例:

  //序列化,向同一文件中写入多个对象
ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("./obj.txt")); //处理流,要建立在节点流之上
Student zhangsan=new Student(1,"张三",20);
Student lisi=new Student(2,"李四",20);
Student wangwu=new Student(3,"王五",20);
oos.writeObject(zhangsan); //张三
oos.writeObject(lisi); //李四
oos.writeObject(wangwu); //王五 //反序列化,读取顺序和写入顺序是一致的
ObjectInputStream ois=new ObjectInputStream(new FileInputStream("./obj.txt"));
Student zhangsan1=(Student)ois.readObject(); //张三
Student lisi1=(Student)ois.readObject(); //李四
Student wangwu1=(Student)ois.readObject(); //王五

如果要序列化的类有父类(直接或者间接),那么这些父类必须要是可序列化的,或者具有无参的构造函数,否则会抛出 InvalidClassException  异常。

 class Student extends People implements Serializable{
//......
}

如果Student要序列化,则有2种方案可选:

  • 其父类People也要是可序列化的(递归)。       这样Student继承自People中的成员才可以序列化输出到文件/网络。
  • 其父类不是可序列化的,但是其父类有无参的构造函数。      这样不会报错,但Student继承自People中的成员不会序列化输出到文件/网络。

如果该类持有其它类的引用,则引用的类都要是可序列化的,此类才是可序列化的。

 class Student implements Serializable {
private int id;
private String name; //Java自带的类基本都实现了可序列化,不用管
private int age;
private Teacher teacher; //此类持有一个Teacher类的引用。Teacher类必须要是可序列化的,Student类才是可序列化的。
//......
}

如果要多次输出同一个对象,则第一次输出的是该对象的字节序列,以后每次输出的是该对象的编号。

   ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("./obj.txt"));
Student zhangsan=new Student(1,"张三",20); oos.writeObject(zhangsan); //第一次序列化这个对象,输出此对象的字节序列。假设此对象的编号为1,编号唯一标识此对象。
oos.writeObject(zhangsan); //再次输出此对象,输出的是此对象的编号,直接输出1。读取此对象时读取的仍是zhangsan这个对象。
oos.writeObject(zhangsan); //直接输出1。
//类似于c++中的指针,通过编号指向某个已存在对象。减少了重新序列化对象的时间、内存开销。

如果中间修改了此对象,再次序列化此对象时,修改后的结果不会同步到文件/网络节点中。

  ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("./obj.txt"));
Student zhangsan=new Student(1,"张三",20); oos.writeObject(zhangsan); //初次序列化,输出此对象的字节序列。假设编号为1
zhangsan.setName("李四"); //之后修改了此对象
oos.writeObject(zhangsan); //再次输出此对象,输出的是编号1。1代表编号为1的对象。 /*
在此对象第一次序列化之后,对此对象做修改,后面再次输出此对象时,都是指向第一次输出的那个对象,做的修改并不会写到文件/网络节点中。
读取的也都是第一次输出的那个对象。 实际上,序列化一个对象时,JVM会先检查在本次虚拟机中是否已序列化过这个对象,如果已序列化过,直接输出对应的序列化编号,未序列化过才序列化。
     JVM是以对象名来区分序列化的是否是同一个对象。对象名相同,JVM就认为序列化的是同一个对象,直接输出该对象的编号,并不判断此对象是否修改过。 修改此对象后,就算我们把此对象序列化到另一个文件中,输出的也是此对象第一次序列化时的编号,修改并不会同步到这个文件中。
*/

Java9新增了过滤功能,读取对象时,可以先检查该对象是否满足指定的要求,满足才允许恢复,否则拒绝恢复。

  ObjectInputStream ois=new ObjectInputStream(new FileInputStream("obj.txt"));
/*
参数是一个ObjectInputFilter接口的对象,函数式接口,只需实现checkInput()方法。
参数info是FilterInfo类的对象。 返回值要是预定义的常量:
Status.ALLOWED 允许恢复
Status.REJECTED 拒绝恢复
Status.UNDECIDED 未决定,将继续执行检查
*/
ois.setObjectInputFilter((info)->{
ObjectInputFilter serialFilter=ObjectInputFilter.Config.getSerialFilter(); //获取默认的输入过滤器
if(serialFilter!=null){
//先使用默认的过滤器进行检查
ObjectInputFilter.Status status=serialFilter.checkInput(info);
//如果已知道结果
if (status!= ObjectInputFilter.Status.UNDECIDED)
return status;
}
//执行到此,就说明结果是Status.UNDECIDED,还不知道结果,要继续检查
//如果引用不唯一,说明数据被污染了,不允许恢复
if(info.references()!=1)
return ObjectInputFilter.Status.REJECTED;
//是指定的类的对象(Student),才允许恢复(执行反序列化)
if(info.serialClass()!=null && info.serialClass() != Student.class)
return ObjectInputFilter.Status.ALLOWED;
//执行到此,说明不是指定类的对象,不允许恢复。
return ObjectInputFilter.Status.REJECTED;
});
//这样恢复的对象都是唯一的Student对象
//...........

Java 对象的序列化、反序列化的更多相关文章

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

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

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

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

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

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

  4. Java基础学习总结——Java对象的序列化和反序列化

    一.序列化和反序列化的概念 把对象转换为字节序列的过程称为对象的序列化. 把字节序列恢复为对象的过程称为对象的反序列化. 对象的序列化主要有两种用途: 1) 把对象的字节序列永久地保存到硬盘上,通常存 ...

  5. Java对象的序列化和反序列化

    对象的序列化是指将对象转换为字节序列的过程 对象的反序列化是指将字节序列恢复对象的过程 主要有两种用途: 1.把对象的字节序列永久地保存在硬盘上,通常放在一个文件中. 2.在网络上传输对象的字节序列. ...

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

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

  7. Java对象的序列化和反序列化实践

    2013-12-20 14:58 对象序列化的目标是将对象保存在磁盘中,或者允许在网络中直接传输对象.对象序列化机制允许把内存中的Java对象转换成平台无关的二进制流,从而允许把这种二进制流持久的保存 ...

  8. Java基础学习总结--Java对象的序列化和反序列化

    一.序列化和反序列化的概念 把对象转换成字节序列的过程称之为对象的序列化 把字节序列恢复为对象的过程称之为对象的反序列化 对象序列化的主要用途: 1)把对象的字节序列永久的保存到硬盘上,通常放在一个文 ...

  9. 深入理解Java对象的序列化与反序列化的应用

    当两个进程在进行远程通信时,彼此可以发送各种类型的数据.无论是何种类型的数据,都会以二进制序列的形式在网络上传送.发送方需要把这个Java对象转换为字节序列,才能在网络上传送:接收方则需要把字节序列再 ...

  10. java 对象的序列化与反序列化

    一.序列化和反序列化的概念 把对象转换为字节序列的过程称为对象的序列化. 把字节序列恢复为对象的过程称为对象的反序列化. 对象的序列化主要有两种用途: 1) 把对象的字节序列永久地保存到硬盘上,通常存 ...

随机推荐

  1. leetcode-24-exercise

    506. Relative Ranks 解题思路: 使用priority_queue.它在插入时会将数据按照由大到小的顺序插入,自然排序了.所以插入时考虑插入pair<nums[i],i> ...

  2. Linux学习-开机过程的问题解决

    忘记 root 密码的解决之道 新版的 systemd 的管理机制中,默认的 rescue 模式是无法直接取得 root 权限的喔!还是得要 使用 root 的密码才能够登入 rescure 环境.没 ...

  3. 树状数组 - BZOJ 1103 [POI2007]大都市

    bzoj 1103 [POI2007]大都市 描述 在经济全球化浪潮的影响下,习惯于漫步在清晨的乡间小路的邮递员 Blue Mary也开始骑着摩托车传递邮件了.不过,她经常回忆起以前在乡间漫步的情景. ...

  4. Linux入门(一)

    Linux安装的注意问题: 关键的两点: 1)为Linux操作系统准备硬盘空间: 2)启动ISO镜像文件中的安装程序. 前期准备:   1.硬盘分区魔术师   2.grub 纯DOS环境   3.Ub ...

  5. SpringBoot 项目打包部署Resin遇到的问题

    1)javax/validation/ParameterNameProvider 找不到. 解决:A) resin/lib 目录下删掉原来的,validation-api 更新为 validation ...

  6. Web网站性能测试分析及调优实例

    1 背景   前段时间,性能测试团队经历了一个规模较大的门户网站的性能优化工作,该网站的开发和合作涉及多个组织和部门,而且网站的重要性不言而喻,同时上线时间非常紧迫,关注度也很高,所以对于整个团队的压 ...

  7. K-means算法的优缺点

    K-means算法的优缺点 优点:原理简单,实现容易 缺点: 收敛较慢 算法时间复杂度比较高 \(O(nkt)\) 不能发现非凸形状的簇 需要事先确定超参数K 对噪声和离群点敏感 结果不一定是全局最优 ...

  8. 国际化多语言(本地化)缩写 NLS API

    NLS Information for Windows 7 LCID Culture Identifier Culture Name Locale Language Country/Region La ...

  9. [uiautomator篇] 使用uiautomator需要导入uiautomator库

    1 修改依赖文件:build/gradle( 是在app目录下)而不是和app同级目录的build/gradle androidTestCompile 'com.android.support.tes ...

  10. [整理]配置SSH密钥自动登录远程服务器

    原理: 公钥私钥匹配通过验证,允许访问服务器. 简单步骤: 1.在本地创建一对密钥 2.将公钥传到需要访问的服务器上 3.将公钥放入服务器的authorized_keys,确保访问时能通过验证 4.本 ...