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

时间:2023-01-13 16:37:30

序列化与反序列化

序列化 (Serialization)是将对象的状态信息转换为可以存储或传输的形式的过程。一般将一个对象存储至一个储存媒介,例如档案或是记亿体缓冲等。在网络传输过程中,可以是字节或是XML等格式。而字节的或XML编码格式可以还原完全相等的对象。这个相反的过程又称为反序列化。

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

在Java中,我们可以通过多种方式来创建对象,并且只要对象没有被回收我们都可以复用该对象。但是,我们创建出来的这些Java对象都是存在于JVM的堆内存中的。只有JVM处于运行状态的时候,这些对象才可能存在。一旦JVM停止运行,这些对象的状态也就随之而丢失了。

但是在真实的应用场景中,我们需要将这些对象持久化下来,并且能够在需要的时候把对象重新读取出来。Java的对象序列化可以帮助我们实现该功能。

对象序列化机制(object serialization)是Java语言内建的一种对象持久化方式,通过对象序列化,可以把对象的状态保存为字节数组,并且可以在有需要的时候将这个字节数组通过反序列化的方式再转换成对象。对象序列化可以很容易的在JVM中的活动对象和字节数组(流)之间进行转换。

在Java中,对象的序列化与反序列化被广泛应用到RMI(远程方法调用)及网络传输中。

相关接口及类

Java为了方便开发人员将Java对象进行序列化及反序列化提供了一套方便的API来支持。其中包括以下接口和类:

java.io.Serializable

java.io.Externalizable

ObjectOutput

ObjectInput

ObjectOutputStream

ObjectInputStream

Serializable 接口

类通过实现 java.io.Serializable 接口以启用其序列化功能。未实现此接口的类将无法使其任何状态序列化或反序列化。可序列化类的所有子类型本身都是可序列化的。序列化接口没有方法或字段,仅用于标识可序列化的语义。 (该接口并没有方法和字段,为什么只有实现了该接口的类的对象才能被序列化呢?)

当试图对一个对象进行序列化的时候,如果遇到不支持 Serializable 接口的对象。在此情况下,将抛出NotSerializableException。

如果要序列化的类有父类,要想同时将在父类中定义过的变量持久化下来,那么父类也应该集成java.io.Serializable接口。

下面是一个实现了java.io.Serializable接口的类

package com.hollischaung.serialization.SerializableDemos;

import java.io.Serializable;

/**

* Created by hollis on 16/2/17.

* 实现Serializable接口

*/

public class User1 implements Serializable {

private String name;

private int age;

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

public int getAge() {

return age;

}

public void setAge(int age) {

this.age = age;

}

@Override

public String toString() {

return "User{" +

"name='" + name + '\'' +

", age=" + age +

'}';

}

}

通过下面的代码进行序列化及反序列化

package com.hollischaung.serialization.SerializableDemos;

import org.apache.commons.io.FileUtils;

import org.apache.commons.io.IOUtils;

import java.io.*;

/**

* Created by hollis on 16/2/17.

* SerializableDemo1 结合SerializableDemo2说明 一个类要想被序列化必须实现Serializable接口

*/

public class SerializableDemo1 {

public static void main(String[] args) {

//Initializes The Object

User1 user = new User1();

user.setName("hollis");

user.setAge(23);

System.out.println(user);

//Write Obj to File

ObjectOutputStream oos = null;

try {

oos = new ObjectOutputStream(new FileOutputStream("tempFile"));

oos.writeObject(user);

} catch (IOException e) {

e.printStackTrace();

} finally {

IOUtils.closeQuietly(oos);

}

//Read Obj from File

File file = new File("tempFile");

ObjectInputStream ois = null;

try {

ois = new ObjectInputStream(new FileInputStream(file));

User1 newUser = (User1) ois.readObject();

System.out.println(newUser);

} catch (IOException e) {

e.printStackTrace();

} catch (ClassNotFoundException e) {

e.printStackTrace();

} finally {

IOUtils.closeQuietly(ois);

try {

FileUtils.forceDelete(file);

} catch (IOException e) {

e.printStackTrace();

}

}

}

}

//OutPut:

//User{name='hollis', age=23}

//User{name='hollis', age=23}

更多关于Serializable的使用,请参考代码实例

Externalizable接口

除了Serializable 之外,java中还提供了另一个序列化接口Externalizable

为了了解Externalizable接口和Serializable接口的区别,先来看代码,我们把上面的代码改成使用Externalizable的形式。

package com.hollischaung.serialization.ExternalizableDemos;

import java.io.Externalizable;

import java.io.IOException;

import java.io.ObjectInput;

import java.io.ObjectOutput;

/**

* Created by hollis on 16/2/17.

* 实现Externalizable接口

*/

