序列化定义

  将对象转换为字节流保存起来,并在以后还原这个对象,这种机制叫做对象序列化

  将一个对象保存到永久存储设备上称为持久化

  一个对象要想能够实现序列化,必须实现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类对象的序列化(写出)和反序列化(读入)。 

SerializableTest1

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关键字。 

SerializableTest2

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接口的文档中也有解释),那么就允许我们以更加底层、更加细粒度的方式控制序列化/反序列化的过程。

  如下代码,在前面代码的基础上加入了其中两个变量的序列化和反序列化代码。

SerializableTest2 改进版

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中的序列化与反序列化的更多相关文章

  1. java中的序列化与反序列化,还包括将多个对象序列化到一个文件中

    package Serialize; /** * Created by hu on 2015/11/7. */ //实现序列化必须实现的接口,这就是一个空接口,起到标识的作用 import java. ...

  2. K:java中的序列化与反序列化

    Java序列化与反序列化是什么?为什么需要序列化与反序列化?如何实现Java序列化与反序列化?以下内容将围绕这些问题进行展开讨论. Java序列化与反序列化 简单来说Java序列化是指把Java对象转 ...

  3. 在Java中进行序列化和反序列化

    对象序列化的目标是将对象保存在磁盘中,或者允许在网络中直接传输对象. 对象序列化允许把内存中的Java对象转换成平台无关的二进制流,从而允许把这种二进制流持久保存在磁盘上或者通过网络将这种二进制流传输 ...

  4. java中的序列化和反序列化学习笔记

    须要序列化的Person类: package cn.itcast_07; import java.io.Serializable; /* * NotSerializableException:未序列化 ...

  5. java中的序列化和反序列化

    package cn.zhou; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.Fil ...

  6. 【Java基础】序列化与反序列化深入分析

    一.前言 复习Java基础知识点的序列化与反序列化过程,整理了如下学习笔记. 二.为什么需要序列化与反序列化 程序运行时,只要需要,对象可以一直存在,并且我们可以随时访问对象的一些状态信息,如果程序终 ...

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

    序列化与反序列化 序列化 (Serialization)是将对象的状态信息转换为可以存储或传输的形式的过程.一般将一个对象存储至一个储存媒介,例如档案或是记亿体缓冲等.在网络传输过程中,可以是字节或是 ...

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

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

  9. JAVA基础之——序列化和反序列化

    1 概念 序列化,将java对象转换成字节序列的过程. 反序列化,将字节序列恢复成java对象的过程. 2 为什么要序列化? 2.1 实现数据持久化,当对象创建后,它就会一直在,但是在程序终止时,这个 ...

随机推荐

  1. VxWorks下USB驱动总结1

    1.USB设备 物理特征:4条电缆,电源线.地线.数据线.脉冲线; 速 度:低速1.5Mbps,全速12Mbps,高速480Mbps; 规范版本:1998年USB1.1,2000年USB2.0; 连 ...

  2. 一种在BIOS中嵌入应用程序的方法及实现

    本文针对Award公司开发的计算机系统BIOS提出了一种嵌入应用程序的方法,其基本原理对别的品牌的BIOS也一样适用,仅需稍加修改.文中作者给出并讨论一个完整的例子程序,该程序已经通过实验验证.  正 ...

  3. OpenStack_I版 5.Nova部署

    Nova安装 创建配置存放目录,日志存放目录,执行文件目录,虚拟机目录  Nova配置修改 生成主配置文件 创建Nova数据库 同步Nova数据库 验证 Nova连接RabbitMQ配置修改  key ...

  4. @SuppressWarnings("unchecked")(解决标准的后台HttpServletRequest request, HttpServletResponse response)格式

    在springmvc的应用中有些限制会出现必须是 public void save(HttpServletRequest request, HttpServletResponse response) ...

  5. 第十篇:K均值聚类(KMeans)

    前言 本文讲解如何使用R语言进行 KMeans 均值聚类分析,并以一个关于人口出生率死亡率的实例演示具体分析步骤. 聚类分析总体流程 1. 载入并了解数据集:2. 调用聚类函数进行聚类:3. 查看聚类 ...

  6. 通过smtp直接发送邮件

    /// <param name="fromEmail">发件人的邮箱</param> /// <param name="toEmail&qu ...

  7. UVA10294 Arif in Dhaka (群论,Polya定理)

    UVA10294 Arif in Dhaka (群论,Polya定理) 题意 : 给你一个长为\(n\)的项链和手镯,每个珠子有\(m\)种颜色. 两个手镯定义为相同,即它们通过翻转和旋转得到一样的手 ...

  8. 【NOIP2004】【CJOJ1703】【洛谷1092】虫食算

    题面 题目描述 所谓虫食算,就是原先的算式中有一部分被虫子啃掉了,需要我们根据剩下的数字来判定被啃掉的字母.来看一个简单的例子: 43#9865#045 +8468#6633 44445509678 ...

  9. Luogu2045 方格取数加强版

    题目描述 给出一个n*n的矩阵,每一格有一个非负整数Aij,(Aij <= 1000)现在从(1,1)出发,可以往右或者往下走,最后到达(n,n),每达到一格,把该格子的数取出来,该格子的数就变 ...

  10. js表单验证处理和childNodes 和children 的区别

    一.对提交表单进行空值验证 html代码: <form action="#"onsubmit="return validate_form(this);" ...