• 概述
  • 方法概述
  • 哪些类型的对象有资格进行序列化
  • ObjectInputStream ObjectOutputStream
  • javaioSerializable Externalizable Interfaces
    • javaioExternalizable Interface

      • 示例
  • 代码

概述

数据流(DataInputStream和DataOutputStream)允许我们读取和写入原始数据(如int,double)和String,而不是单个字节。 对象流(ObjectInputStream和ObjectOutputStream)进一步让我们读取和写入整个对象(如Date,ArrayList或任何自定义对象)。

对象序列化是在序列化比特流(bit-stream)中表示“对象的特定状态”的过程,从而将比特流写入外部设备(例如,磁盘文件或网络)。 我们也可以重新构造bit-stream以恢复该对象的状态。

对象序列化对于将对象的状态保存到磁盘文件中以进行持久化是必需的,或者通过网络将对象发送到Web服务,分布式对象应用程序和远程方法调用(RMI)等应用程序。

在Java中,需要序列化的对象必须实现java.io.Serializable或java.io.Externalizable接口。 Serializable接口是一个没有声明的空接口(或标记接口)。 其目的只是声明特定的对象是可序列化的。


方法概述

ObjectOutputStream类实现了ObjectOutput接口,该接口定义了将对象写入输出流的方法:

writeObject(Object)

将对象写入底层存储或流。 如果发生I / O错误,此方法将抛出IOException异常。

将对象写入输出流的过程称为序列化。

ObjectOutput接口从DataOutput接口扩展,这意味着ObjectOutputStream继承了写入基本类型和字符串的所有行为,如DataOutputStream。

同样,ObjectInputStream类实现了ObjectInput接口,该接口定义了一种从输入流读取对象的方法:

readObject()

读取并返回一个对象。
如果找不到序列化对象的类,则此方法抛出ClassNotFoundException,如果发生I / O错误,则抛出IOException。

从输入流重建对象的过程称为反序列化。

ObjectInput接口从DataInput接口扩展,这意味着ObjectInputStream还具有读取原始类型和字符串(如DataInputStream)的行为。

下面的类图描述了对象流类和接口的API层次结构:


哪些类型的对象有资格进行序列化?

请注意,只有实现java.io.Serializable接口的类的对象才能被写入输出/输入流并从其读取。

Serializable是一个标记界面,它没有定义任何方法。

只有标记为’serializable’的对象可以与ObjectOutputStream和ObjectInputStream一起使用。

Java中的大多数类(包括Date和原始包装器Integer,Double,Long等)都实现了Serializable接口。 我们必须为我们的自定义类实现此接口。

如果尝试编写一个不可序列化的类的对象,将抛出一个java.io.NotSerializableException.


ObjectInputStream & ObjectOutputStream

StudentRecordWriter

