Java序列化熟悉而又陌生

编程学思 2020-03-11 14:43:41 ⋅ 688 阅读

前言

java 的序列化大家肯定并不陌生, 在使用一些开源开源框架比如 dubbo 的时候,肯定踩过实体类没有实现序列化接口(java.io.Serializable)而报错的情况, 那大家有没有想过为什么要序列化实体类?如果实体类引用了一个不能序列化的类该怎么做呢?下面就给大家讲下我所探索的Java序列化以及他的使用场景。



如何序列化

  1. 首先实体类要实现 Serializable 接口

public class Student implements java.io.Serializable {
private String name;
private int age;
// getter setter
...
}
  1. 然后可以使用 ObjectOutStream 序列化到本地文件

// 创建输出流
ObjectOutStream out = new ObjectOutputStream(new FileOutputStream("student.dat"))
// 创建需要序列化的对象
Student jack = new Student("Jack", 21);
Student jim = new Student("Jim", 20);
// 写入流
out.writeObject(jack);
out.writeObject(jim);

如何反序列化

// 读回对象数据
ObjectInputStream in = new ObjectInputStream(new FileInputStream("student.dat"));
// 然后用 readObject方法以这些对象写入的顺序获得他们
Student jack = (Student) in.readObject();
Student jim = (Student) in.readObject();

方法调用顺序

注意:序列化write顺序和反序列化read顺序要一致

例如:

writeInt(a), writeInt(b)

readInt(a), readInt(b)


引用不能序列化的属性该怎么做?

很简单有几种方式

  1. 属性上面使用java 关键字 transient

  2. 方法上面用注解 @Transient

  3. 如果条件允许还可以改为静态属性

因为静态数据不在堆内存中,而是在静态方法区中




完整的例子

我们看下我们很常用的java ArrayList 的源码是怎么完成序列化的


public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
private static final long serialVersionUID = 8683452581122892189L;
/**
* The array buffer into which the elements of the ArrayList are stored.
* The capacity of the ArrayList is the length of this array buffer. Any
* empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
* will be expanded to DEFAULT_CAPACITY when the first element is added.
*/
transient Object[] elementData; // non-private to simplify nested class access

/**
* The size of the ArrayList (the number of elements it contains).
*
* @serial
*/
private int size;

···

/**
* Save the state of the <tt>ArrayList</tt> instance to a stream (that
* is, serialize it).
*
* @serialData The length of the array backing the <tt>ArrayList</tt>
* instance is emitted (int), followed by all of its elements
* (each an <tt>Object</tt>) in the proper order.
*/
private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException{
// Write out element count, and any hidden stuff
int expectedModCount = modCount;
s.defaultWriteObject();

// Write out size as capacity for behavioural compatibility with clone()
s.writeInt(size);

// Write out all elements in the proper order.
for (int i=0; i<size; i++) {
s.writeObject(elementData[i]);
}

if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}

/**
* Reconstitute the <tt>ArrayList</tt> instance from a stream (that is,
* deserialize it).
*/
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
elementData = EMPTY_ELEMENTDATA;

// Read in size, and any hidden stuff
s.defaultReadObject();

// Read in capacity
s.readInt(); // ignored

if (size > 0) {
// be like clone(), allocate array based upon size not capacity
ensureCapacityInternal(size);

Object[] a = elementData;
// Read in all elements in the proper order.
for (int i=0; i<size; i++) {
a[i] = s.readObject();
}
}
}
  1. 他实现了 java.io.Serializable

  2. 他在字段 elementData 数组上面用了 transient 关键字

  3. 他还写了writeObject 和 readObject 方法

你如果用IDE查询的话发现这两个方法没实现任何接口,那是再什么时候调用的呢?经过一番查阅资料发现 java.io.Serializable 的注释里面有写道

Classes that require special handling during the serialization and
deserialization process must implement special methods with these exact
signatures:
<PRE>
private void writeObject(java.io.ObjectOutputStream out)
throws IOException
private void readObject(java.io.ObjectInputStream in)
throws IOException, ClassNotFoundException;
private void readObjectNoData()
throws ObjectStreamException;
</PRE>
···
@author unascribed
@see java.io.ObjectOutputStream
@see java.io.ObjectInputStream
@see java.io.ObjectOutput
@see java.io.ObjectInput
@see java.io.Externalizable
@since JDK1.1

也就是说这两个方法是特殊的回调方法, 当你的实体类很特殊需要手动序列化的时候就可以手动实现这两个方法

然后你可以返回去细品 ArrayList 是把 elementData 数组循环的writeObject 了




手动序列化和反序列化对象

Serializable 接口支持的钩子方法

先看下jdk序列化接口的定义

