Java序列化和反序列化

1. Java序列化和反序列化(What)

Java序列化(Serialize)是指将一个Java对象写入IO流中;
Java反序列化(Deserialize)指的是从IO流中回复IO对象。

2. 序列化的意义(Why)

序列化机制可以将Java对象转换为数据流用来保存在磁盘上或者通过网络传输。这使得对象可以脱离程序独立存在。

3. 如何进行序列化(How)

为了使对象支持序列化机制,需要让它的类变成可序列化的(serializable)。通过实现两个接口之一实现:

  • Serializable
  • Externalnalizable
3.1 序列化的步骤

实现了Serializable接口的类,可以通过两个步骤序列化该对象:

  1. 创建建立在其他节点流上的ObjectOutputStream
//创建ObjectOutputStream输出流
ObjectOutputStream oos = new ObejctOutputStream(new FileOutputStream("object.txt"));
  1. 调用ObjectOutputStream对象的writeObject()方法输出可序列化对象
//将一个Person对象输出到输出流中
oos.writeObject(per);
3.2 序列化的代码
  1. 定义一个Person类,实现了Serializable接口,标识该类的对象是可序列化的。
public class Person implements java.io.Serializable {
    private String name;
    private int age;

    // 这里没有无参构造器
    public Person(String name, int age){
        this.name = name;
        this.age = age;
    }

    // name和age的setter和getter方法
    ...
}
  1. 将Person对象写入硬盘
import java.io.*;
public class Test{
    public static void main(String[] args){
        try{
            // 创建ObjectOutputStream输出流
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("Object.txt"));
            Person per = new Person("Junzerg", 20);
            // 将per对象写入输出流
            oos.writeObject(per);
        }
        catch (IOException ex){
            ex.printStackTrace();
        }
    }   
}
3.3 序列化结果

AC ED 00 05 73 72 00 06 50 65 72 73 6F 6E 2A 98
15 B9 5C 2E C1 6C 02 00 02 49 00 03 61 67 65 4C
00 04 6E 61 6D 65 74 00 12 4C 6A 61 76 61 2F 6C
61 6E 67 2F 53 74 72 69 6E 67 3B 78 70 00 00 00

14 74 00 07 4A 75 6E 7A 65 72 67

共75字节。

3.4 序列化的字节数

把Person类的age属性设置为Long,重新序列化,结果为:

AC ED 00 05 73 72 00 06 50 65 72 73 6F 6E BE CF
78 98 E0 A3 0B E9 02 00 02 4A 00 03 61 67 65 4C
00 04 6E 61 6D 65 74 00 12 4C 6A 61 76 61 2F 6C
61 6E 67 2F 53 74 72 69 6E 67 3B 78 70 00 00 00
00 00 00 00 14 74 00 07 4A 75 6E 7A 65 72 67

共79字节。

4. 反序列化

4.1 反序列化的步骤
  1. 创建一个建立在其他节点流基础上的ObjectInputStream输入流
// 出啊构建一个ObjectInputStream输入流
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("Object.txt"));
  1. 调用ObjectInputStream对象的readObject方法读取流中的对象,该方法返回一个Java对象,再强制转换为真实类型。
//从输入流中读取一个Java对象并将其强制类型转换成Person类
Person p = (Person) ois.readObject();
4.2 反序列化的代码

从前文创建的Object.txt中读取Person类

import java.io.*;
public class ReadObject{
    public static void main(String[] args){
        try{
            // 创建一个ObjectInputStream输入流
            ObjectInputStream ois = 
                new ObjectInputStream(new FileInputStream("Object.txt"));
            // 从输入流中读取一个Java对象,转换为Person类
                Person p = (Person)ois.readObject();
                System.out.println("name: " + p.getName()
                    + "\n age: " + p.getAge());
        }
        catch(Exception ex){
            ex.printStackTrace();
        }
    }
}
4.3 反序列化的结果

name: Junzerg
age: 20

5 对象引用的序列化和序列化算法

5.1 对象引用的序列化

如果要序列化的类的某个成员变量是一个非String类型的引用类型,那么这个引用类型必须是可序列化的。
例如有一个Teacher类持有Person类的引用