public class User1 implements Externalizable {

private String name;

private int age;

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

public int getAge() {

return age;

}

public void setAge(int age) {

this.age = age;

}

public void writeExternal(ObjectOutput out) throws IOException {

}

public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {

}

@Override

public String toString() {

return "User{" +

"name='" + name + '\'' +

", age=" + age +

'}';

}

}

package com.hollischaung.serialization.ExternalizableDemos;

import java.io.*;

/**

* Created by hollis on 16/2/17.

*/

public class ExternalizableDemo1 {

//为了便于理解和节省篇幅,忽略关闭流操作及删除文件操作。真正编码时千万不要忘记

//IOException直接抛出

public static void main(String[] args) throws IOException, ClassNotFoundException {

//Write Obj to file

ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("tempFile"));

User1 user = new User1();

user.setName("hollis");

user.setAge(23);

oos.writeObject(user);

//Read Obj from file

File file = new File("tempFile");

ObjectInputStream ois =  new ObjectInputStream(new FileInputStream(file));

User1 newInstance = (User1) ois.readObject();

//output

System.out.println(newInstance);

}

}

//OutPut:

//User{name='null', age=0}

通过上面的实例可以发现,对User1类进行序列化及反序列化之后得到的对象的所有属性的值都变成了默认值。也就是说,之前的那个对象的状态并没有被持久化下来。这就是Externalizable接口和Serializable接口的区别:

Externalizable继承了Serializable,该接口中定义了两个抽象方法:writeExternal()与readExternal()。当使用Externalizable接口来进行序列化与反序列化的时候需要开发人员重写writeExternal()与readExternal()方法。由于上面的代码中,并没有在这两个方法中定义序列化实现细节,所以输出的内容为空。还有一点值得注意:在使用Externalizable进行序列化的时候,在读取对象时,会调用被序列化类的无参构造器去创建一个新的对象,然后再将被保存对象的字段的值分别填充到新对象中。所以,实现Externalizable接口的类必须要提供一个public的无参的构造器。

按照要求修改之后代码如下:

package com.hollischaung.serialization.ExternalizableDemos;

import java.io.Externalizable;

import java.io.IOException;

import java.io.ObjectInput;

import java.io.ObjectOutput;

/**

* Created by hollis on 16/2/17.

* 实现Externalizable接口,并实现writeExternal和readExternal方法

*/

public class User2 implements Externalizable {

private String name;

private int age;

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

public int getAge() {

return age;

}

public void setAge(int age) {

this.age = age;

}

public void writeExternal(ObjectOutput out) throws IOException {

out.writeObject(name);

out.writeInt(age);

}

public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {

name = (String) in.readObject();

age = in.readInt();

}

@Override

public String toString() {

return "User{" +

"name='" + name + '\'' +

", age=" + age +

'}';

}

}

package com.hollischaung.serialization.ExternalizableDemos;

import java.io.*;

/**

* Created by hollis on 16/2/17.

*/

public class ExternalizableDemo2 {

//为了便于理解和节省篇幅,忽略关闭流操作及删除文件操作。真正编码时千万不要忘记

//IOException直接抛出

public static void main(String[] args) throws IOException, ClassNotFoundException {

//Write Obj to file

ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("tempFile"));

User2 user = new User2();

user.setName("hollis");

user.setAge(23);

oos.writeObject(user);

//Read Obj from file

File file = new File("tempFile");

ObjectInputStream ois =  new ObjectInputStream(new FileInputStream(file));

User2 newInstance = (User2) ois.readObject();

//output

System.out.println(newInstance);

}

}

//OutPut:

//User{name='hollis', age=23}

这次,就可以把之前的对象状态持久化下来了。

如果User类中没有无参数的构造函数,在运行时会抛出异常:java.io.InvalidClassException

更多Externalizable接口使用实例请参考代码实例

ObjectOutput和ObjectInput 接口

ObjectInput接口 扩展自 DataInput 接口以包含对象的读操作。

DataInput 接口用于从二进制流中读取字节,并根据所有 Java 基本类型数据进行重构。同时还提供根据 UTF-8 修改版格式的数据重构 String 的工具。

对于此接口中的所有数据读取例程来说,如果在读取所需字节数之前已经到达文件末尾 (end of file),则将抛出 EOFException(IOException 的一种)。如果因为到达文件末尾以外的其他原因无法读取字节,则将抛出 IOException 而不是 EOFException。尤其是,在输入流已关闭的情况下,将抛出 IOException。

ObjectOutput 扩展 DataOutput 接口以包含对象的写入操作。

DataOutput 接口用于将数据从任意 Java 基本类型转换为一系列字节,并将这些字节写入二进制流。同时还提供了一个将 String 转换成 UTF-8 修改版格式并写入所得到的系列字节的工具。

