Java中的序列化与反序列化
序列化定义
将对象转换为字节流保存起来,并在以后还原这个对象,这种机制叫做对象序列化。
将一个对象保存到永久存储设备上称为持久化。
一个对象要想能够实现序列化,必须实现java.io.Serializable接口。该接口中没有定义任何方法,是一个标识性接口(Marker Interface),当一个类实现了该接口,就表示这个类的对象是可以序列化的。
序列化(serialization)是把一个对象的状态写入一个字节流的过程。当你想要把你的程序状态存到一个固定的存储区域,例如文件时,它是很管用的。
从程序到外面叫序列化,从外面读回来叫反序列化。
序列化特点
假设一个被序列化的对象引用了其他对象,同样,其他对象又引用了更多的对象。这一系列的对象和它们的关系形成了一个顺序图表。
对象序列化和反序列化工具被设计出来并在这一假定条件下运行良好。
如果你试图序列化一个对象图表中顶层的对象,所有其他的引用对象都被循环地定位和序列化;同样,在反序列化过程中,所有的这些对象以及它们的引用都被正确地恢复。
序列化实现细节
只有实现Serializable接口的对象可以被序列化工具存储和恢复。
Serializable接口没有定义任何成员,它只用来表示一个类可以被序列化。
如果一个类可以被序列化,它的所有子类都可以序列化。
当一个对象被序列化时,只保存对象的非静态成员变量,不能保存任何的成员方法和静态的成员变量。
如果一个对象的成员变量是一个对象,那么这个对象的数据成员也会被保存。
如果一个可序列化的对象包含对某个不可序列化对象的引用,那么整个序列化操作将会失败,并且会抛出一个NotSerializableException。
我们可以将这个不可序列化的引用标记为transient,那么原对象仍然可以序列化。使用transient修饰的变量将不会被序列化。
反序列化时不会调用对象的任何构造方法,仅仅根据所保存的对象状态信息,在内存中重新构建对象。
ObjectOutput接口
ObjectOutput接口继承DataOutput接口并且支持对象序列化。
特别注意writeObject(Object obj)方法,它被称为序列化一个对象。
所有这些方法在出错情况下引发IOException异常。
ObjectOutputStream类
对象的输出流。ObjectOutputStream类继承OutputStream类和实现ObjectOutput接口。它负责向流写入对象。
该类的构造函数ObjectOutputStream(OutputStream out),参数是序列化对象将要写入的输出流。
根据它的构造函数可以判断它是一个包装类,是一个过滤流。
ObjectInput接口
ObjectInput接口继承DataInput接口。它支持对象序列化。
特别注意readObject()方法,它叫反序列化一个对象。
所有这些方法在出错情况下引发IOException异常。
ObjectInputStream类
ObjectInputStream类继承InputStream类,实现ObjectInput接口。它负责从流中读取对象。
该类的构造函数ObjectInputStream(InputStream in),参数是序列化对象将被读取的输入流。
ObjectInputStream类是一个过滤流,它包装了节点流。
序列化的程序例子SerializableTest1:这个例子定义了一个类叫Person,程序实现了Person类对象的序列化(写出)和反序列化(读入)。
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable; public class SerializableTest1
{
public static void main(String[] args) throws Exception
{
Person p1 = new Person("ZhangSan", 29, 1.78);
Person p2 = new Person("LiSi", 50, 1.7);
Person p3 = new Person("WangWu", 46, 1.67); FileOutputStream fos = new FileOutputStream("Person.txt");// 当前项目路径下的一个文本文档
ObjectOutputStream oos = new ObjectOutputStream(fos); // 用序列化的方式将对象存储在文件中
oos.writeObject(p1);
oos.writeObject(p2);
oos.writeObject(p3); oos.close(); // 反序列化,将对象信息读出
FileInputStream fis = new FileInputStream("Person.txt");
ObjectInputStream ois = new ObjectInputStream(fis); Person person = null; for (int i = 0; i < 3; ++i)
{
person = (Person) ois.readObject(); System.out.println(person.name + ", " + person.age + ", "
+ person.height);
} ois.close(); } } class Person implements Serializable
{
// 这个类需要实现Serializable接口
// 否则在序列化时会抛出java.io.NotSerializableException
transient String name;
transient int age;
double height; // 加上transient关键字后将不被序列化 public Person(String name, int age, double height)
{
this.name = name;
this.age = age;
this.height = height;
}
}
在程序中尝试可知,如果加上transient关键字,变量将不被序列化,反序列化时读取不到,输出默认值,如整形和double型变量输出为默认值0,字符串将输出为null。
一个序列化中的特殊情况
在序列化和反序列化中需要特殊处理的Serializable类应该实现以下方法:
private void writeObject (java.io.ObjectOutputStream stream) throws IOException
private void readObject (java.io.ObjectInputStream stream) throws IOException, ClassNotFoundException;
这两个方法不属于任何一个类或任何一个接口,是非常特殊的方法。
只要我们提供了这两个方法,在序列化反序列化的时候它们会得到自动调用。
如根据第一个程序改编的程序2,注意只是将Person类改名为Person2,然后加入上面两个特殊的函数。注意这时候去掉了所有的transient关键字。

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable; public class SerializableTest2
{
public static void main(String[] args) throws Exception
{
Person2 p1 = new Person2("ZhangSan", 29, 1.78);
Person2 p2 = new Person2("LiSi", 50, 1.7);
Person2 p3 = new Person2("WangWu", 46, 1.67); FileOutputStream fos = new FileOutputStream("Person2.txt");// 当前项目路径下的一个文本文档
ObjectOutputStream oos = new ObjectOutputStream(fos); // 用序列化的方式将对象存储在文件中
oos.writeObject(p1);
oos.writeObject(p2);
oos.writeObject(p3); oos.close(); // 反序列化,将对象信息读出
FileInputStream fis = new FileInputStream("Person2.txt");
ObjectInputStream ois = new ObjectInputStream(fis); Person2 person = null; for (int i = 0; i < 3; ++i)
{
person = (Person2) ois.readObject(); System.out.println(person.name + ", " + person.age + ", "
+ person.height);
} ois.close(); } } class Person2 implements Serializable
{
// 这个类需要实现Serializable接口
// 否则在序列化时会抛出java.io.NotSerializableException
String name;
int age;
double height; // 加上transient关键字后将不被序列化 public Person2(String name, int age, double height)
{
this.name = name;
this.age = age;
this.height = height;
} private void writeObject(java.io.ObjectOutputStream out) throws IOException
{
System.out.println("writeObject Method");
} private void readObject(java.io.ObjectInputStream in) throws IOException,
ClassNotFoundException
{
System.out.println("readObject Method");
} }