public class Teacher implements Seializable{
    private String name;
    Private Person student;
    public Teacher(String name, Person student){
        this.name = name;
        this.student = student;
    }
    // 省略name和student的stter和getter方法
    ...
}

为了在反序列化Teacher对象时正确恢复,Person类必须也是可序列化的,否则Teacher不可序列化

5.2 多个实例变量引用同一个引用对象的特殊情况

当两个Teacher对象引用同一个Person对象的时候:

Person per = new Person("Junzerg", 20);
Teacher t1 = new Teacher("Miss Li", per);
Teacher t2 = new Teacher("Mr Wu", per);

在程序依次序列化三个对象的过程中,看起来似乎会向输出流中输出三个Person对象。
这时当程序从输入流中反序列化这些对象时,就会得到三个Person对象,这样这样t1和t2引用的就不是同一个Person对象了。

5.3 Java序列化算法

为了避免5.2中出现的错误,Java的序列化算法如下:

  1. 所有保存在磁盘中的对象都有一个序列化编号
  2. 当程序试图序列化一个对象时,程序会先检查该对象是否已经被序列化过,只有改对象从未(在本次虚拟机中)被序列化过,系统才会将给对象转换成字节序列并出输出。
  3. 如果某个对象已经被序列化过,程序将直接出书一个序列化编号,而不是重新序列化该对象。

6. 自定义序列化

6.1 递归序列化

当对某个对象及进行序列化时,系统自动把该对象的所有实例变量依次进行序列化,如果某个实例变量引用另一个对象,则被引用的变量也会被序列化,这种情况被称为递归序列化。

6.2 transient关键字

在递归序列化的过程中,可能遇到不想被序列化或者不能被序列化的变量。这时可以使用transient关键字在序列化时忽略该变量,避免引发java.io.NotSerializableException异常。

6.3 transient关键字的使用
  1. 有带有transient关键字修饰的变量的Person类
public class Person implements java.io.Serializable {
    private String name;
    private transient int age;

    // 这里没有无参构造器
    public Person(String name, int age){
        this.name = name;
        this.age = age;
    }

    // name和age的setter和getter方法
    ...
}

注意:transient关键字只能用于修饰实例变量,不可修饰Java程序中的其他部分。

  1. Person对象的序列化和反序列化。
public class TransientTest{
    public static void main(String[] args){
        try{
            // 创建ObjectOutputStream输出流
            ObjectOutputStream oos = new ObjectOutputStream(
                new FileOutputStream("transient.txt"));
            //创建ObjectInputStream输入流
            ObjectInputStream ois = new ObjectInputStream(
                new FileInputStream("transient.txt"));
            Person per = new Person("Junzerg", 20);
            // 将per对象序列化输出
            oos.writeObject(per);
            Person p = (Person) ois.readObject();
            System.out.println(p.getAge());
        }
        catch (Exception ex){
            ex.printStackTrace();
        }
    }
}
  1. 输出结果
    3.1 控制台输出:

0

3.2 transient.txt内容为:

AC ED 00 05 73 72 00 06 50 65 72 73 6F 6E DB F9
DD 8C 83 99 C5 2E 02 00 01 4C 00 04 6E 61 6D 65
74 00 12 4C 6A 61 76 61 2F 6C 61 6E 67 2F 53 74
72 69 6E 67 3B 78 70 74 00 07 4A 75 6E 7A 65 72
67

大小为65字节。

可以看到per实例中的age变量并没有序列化。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容

  • JAVA序列化机制的深入研究 对象序列化的最主要的用处就是在传递,和保存对象(object)的时候,保证对象的完整...
    时待吾阅读 10,739评论 0 24
  • 1.背景 某天,我在写代码定义 bean 的时候,顺手写了个 public class User implemen...
    李眼镜阅读 755评论 0 2
  • 什么是序列化? 序列化是将对象存储为二进制格式。在序列化的过程中,对象和它的元数据(比如对象的类名和它的属性名称)...
    Chokez阅读 1,066评论 0 0
  • Java 提供了一种对象序列化的机制,该机制中,一个对象可以被表示为一个字节序列,该字节序列包括该对象的数据、有关...
    笑漫人生阅读 391评论 0 0
  • 对象序列化(serialization)和反序列化(deserialization)是将对象转化为便于传输的格式进...
    JerryL_阅读 7,459评论 1 7