Classes that require special handling during the serialization and
deserialization process must implement special methods with these exact
signatures:
<PRE>
private void writeObject(java.io.ObjectOutputStream out)
throws IOException
private void readObject(java.io.ObjectInputStream in)
throws IOException, ClassNotFoundException;
private void readObjectNoData()
throws ObjectStreamException;
</PRE>
···
@author unascribed
@see java.io.ObjectOutputStream
@see java.io.ObjectInputStream
@see java.io.ObjectOutput
@see java.io.ObjectInput
@see java.io.Externalizable
@since JDK1.1

就是告诉我们可以编写writeObject 和 readObject 来手动序列化(不受关键字修饰的影响)

序列化的使用场景

  1. 比如常见的Java对象的网络传输

  2. java小游戏开发存放游戏数据的时候比xml更方便存储复杂的关系

他是一种存储方式,只要你需要你就可以去用

总结

  1. 序列化使用 java.io.Serializable 接口

  2. 静态字段不会被序列化

  3. 字段屏蔽用 transient 关键字

  4. 自定义序列化编写 readObject 和 writeObject 方法

  5. 写入顺序和读取顺序要一致,就算不用也需要read一下



全部评论: 0

    我有话说:

    Google 宣布正式开源 Jib ,帮助 Java 应用快速容器

    Google 本周宣布开源一款新的 Java 工具 Jib ,旨在让开发者使用他们熟悉的工具更轻松地将 Java 应用程序容器

    电话号码校验,你确定不会有Bug?

    熟悉陌生的电话号码

    GitHub精选:2018年11月份最热门的Java开源项目

    到了揭晓 11 月份最热门 Java 开源项目排名的时候了,在本月的名单中,出现了几个新面孔,如Java 核心知识库、轻量级容错组件Resilience4j .....

    蚂蚁金服 Java RPC 开源框架—SOFARPC

    SOFARPC 是一个高可扩展性、高性能、生产级的 Java RPC 框架。

    微信开发神器全能微信Java开发工具包

    必须分享的微信神器 weixin-java-tools

    推荐一款功能强大,开源免费的H5可视编辑器

    H5-Dooring 是一款功能强大,开源免费的H5可视页面配置解决方案,致力于提供一套简单方便、专业可靠、无限可能的H5落地页最佳实践。技术栈以react为主, 后台采用nodejs开发. 预览

    JavaWeb实战篇:视图了解多线程Wait、NotifyAll使用案例

    内容简介本节针对于我们常听说不常使用的 wait 和 notify 方法做个生产者和消费者案例效果图多线程......

    精品推荐:一览GitHub上最受程序欢迎的5大Java开源项目

    列举了GitHub上一些最流行的Java项目。从Mockitos到Guava,以及 java-design-patterns等供大家学习。

    2017 年度编程语言榜,Java 最流行、JavaScript 最没价值?

    2017 年度编程语言榜,Java 最流行、JavaScript 最没价值?

    精品推荐:Java核心数据结构(List,Map,Set)使用技巧与优化

    JDK提供了一组主要的数据结构实现,如List、Map、Set等常用数据结构。这些数据都继承自 java.util.Collection 接口,并位于 java.util 包内。

    Java Web实战篇:发布和运维必备的12条Linux命令

    作为一名Java起步的从业人员,学会一些常用的Linux命令是必须的。

    Java Web实战篇-轻松提高千万级数据库查询效率

    通过优化数据库设计、java后台和数据库优化达到提高千万级数据查询的效率。

    Google发布Java 核心工具库——Guava 28.0

    Guava是一种基于开源的Java库,其中包含谷歌正在由他们很多项目使用的很多核心库。这个库是为了方便编码,并减少编码错误。这个库提供用于集合,缓存,支持原语,并发性,常见注解,字符串处理,I/O和

    Java 零注解文档生成工具—smart-doc,看完有替换swagger的冲动

    Tips:喜欢的话可以关注小萌哦~~~今天小萌给大家推荐的一个开源Java Restful API 文档生成

    「开源资讯」Guava 28.2 发布,Google 的 Java 核心工具库

    前言 Guava 28.2 发布了,Guava 是 Google 的一个开源项目,包含许多 Google 核心 Java 常用库,如:集合 [collections] 、缓存 [caching

    RxJava 3.0.11 发布,Rx 的 Java 实现

    RxJava 是一个在 Java VM 上使用可观测的序列来组成异步的、基于事件的程序的库。它扩展了 observer pattern 以支持数据/事件序列,并添加了运算符,使您可以声明性地将序列

    Python 基础系列--序列类型【4】

    目标 1.对序列对象有整体的认识;2.掌握list的常用操作3.掌握tuple的常用操作4.理解深浅拷贝问题 第一部分 序列对象 1. 了解序列对象 Python一切皆对象。 1.序列对象包含str

    「轻阅读」图解 Java 线程生命周期

    Java 线程生命周期中都包含哪些状态?生命周期中各个状态都是什么含义?