程序输出如下:
writeObject Method
writeObject Method
writeObject Method
readObject Method
null, 0, 0.0
readObject Method
null, 0, 0.0
readObject Method
null, 0, 0.0
可以看到这两个方法是自动被调用的,但要注意的是反序列化后得到的值全是默认值。
虽然所有的变量都不是transient关键字修饰,但是程序在序列化时只处理了变量名字,并没有将变量值序列化。
当提供了这两个方法后,序列化和反序列化就完全由我们自己控制。
如果我们在方法中不写入相关代码,则反序列化后并不能读入什么值,所有变量都是默认值。加入相关变量的序列化和反序列化代码后才能真的执行操作。
我们在一个待序列化/反序列化的类中实现了以上两个private方法(方法声明要与上面的完全保持一致,具体在Serializable接口的文档中也有解释),那么就允许我们以更加底层、更加细粒度的方式控制序列化/反序列化的过程。
如下代码,在前面代码的基础上加入了其中两个变量的序列化和反序列化代码。

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable; public class SerializableTest2
{
public static void main(String[] args) throws Exception
{
Person2 p1 = new Person2("ZhangSan", 29, 1.78);
Person2 p2 = new Person2("LiSi", 50, 1.7);
Person2 p3 = new Person2("WangWu", 46, 1.67); FileOutputStream fos = new FileOutputStream("Person2.txt");// 当前项目路径下的一个文本文档
ObjectOutputStream oos = new ObjectOutputStream(fos); // 用序列化的方式将对象存储在文件中
oos.writeObject(p1);
oos.writeObject(p2);
oos.writeObject(p3); oos.close(); // 反序列化,将对象信息读出
FileInputStream fis = new FileInputStream("Person2.txt");
ObjectInputStream ois = new ObjectInputStream(fis); Person2 person = null; for (int i = 0; i < 3; ++i)
{
person = (Person2) ois.readObject(); System.out.println(person.name + ", " + person.age + ", "
+ person.height);
} ois.close(); } } class Person2 implements Serializable
{
// 这个类需要实现Serializable接口
// 否则在序列化时会抛出java.io.NotSerializableException
String name;
int age;
double height; // 加上transient关键字后将不被序列化 public Person2(String name, int age, double height)
{
this.name = name;
this.age = age;
this.height = height;
} private void writeObject(java.io.ObjectOutputStream out) throws IOException
{
out.writeUTF(name);
out.writeInt(age);
System.out.println("writeObject Method");
} private void readObject(java.io.ObjectInputStream in) throws IOException,
ClassNotFoundException
{
name = in.readUTF();
age = in.readInt();
System.out.println("readObject Method");
} }

