什么是序列化和反序列化?

序列化:将对象转化为字节的过程称为序列化过程。
反序列化:将字节转化为对象的过程称为反序列化。

序列化主要应用于网络传输和数据存储的场景。在java中,只有类实现了java.io.serializable接口,该类才能被序列化。
示例Demo1.java:

package com.example.demo;import java.io.*;public class Demo1 {public static class Person implements Serializable {private static final long serialVersionUID = 233858934995755239L;private String firstName;private String lastName;public Person(String firstName,String lastName){System.out.println("Init Person...");this.firstName = firstName;this.lastName = lastName;}public String toString() {return String.format("Pearson.toString(): firstName=%s,lastName=%s", firstName, lastName);}}public static void main(String[] args) {String firstName="Li",LastName="Kelly";Person person  = new Person(firstName, LastName);System.out.println("序列化前:"+person.toString());ObjectOutputStream outStream;ObjectInputStream inStream;String filePath = "D:/demo/person.obj";try {//创建一个ObjectOutputStream输出流outStream = new ObjectOutputStream(new FileOutputStream(filePath));//将对象序列化到文件filePathoutStream.writeObject(person);inStream = new ObjectInputStream(new FileInputStream(filePath));Person readObject = (Person)inStream.readObject();System.out.println("反序列化后:"+readObject.toString());} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (ClassNotFoundException e) {// TODO Auto-generated catch blocke.printStackTrace();}}
}

输出:

Init Person...
序列化前:Pearson.toString(): firstName=Li,lastName=Kelly

反序列化后:Pearson.toString(): firstName=Li,lastName=Kelly

可见对象反序列化,并不没有调用类的构造方法(反序列化没有输出Init Person...)。

注:

如果一个可序列化的类的成员不是基本类型,也不是String类型,那这个引用类型也必须是可序列化的,也即继承serializable,否则,会导致此类不能序列化。

比如上面的代码中,我们给Person类加一个Dog对象,这个Dog没有继承serializable。

package com.example.demo;import java.io.*;public class Demo1 {public static class Dog{}public static class Person implements Serializable {private static final long serialVersionUID = 233858934995755239L;private String firstName;private String lastName;private Dog dog;public Person(String firstName,String lastName, Dog dog){System.out.println("Init Person...");this.firstName = firstName;this.lastName = lastName;this.dog = dog;}public String toString() {return String.format("Pearson.toString(): firstName=%s,lastName=%s", firstName, lastName);}}public static void main(String[] args) {String firstName="Li",LastName="Kelly";Dog dog= new Dog();Person person  = new Person(firstName, LastName,dog);System.out.println("序列化前:"+person.toString());ObjectOutputStream outStream;ObjectInputStream inStream;String filePath = "D:/demo/person.obj";try {//创建一个ObjectOutputStream输出流outStream = new ObjectOutputStream(new FileOutputStream(filePath));//将对象序列化到文件filePathoutStream.writeObject(person);inStream = new ObjectInputStream(new FileInputStream(filePath));Person readObject = (Person)inStream.readObject();System.out.println("反序列化后:"+readObject.toString());} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (ClassNotFoundException e) {// TODO Auto-generated catch blocke.printStackTrace();}}
}

此时执行代码会报java.io.NotSerializableException: com.example.demo.Demo1$Dog的错误。让Dog继承serializable则不会报错。

问:

①同一个对象序列化多次,会将这个对象序列化多次吗?否。java序列化同一对象,并不会将此对象序列化多次得到多个对象。

            outStream = new ObjectOutputStream(new FileOutputStream(filePath));//将对象序列化到文件filePathoutStream.writeObject(person);outStream.writeObject(person);inStream = new ObjectInputStream(new FileInputStream(filePath));Person readObject1 = (Person)inStream.readObject();Person readObject2 = (Person)inStream.readObject();System.out.println(readObject1==readObject2);System.out.println("反序列化后:"+readObject1.toString());

output:

Init Person...
序列化前:Pearson.toString(): firstName=Li,lastName=Kelly
true
序列化后:Pearson.toString(): firstName=Li,lastName=Kelly

ReadObject1和ReadObject2是同一对象。

②有些时候,我们有这样的需求,某些属性不需要序列化。怎么做?使用transient关键字选择不需要序列化的字段。

示例:

package com.example.demo;import java.io.*;public class Demo1 {public static class Dog implements Serializable{}public static class Person implements Serializable {private static final long serialVersionUID = 233858934995755239L;private String firstName;private transient String lastName;private transient int age;private Dog dog;public Person(String firstName,String lastName, Dog dog, int age){System.out.println("Init Person...");this.firstName = firstName;this.lastName = lastName;this.dog = dog;this.age = age;}public String toString() {return String.format("Pearson.toString(): firstName=%s,lastName=%s,age=%s", firstName, lastName,age);}}public static void main(String[] args) {String firstName="Li",LastName="Kelly";Dog dog= new Dog();Person person  = new Person(firstName, LastName,dog,23);System.out.println("序列化前:"+person.toString());ObjectOutputStream outStream;ObjectInputStream inStream;String filePath = "D:/demo/person.obj";try {//创建一个ObjectOutputStream输出流outStream = new ObjectOutputStream(new FileOutputStream(filePath));//将对象序列化到文件filePathoutStream.writeObject(person);inStream = new ObjectInputStream(new FileInputStream(filePath));Person readObject = (Person)inStream.readObject();System.out.println("反序列化后:"+readObject.toString());} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (ClassNotFoundException e) {// TODO Auto-generated catch blocke.printStackTrace();}}
}

输出:

Init Person...
序列化前:Pearson.toString(): firstName=Li,lastName=Kelly,age=23
反序列化后:Pearson.toString(): firstName=Li,lastName=null,age=0

从输出我们看到,使用transient修饰的属性(此处是lastName、age),java序列化时,会忽略掉此类字段。

反序列化出的对象,被transient修饰的属性是默认值。对于引用类型,值是null;基本类型,值是0;boolean类型,值是false。

③使用transient虽然简单,但是将此属性完全隔离在了序列化之外。有没有一种机制,可以定制对象序列化的方式?

答:有。通过重写writeObject与readObject方法,可以自己选择哪些属性需要序列化, 哪些属性不需要。如果writeObject使用某种规则序列化,则相应的readObject需要相反的规则反序列化,以便能正确反序列化出对象。

示例:

package com.example.demo;import java.io.*;public class Demo1 {public static class Dog implements Serializable{}public static class Person implements Serializable {private static final long serialVersionUID = 233858934995755239L;private String firstName;private transient String lastName;private transient int age;private Dog dog;public Person(String firstName,String lastName, Dog dog, int age){System.out.println("Init Person...");this.firstName = firstName;this.lastName = lastName;this.dog = dog;this.age = age;}public String toString() {return String.format("Pearson.toString(): firstName=%s,lastName=%s,age=%s", firstName, lastName,age);}private void writeObject(ObjectOutputStream out) throws IOException {//将名字反转写入二进制流out.writeObject(new StringBuffer(this.lastName).reverse());out.writeInt(age);}private void readObject(ObjectInputStream ins) throws IOException,ClassNotFoundException{//将读出的字符串反转恢复回来this.lastName = ((StringBuffer)ins.readObject()).reverse().toString();this.age = ins.readInt();}}public static void main(String[] args) {String firstName="Li",lastName="Kelly";Dog dog= new Dog();Person person  = new Person(firstName, lastName, dog,23);System.out.println("序列化前:"+person.toString());ObjectOutputStream outStream;ObjectInputStream inStream;String filePath = "D:/demo/person.obj";try {//创建一个ObjectOutputStream输出流outStream = new ObjectOutputStream(new FileOutputStream(filePath));//将对象序列化到文件filePathoutStream.writeObject(person);inStream = new ObjectInputStream(new FileInputStream(filePath));Person readObject = (Person)inStream.readObject();System.out.println("反序列化后:"+readObject.toString());} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (ClassNotFoundException e) {// TODO Auto-generated catch blocke.printStackTrace();}}
}

在上面的例子中,我们给Person类增加了writeObject和readObject方法。

private void writeObject(ObjectOutputStream out) throws IOException {//将名字反转写入二进制流out.writeObject(new StringBuffer(this.lastName).reverse());out.writeInt(age);}private void readObject(ObjectInputStream ins) throws IOException,ClassNotFoundException{//将读出的字符串反转恢复回来this.lastName = ((StringBuffer)ins.readObject()).reverse().toString();this.age = ins.readInt();}

输出:

Init Person...
序列化前:Pearson.toString(): firstName=Li,lastName=Kelly,age=23
反序列化后:Pearson.toString(): firstName=null,lastName=Kelly,age=23

在writeObject里,首先out.writeObject(new StringBuffer(this.lastName).reverse());这句的作用是将名字反转写入二进制流。我们打开D:/demo/person.obj文件,看下lastName的形式是不是反转的。

person.obj文件是乱码的,但是我们仍然可以看到我们想要的,Kelly变成了yllek。

writeObject第二句是out.writeInt(age);age属性我们加了transient关键字,理论上这个属性应该不会被序列化。然而我们在这里加上了这行代码,所以age又可以被序列化。

除了这个,我们还可以看到writeObject和readObject都是private方法,但是它却被外部类(ObjectOutputStream和ObjectInputStream)调用,并且它们既不存在与Java.lang.Object,也没有在Serializeable中声明,那它们是如何被ObjectOutputStream和ObjectInputStream调用的呢?
答:利用反射机制。ObjectOutputStream和ObjectInputStream使用了反射来寻找是否声明了这两个方法。因为它们使用getPrivateMethod,所以这些方法不得不被声明为priate以至于供ObjectOutputStream来使用。

注:Write的顺序和read的顺序需要对应。譬如writeObject有多个字段都用writeInt写入流中,那么readint需要按照顺序将其赋值。

例:

private void writeObject(ObjectOutputStream out) throws IOException {//将名字反转写入二进制流out.writeObject(new StringBuffer(this.lastName).reverse());out.writeInt(age);out.writeObject(this.firstName);}private void readObject(ObjectInputStream ins) throws IOException,ClassNotFoundException{//将读出的字符串反转恢复回来this.lastName = ((StringBuffer)ins.readObject()).reverse().toString();this.firstName =(String) ins.readObject();this.age = ins.readInt();}

此时代码报错:

java.io.OptionalDataException
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1585)

现在主要在公众号PostgreSQL运维技术 更新,欢迎来踩~

悄悄放一张:

PostgreSQL运维技术

java序列化之writeObject 和readObject相关推荐

  1. Java序列化魔术方法及其示例使用

    在上一篇文章中, 您需要了解有关Java序列化的所有知识 ,我们讨论了如何通过实现Java序列化来启用类的可序列化性. Serializable接口. 如果我们的类未实现Serializable接口, ...

  2. Java序列化的机制和原理

    有关Java对象的序列化和反序列化也算是Java基础的一部分,下面对Java序列化的机制和原理进行一些介绍. Java序列化算法透析 Serialization(序列化)是一种将对象以一连串的字节描述 ...

  3. java基础(十)-----Java 序列化的高级认识

    将 Java 对象序列化为二进制文件的 Java 序列化技术是 Java 系列技术中一个较为重要的技术点,在大部分情况下,开发人员只需要了解被序列化的类需要实现 Serializable 接口,使用 ...

  4. java序列化和RMI

    深入了解序列化"契约" 由于Java提供了良好的默认支持,实现基本的对象序列化是件比较简单的事.待序列化的Java类只需要实现Serializable接口即可.Serializab ...

  5. 什么是java序列化_什么是Java序列化?为什么序列化?序列化有哪些方式?

    先普及一下,计算机中无法识别一个基本单元[字节]来表示,必须经过"翻译"才能让计算机理解人类的语言,这个翻译过程就是[编码],通常所说的字符转换为字节. ?有I/O的地方机就会涉及 ...

  6. Java序列化的作用和反序列化

    1.序列化是干什么的? 简单说就是为了保存在内存中的各种对象的状态(也就是实例变量,不是方法),并且可以把保存的对象状态再读出来.虽然你可以用你自己的各种各样的方法来保存object states,但 ...

  7. Java序列化技术与Protobuff

    前言: Java序列化是Java技术体系当中的一个重要议题,序列化的意义在于信息的交换和存储,通常会和io.持久化.rmi技术有关(eg:一些orm框架会要求持久化的对象类型实现Serializabl ...

  8. 深入理解JAVA序列化

    2019独角兽企业重金招聘Python工程师标准>>> 如果你只知道实现 Serializable 接口的对象,可以序列化为本地文件.那你最好再阅读该篇文章,文章对序列化进行了更深一 ...

  9. Java 序列化的高级认识

    这篇文章来自:http://www.ibm.com/developerworks/cn/java/j-lo-serial/index.html 引言 将 Java 对象序列化为二进制文件的 Java ...

最新文章

  1. SSL ×××的强劲发展势头似乎表明,它将取代IPSec ×××
  2. UA OPTI512R 傅立叶光学导论11 卷积的性质
  3. 交多个朋友不如交一个朋友
  4. ABAP GUI Text Edit
  5. P2796-Facer的程序【dp】
  6. python 线程退出_python线程退出
  7. 企业实战_10_MyCat Mysql 主复制总结
  8. 快速突破面试算法之图篇
  9. 解析二进制文件的工具方法
  10. 卷积神经网络概念与原理
  11. 关于js的match用法
  12. 淘宝API 添加上传商品图片
  13. Rosalind Java|k-Mer Composition
  14. AI智能语音机器人安装 --小白如何安装智能电话机器人
  15. 两波形相位差的计算值_国际学科备考系列A-level物理:相位和相位差的数学解释及物理应用...
  16. C# 编辑器 Editor
  17. IOS NDDictionary使用中value遇到no summary如何判断为NULL
  18. JavaScript语法 ES6、ES7、ES8、ES9、ES10、ES11、ES12新特性汇总
  19. JS:Caesars Cipher(凯撒密码)
  20. 弹性云服务器的规格系列,新睿云简析云服务器的配置规格怎么选择?

热门文章

  1. 苹果手机微信浏览器select标签选择完成之后页面不会自动回到原位
  2. java+Selenium+TestNg搭建自动化测试架构(1)实现代码和数据的分离
  3. g2o图优化库入门介绍
  4. windows 命令行下的find和findstr命令
  5. CSS:宽高自适应详解
  6. setup/teardown 的用法
  7. 小米3联通电信通刷_2013062 2013063_官方线刷包_救砖包_解账户锁
  8. 2022-01-02中国电信香港CN2线路服务的具体特点以及判断真假CN2的方法(详细)
  9. 巡检机器人应用在哪些场景?
  10. [论文学习]3——在工业炼油中应用VM-SAE的案例分析