package com.xgj.master.java.io.fileDemo.byteStreams.objectStreams;import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;import org.junit.Test;/*** * * @ClassName: StudentRecordWriter* * @Description: This class will uses the ObjectOutputStream class to write a*               list of Students object to a file on disk* * @author: Mr.Yang* * @date: 2017年9月7日 下午5:25:28*/
public class StudentRecordWriter {@Testpublic void writeStudent2DiskFile() {DateFormat dateFormat = new SimpleDateFormat("yyyy-mm-dd");// JDK 7中的写法try (ObjectOutputStream oos = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(new File("D:\\StudentRecord.txt"))))) {List<Student> studentList = new ArrayList<>();studentList.add(new Student("Xiao", dateFormat.parse("1993-02-15"), false, 23, 80.5f));studentList.add(new Student("Gong", dateFormat.parse("1994-10-03"), true, 22, 95.0f));studentList.add(new Student("Jiang", dateFormat.parse("1995-08-22"), false, 21, 79.8f));for (Student student : studentList) {oos.writeObject(student);}} catch (IOException | ParseException ex) {ex.printStackTrace();}}
}

StudentRecordReader

package com.xgj.master.java.io.fileDemo.byteStreams.objectStreams;import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.text.DateFormat;
import java.text.SimpleDateFormat;import org.junit.Test;/*** * * @ClassName: StudentRecordReader* * @Description: StudentRecordReader uses the ObjectInputStream class to read*               objects from a file on disk* * @author: Mr.Yang* * @date: 2017年9月7日 下午5:31:50*/
public class StudentRecordReader {@Testpublic void readStudentFromDiskFile() {DateFormat dateFormat = new SimpleDateFormat("yyyy-mm-dd");try (ObjectInputStream ois = new ObjectInputStream(new BufferedInputStream(new FileInputStream(new File("D:\\StudentRecord.txt"))))) {while (true) {Student student = (Student) ois.readObject();System.out.print(student.getName() + "\t");System.out.print(dateFormat.format(student.getBirthday()) + "\t");System.out.print(student.getGender() + "\t");System.out.print(student.getAge() + "\t");System.out.println(student.getGrade());}} catch (IOException | ClassNotFoundException e) {e.printStackTrace();}}}

默认情况下,原始类型和数组是可序列化的。

ObjectInputStream和ObjectOutputStream分别实现DataInput和DataOutput接口。我们可以使用readInt(),readDouble(),writeInt(),writeDouble()等方法来读取和写入原始类型。

transient & static
- 静态字段不是序列化的,因为它属于类而不是要序列化的特定实例。
- 为防止某些字段被序列化,请使用关键字transient标记它们。 这可以减少数据流量


java.io.Serializable & Externalizable Interfaces

当我们创建可能被序列化的类时,该类必须实现java.io.Serializable接口。 Serializable接口没有声明任何方法。 诸如Serializable之类的空接口称为标记接口。 它们将实现类标识为具有某些属性,而不需要这些类来实际实现任何方法。

大多数核心Java类都实现了Serializable,例如所有的包装类,集合类和GUI类。 实际上,唯一没有实现Serializable的核心Java类是不应该被序列化的。 原始数组或可序列化对象的数组本身是可序列化的。


java.io.Externalizable Interface

在序列化中,Java虚拟机完全负责写入和读取对象的过程。 这在大多数情况下是有用的,因为程序员不必关心序列化过程的底层细节。 但是,默认序列化不会保护敏感信息,例如密码和凭据,或者程序员想要在序列化过程中要保护某些信息呢?

因此,Externalizable是为了让程序员在序列化期间对对象的读写进行完全控制。

Serializable有一个Externalizable的子接口,如果要自定义类的序列化方式,可以使用它。 由于Externalizable扩展了Serializable,它也是一个Serializable,可以调用readObject()和writeObject()。

Externalizable声明了两种抽象方法:

void writeExternal(ObjectOutput out) throws IOException

The object implements this method to save its contents by calling the methods of DataOutput for its primitive values or calling the writeObject method of ObjectOutput for objects, strings, and arrays.

void readExternal(ObjectInput in) throws IOException, ClassNotFoundException

The object implements this method to restore its contents by calling the methods of DataInput for primitive types and readObject for objects, strings and arrays.

ObjectOutput和ObjectInput是由ObjectOutputStream和ObjectInputStream实现的接口,它们分别定义了writeObject()和readObject()方法。

当Externalizable的一个实例传递给ObjectOutputStream时,会绕过默认的序列化过程,会用实例的writeExternal()方法。

类似地,当ObjectInputStream读取一个Exteranlizabled实例时,它使用readExternal()来重建该实例。

示例

假设我们有个User类,实现了externalization 接口

public class User implements Externalizable {public static final long serialVersionUID = 1234L;// attributesprivate int code;private String name;private String password;private Date birthday;private int socialSecurityNumber;public User() {}// methods (getters and setters)public int getCode() {return this.code;}public void setCode(int code) {this.code = code;}public String getName() {return this.name;}public void setName(String name) {this.name = name;}public String getPassword() {return this.password;}public void setPassword(String password) {this.password = password;}public void setBirthday(Date birthday) {this.birthday = birthday;}public Date getBirthday() {return this.birthday;}public void setSocialSecurityNumber(int ssn) {this.socialSecurityNumber = ssn;}public int getSocialSecurityNumber() {return this.socialSecurityNumber;}// externalization methods:public void writeExternal(ObjectOutput out) {// implement your own code to write objects of this class}public void readExternal(ObjectInput in) {// implement your own code to read serialized objects of this class}
}

强烈建议所有可序列化的类定义在上面的User类中声明的serialVersionUID常量

public static final long serialVersionUID = 1234L;

这有助于反序列化过程在可序列化类超时更改时保持正确重新构建对象,并避免InvalidClassException。

下面来重写writeExternal() 方法

由于writeExternal()方法使用ObjectOutput,我们可以使用它的方法将对象的状态写入基础流,遵循以下规则:

  • 对于原始类型,使用DataOutput接口的writeXXX()方法,如writeBoolean(),writeByte(),writeInt(),writeLong()等)。

  • 对于对象类型(字符串,数组,自定义类),请使用writeObject()方法