参考资料
圣思园张龙老师Java SE系列视频。
Java中的序列化与反序列化的更多相关文章
- java中的序列化与反序列化,还包括将多个对象序列化到一个文件中
package Serialize; /** * Created by hu on 2015/11/7. */ //实现序列化必须实现的接口,这就是一个空接口,起到标识的作用 import java. ...
- K:java中的序列化与反序列化
Java序列化与反序列化是什么?为什么需要序列化与反序列化?如何实现Java序列化与反序列化?以下内容将围绕这些问题进行展开讨论. Java序列化与反序列化 简单来说Java序列化是指把Java对象转 ...
- 在Java中进行序列化和反序列化
对象序列化的目标是将对象保存在磁盘中,或者允许在网络中直接传输对象. 对象序列化允许把内存中的Java对象转换成平台无关的二进制流,从而允许把这种二进制流持久保存在磁盘上或者通过网络将这种二进制流传输 ...
- java中的序列化和反序列化学习笔记
须要序列化的Person类: package cn.itcast_07; import java.io.Serializable; /* * NotSerializableException:未序列化 ...
- java中的序列化和反序列化
package cn.zhou; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.Fil ...
- 【Java基础】序列化与反序列化深入分析
一.前言 复习Java基础知识点的序列化与反序列化过程,整理了如下学习笔记. 二.为什么需要序列化与反序列化 程序运行时,只要需要,对象可以一直存在,并且我们可以随时访问对象的一些状态信息,如果程序终 ...
- Java对象的序列化与反序列化
序列化与反序列化 序列化 (Serialization)是将对象的状态信息转换为可以存储或传输的形式的过程.一般将一个对象存储至一个储存媒介,例如档案或是记亿体缓冲等.在网络传输过程中,可以是字节或是 ...
- Java对象的序列化和反序列化[转]
Java基础学习总结--Java对象的序列化和反序列化 一.序列化和反序列化的概念 把对象转换为字节序列的过程称为对象的序列化.把字节序列恢复为对象的过程称为对象的反序列化. 对象的序列化主要有两种用 ...
- JAVA基础之——序列化和反序列化
1 概念 序列化,将java对象转换成字节序列的过程. 反序列化,将字节序列恢复成java对象的过程. 2 为什么要序列化? 2.1 实现数据持久化,当对象创建后,它就会一直在,但是在程序终止时,这个 ...
随机推荐
- 基于嵌入式操作系统VxWorks的多任务并发程序设计(2) ――任务控制
4 任务与任务状态 VxWorks实时内核Wind提供了基本的多任务环境.对用户而言,宏观上看起来,多个任务同时在执行.而本质而言,在微观上,系统内核中的任务调度器总是在根据特定的调度策略让它们交替运 ...
- R语言︱常用统计方法包+机器学习包(名称、简介)
一.一些函数包大汇总 转载于:http://www.dataguru.cn/thread-116761-1-1.html 时间上有点过期,下面的资料供大家参考基本的R包已经实现了传统多元统计的很多功能 ...
- Vue指令总结---小白同学必看
今天主要复习一下我们最熟悉vue指令,想要代码撸得快,就要多看书,多看看官方的文档和学习指令,学习编程是一个非常享受的过程,尤其是你不断地去解决问题,获得一项技能,实现薪水的上涨.进行Vue的指令烹饪 ...
- var dataObj=eval("("+data+")");//转换为json对象(解决在ajax返回json格式数据的时候明明正确的获取了返回值但是却就是进不去success方法的问题。格式错误)
一,原理 1.1,解析1 将字符串解析为JavaScript代码,比如:var a = "alert('a');";这里的a就只是一个字符串而已,输出的话也是alert(a);这句 ...
- AI CV 会议2018
(1) NIPS, Conference and Workshop on Neural Information Processing Systems, A类顶会 1987年由联结主义学派创建,每年一 ...
- 实例 centos自动挂载、备份windows共享文件夹,并删除第7日前当天的备份
此为用户twk的备份执行方案(192.168.42.246虚拟机数据库,备份到192.168.42.147的第二硬盘)此为用户twk的备份执行方案(192.168.42.5虚拟机数据库,备份到192. ...
- SPOJ1825:Free tour II
题意 luogu的翻译 给定一棵n个点的树,树上有m个黑点,求出一条路径,使得这条路径经过的黑点数小于等于k,且路径长度最大 Sol 点分治辣 如果是等于\(k\)的话,开个桶取\(max\)就好了 ...
- iOS开发——下载器的功能基本实现
今天,做了一个下载器的Demo,即从本地配置的Apache服务器上,下载指定的文件.这次,我们下载服务器根目录下的html.mp4文件. 按照惯例,我们先创建一个URL对象和请求. NSURL *ur ...
- 关于 CI框架访问数据库类提示Call to undefined function mysqli_init() 问题解决
我上次实践发现,安装在Win10 WampServer3.0.4集成环境,不仅打不开phpmyadmin会报错就算了,而且报错后又没提示那么解决,同时你打开php扩展配置发现,WampServer系统 ...
- webuploader配置
做图片上传的时候用webuploader是个不错的选择,他可以通过简单的配置实现图片的上传预览和处理. <!--引入CSS--> <link rel="stylesheet ...