对于此接口中写入字节的所有方法,如果由于某种原因无法写入某个字节,则抛出 IOException。

ObjectOutputStream类和ObjectInputStream类

通过前面的代码片段中我们也能知道,我们一般使用ObjectOutputStream的writeObject方法把一个对象进行持久化。再使用ObjectInputStream的readObject从持久化存储中把对象读取出来。

更多关于ObjectInputStream和ObjectOutputStream的相关知识欢迎阅读我的另外两篇博文:深入分析Java的序列化与反序列化、单例与序列化的那些事儿

Transient 关键字

Transient 关键字的作用是控制变量的序列化,在变量声明前加上该关键字,可以阻止该变量被序列化到文件中,在被反序列化后,transient 变量的值被设为初始值,如 int 型的是 0,对象型的是 null。关于Transient 关键字的拓展知识欢迎阅读深入分析Java的序列化与反序列化

序列化ID

虚拟机是否允许反序列化,不仅取决于类路径和功能代码是否一致,一个非常重要的一点是两个类的序列化 ID 是否一致(就是 private static final long serialVersionUID)

序列化 ID 在 Eclipse 下提供了两种生成策略,一个是固定的 1L,一个是随机生成一个不重复的 long 类型数据(实际上是使用 JDK 工具生成),在这里有一个建议,如果没有特殊需求,就是用默认的 1L 就可以,这样可以确保代码一致时反序列化成功。那么随机生成的序列化 ID 有什么作用呢,有些时候,通过改变序列化 ID 可以用来限制某些用户的使用。

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

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

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

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

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

  3. 一文带你全面了解java对象的序列化和反序列化

    摘要:这篇文章主要给大家介绍了关于java中对象的序列化与反序列化的相关内容,文中通过详细示例代码介绍,希望能对大家有所帮助. 本文分享自华为云社区<java中什么是序列化和反序列化?>, ...

  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对象转换为字节序列,才能在网络上传送:接收方则需要把字节序列再 ...

随机推荐

  1. 【iCore3 双核心板&lowbar;FPGA】实验二十五:NIOS II之UART串口通信实验

    实验指导书及代码包下载: http://pan.baidu.com/s/1eRMZq18 iCore3 购买链接: https://item.taobao.com/item.htm?id=524229 ...

  2. 甲乙(数理逻辑)转自http&colon;&sol;&sol;www&period;cnblogs&period;com&sol;devymex&sol;p&sol;3329635&period;html

    这是一道历史悠久,又很困难的面试题. 你在旁观主持人和甲.乙两个天才数学家玩猜数字游戏.主持人准备了两个数,告知甲乙:这两个数不同,且大于等于1,小于等于30.然后主持人将两数之积告诉甲,把两数之和告 ...

  3. Oracle 查询库中所有表名、字段名、字段名说明,查询表的数据条数、表名、中文表名、

    查询所有表名:select t.table_name from user_tables t;查询所有字段名:select t.column_name from user_col_comments t; ...

  4. 神奇的Noip模拟试题 T3 科技节 位运算

    3 科技节 (scifest.pas/.c/.cpp) [问题描述] 一年一度的科技节即将到来.同学们报名各项活动的名单交到了方克顺校长那,结果校长一看皱了眉头:这帮学生热情竟然如此高涨,每个人都报那 ...

  5. MongoDB - The mongo Shell&comma; Data Types in the mongo Shell

    MongoDB BSON provides support for additional data types than JSON. Drivers provide native support fo ...

  6. Chrome 实用调试技巧

    Chrome 实用调试技巧 2016-07-23 如今Chrome浏览器无疑是最受前端青睐的工具,原因除了界面简洁.大量的应用插件,良好的代码规范支持.强大的V8解释器之外,还因为Chrome开发者工 ...

  7. 【C&num;】Smtp发送邮件

    class SmtpEmail { SmtpClient smtpclient; MailMessage msg; Attachment attachment; public void sendMai ...

  8. pro asp&period;net mvc5

    mvc 架构的每一个部分都是定义良好和自包含的,称为关注分离.域模型和控制器逻辑与UI是松耦合的.模型中操作数据的逻辑仅包含在模型中,显示数据的逻辑仅包含在视图中,而处理用户请求和用户输入的代码仅包含 ...

  9. 如何在 Windows上编译Objective-C

    Objective-C现在几乎已经变成了苹果的专利了,可以直接在苹果的Xcode上编译Objective-C程序,但是在Windows平台下的编译工具就寥寥无几了,本身这种语言用的人就不是很多.今天在 ...

  10. Caused by&colon; java&period;io&period;FileNotFoundException

    1.错误描述 usage: java org.apache.catalina.startup.Catalina [ -config {pathname} ] [ -nonaming ] { -help ...