@Overridepublic void writeExternal(ObjectOutput out) throws IOException {// implement your own code to write objects of this classout.writeInt(code);out.writeObject(name);// write empty password:out.writeObject("");out.writeObject(birthday);}

可以看到,我们序列化以下属性: code, name, password and birthday。 为了安全起见,密码设置为“”,并且没有序列化socialSecurityNumber。 这给出了我们如何通过实现Externalizable接口来控制序列化过程的想法。


下面来重写readExternal() 方法

由于readExternal()方法接受一个ObjectInput,我们可以使用它的方法从基础流中读取对象的状态,遵循以下规则:

  • 对于原始类型,使用DataInput接口的readXXX()方法,如readBoolean(),readByte(),readInt(),readLong()等。

  • 对于对象类型(字符串,数组,您的自定义类),请使用readObject()方法。

@Overridepublic void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {// implement your own code to read serialized objects of this classthis.code = in.readInt();this.name = (String) in.readObject();this.password = (String) in.readObject();this.birthday = (Date) in.readObject();}

我们可以看到,我们将以下属性反序列化: code, name, password and birthday。 为了安全起见,socialSecurityNumber没有被反序列化。 这给了我们通过实现Externalizable接口来控制反序列化过程的想法。

输出:

User's details before serialization:
Code: 123
Name: Tom
Birthday: Fri Sep 08 14:02:30 BOT 2017
Password: secret123
socialSecurityNumber: 1234567890
Serialization done=============User's details afeter de-serialization:
Code: 123
Name: Tom
Birthday: Fri Sep 08 14:02:30 BOT 2017
Password:
socialSecurityNumber: 0

代码

代码已托管到Github—> https://github.com/yangshangwei/JavaMaster

Java-Java I/O流解读之Object Serialization and Object Streams相关推荐

  1. java io文件流序列化_Java——Properties集合,Object序列化流与反序列化流,打印流,commons-IO文件工具类...

    一.properties集合 集合对象Properties类,继承Hashtable,实现Map接口,可以和IO对象结合使用,实现数据的持久存储. 特点: Hashtable的子类,map集合中的方法 ...

  2. java中io流实现哪个接口_第55节:Java当中的IO流-时间api(下)-上

    标题图 Java当中的IO流(下)-上日期和时间日期类:java.util.Date 系统时间:long time = System.currentTimeMillis();public class  ...

  3. 【Java】IO Stream详细解读

    什么是IO Java中I/O操作主要是指使用Java进行输入,输出操作. Java所有的I/O机制都是基于数据流进行输入输出,这些数据流表示了字符或者字节数据的流动序列. Java的I/O流提供了读写 ...

  4. Java Review - PriorityQueue源码解读

    文章目录 Pre PriorityQueue 概述 PriorityQueue 继承关系 PriorityQueue通过用数组表示的小顶堆实现 时间复杂度 构造函数 方法 add()和offer() ...

  5. Java Review - LinkedList源码解读

    文章目录 Pre 概述 底层数据结构-双向链表 源码解析 构造函数 方法源码分析 getFirst() getLast() remove相关方法 remove(e) remove(index) rem ...

  6. java byte char io流_吃透Java IO:字节流、字符流、缓冲流

    前言有人曾问fastjson的作者(阿里技术专家高铁):"你开发fastjson,没得到什么好处,反而挨了骂背了锅,这种事情你为什么要做呢?" 高铁答道:"因为热爱本身, ...

  7. Java基础学习-IO流

    package IObasics; import java.io.FileWriter; import java.io.IOException;/*IO流* 通过数据流.序列化和文件系统提供系统输入和 ...

  8. 复习Java字节流_字符流使用及案例

    字节流_字符流 主要内容 IO流 字节流 字符流 异常处理 Properties 第一章 IO概述 1.1 什么是IO 生活中,你肯定经历过这样的场景.当你编辑一个文本文件,忘记了ctrl+s ,可能 ...

  9. Java中的IO流(六)

    上一篇<Java中的IO流(五)>把流中的打印流PrintStream,PrintWriter,序列流SequenceInputStream以及结合之前所记录的知识点完成了文件的切割与文件 ...

最新文章

  1. 间接寻址级别不同_详解西门子间接寻址之地址寄存器间接寻址
  2. ffmpeg添加到环境变量_Windows + MSVC环境编译ffmpeg
  3. 虎记:强大的nth-child(n)伪类选择器玩法
  4. 嘿嘿~~大家一起来测测自己反映速度
  5. drools 7.x KIE API解析
  6. arduino支持python吗_python能给arduino的板子编程吗?stm32支持吗?什么游戏引擎支持python?...
  7. UVA 357 - Let Me Count The Ways
  8. 很多人已经不会用WINDOWS的命令行、DOS命令了
  9. Python学习笔记【第十二篇】:Python异常处理
  10. 素材解析程序源代码,用thinkphp开发的 支持12大网站,其他需要定制
  11. RadASM DosBox设置无法生效问题
  12. AABB包围盒和OBB包围盒区别
  13. 如何设置海思开发板的静态IP
  14. 修改Ubuntu时区
  15. 关于笔记本真机装Linux连接WIFI并设置静态IP
  16. 利用QQ文件中转站给多个好友或群友传送文件
  17. React中组件通信的几种方式
  18. uboot-Makefile学习(4)
  19. 余杭医保卡指定使用医院和药店
  20. Imblearn package study(不平衡数据处理之过采样、下采样、综合采样)

热门文章

  1. jetson nano 安装 onnx
  2. Python time 100 天以后的日期
  3. 清华大学计算机毕业论文,清华大学毕业论文撰写要求
  4. winform point数组带数值_带你学够浪:Go语言基础系列 - 8分钟学复合类型
  5. tableau可视化函数使用案例(六十七)-Tableau饼图及其变种(环形图、南丁格尔玫瑰图、旭日图)
  6. 听说你想去大厂看妹子,带你看看字节跳动实习算法岗面试长啥样?
  7. Python3--baby网的数据爬取
  8. android安卓开发-eclipse平台下错误记录
  9. shell启动sublime
  10. 主流Java数据库连接池分析(C3P0,DBCP,TomcatPool,BoneCP,Druid)