侯体宗的博客
  • 首页
  • Hyperf版
  • beego仿版
  • 人生(杂谈)
  • 技术
  • 关于我
  • 更多分类
    • 文件下载
    • 文字修仙
    • 中国象棋ai
    • 群聊
    • 九宫格抽奖
    • 拼图
    • 消消乐
    • 相册

Java之对象的序列化和反序列化

Java  /  管理员 发布于 8年前   490

对象的序列化和反序列化

1)对象序列化,就是将Object对象转换成byte序列,反之叫对象的反序列化。

2)序列化流(ObjectOutputStream),是字节的过滤流—— writeObject()方法

反序列化流(ObjectInputStream)—— readObject()方法

3)序列化接口(Serializable)

对象必须实现序列化接口,才能进行序列化,否则将出现异常。

注:这个接口,没有任何方法,只是一个【标准】

一、最基本的序列化和反序列过程

序列化和反序列都是以Object对象进行操作的,这里通过一个简单的案例来给大家演示一下对象序列化和反序列化的过程。

1、新建一个Student类(测试类)

注意:需要实现序列化接口的类才能进行序列化操作!!

@SuppressWarnings("serial")public class Student implements Serializable{    private String stuno;//id    private String stuna;//姓名    private int stuage;//年龄    public String getStuno() {        return stuno;    }    public void setStuno(String stuno) {        this.stuno = stuno;    }    public String getStuna() {        return stuna;    }    public void setStuna(String stuna) {        this.stuna = stuna;    }    public Student() {        super();        // TODO Auto-generated constructor stub    }    public Student(String stuno, String stuna, int stuage) {        super();        this.stuno = stuno;        this.stuna = stuna;        this.stuage = stuage;    }    @Override    public String toString() {        return "Student [stuno=" + stuno + ", stuna=" + stuna + ", stuage=" + stuage + "]";    }    public int getStuage() {        return stuage;    }    public void setStuage(int stuage) {        this.stuage = stuage;    }}

2、将Student类的实例序列化成文件

基本操作步骤如下:

1)、指定序列化保存的文件

2)、构造ObjectOutputStream类

3)、构造一个Student类

4)、使用writeObject方法序列化

5)、使用close()方法关闭流

String file="demo/obj.dat";        //对象的序列化        ObjectOutputStream oos=new ObjectOutputStream(    new FileOutputStream(file));        //把Student对象保存起来,就是对象的序列化        Student stu=new Student("01","mike",18);        //使用writeObject方法序列化        oos.writeObject(stu);        oos.close();

运行结果:可以看到demo目录下生成了obj.dat的序列化文件

3、将文件反序列化读出Student类对象

基本操作步骤如下:

1)、指定反序列化的文件

2)、构造ObjectInputStream类

3)、使用readObject方法反序列化

1)、使用close方法关闭流

String file="demo/obj.dat";        ObjectInputStream ois =new ObjectInputStream(    new FileInputStream(file));        //使用readObject()方法序列化        Student stu=(Student)ois.readObject();//强制类型转换        System.out.println(stu);        ois.close();

运行结果:

注意:在文件反序列化时,readObject方法取出的对象默认都是Object类型,必须强制转换为相应的类型。

二、transient及ArrayList源码分析

在日常编程过程中,我们有时不希望一个类所有的元素都被编译器序列化,这时该怎么办呢?

Java提供了一个transient关键字来修饰我们不希望被jvm自动序列化的元素。下面简单来讲解一下这个关键字。

transient 关键字:被transient修饰的元素,该元素不会进行jvm默认的序列化,但可以自己完成这个元素的序列化。

注意:

1)在以后的网络编程中,如果有某些元素不需要传输,那就可以用transient修饰,来节省流量;对有效元素序列化,提高性能。

2)可以使用writeObject自己完成这个元素的序列化。

ArrayList就是用了此方法进行了优化操作。ArrayList最核心的容器Object[] elementData使用了transient修饰,但是在writeObject自己实现对elementData数组的序列化。只对数组中有效元素进行序列化。readObject与之类似。

--------------自己序列化的方式---------------

在要序列化的类中加入两个方法(这两个方法都是从ArrayList源码中提取出来的,比较特殊的两个方法):

private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException{        s.defaultWriteObject();//把jvm能默认序列化的元素进行序列化操作        s.writeInt(stuage);//自己完成被transient修饰的元素的序列化    }    private void readObject(java.io.ObjectInputStream s) throws java.io.IOException,ClassNotFoundException{        s.defaultReadObject();//把jvm能默认反序列化的元素进行反序列化操作        this.stuage=s.readInt();//自己完成stuage的反序列化操作    }

加入这两个方法后,即使被transient修饰的元素也能像刚刚那样进行序列化和反序列化了,jvm会自动使用这两个方法帮助我们完成这动作。

这里又有个问题,为什么还需要手动去完成序列化和反序列化呢,有什么意义呢?

这个问题得再从ArrayList的源码中去分析:

可以看出ArrayList源码中自己序列化的目的:ArrayList底层为数组,自己序列化可以过滤数组中无效的元素,只序列化数组中有效的元素,从而提高性能。

因此,实际编程过程中我们可以根据需要来自己完成序列化以提高性能。

三、序列化中子父类构造函数问题

在类的序列化和反序列化中,如果存在子类和父类的关系时,序列化和反序列化的过程又是怎么样的呢?

这里我写一个测试类来测试子类和父类实现序列化和反序列化时构造函数的实现变化。

public static void main(String[] args) throws IOException {        // TODO Auto-generated method stub        String file="demo/foo.dat";        ObjectOutputStream oos=new ObjectOutputStream(    new FileOutputStream(file));        Foo2 foo2 =new Foo2();        oos.writeObject(foo2);        oos.flush();        oos.close();    }}class Foo implements Serializable{    public Foo(){        System.out.println("foo");    }}class Foo1 extends Foo{    public Foo1(){        System.out.println("foo1");    }    }class Foo2 extends Foo1{    public Foo2(){        System.out.println("foo2");    }}

运行结果:这是序列化时递归调用了父类的构造函数

接来下看看反序列化时,是否递归调用父类的构造函数。

ObjectInputStream ois=new ObjectInputStream(new FileInputStream(file));Foo2 foo2=(Foo2)ois.readObject();ois.close();

运行结果:控制台没有任何输出。

那么这个结果是否证明反序列化过程中父类的构造函数就是始终不调用的呢?

然而不能证明!!

因为再看下面这个不同的测试例子:

class Bar {    public Bar(){        System.out.println("bar");    }}class Bar1 extends Bar implements Serializable{    public Bar1(){        System.out.println("bar1");    }}class Bar2 extends Bar1{    public Bar2(){        System.out.println("bar2");    }}

我们用这个例子来测试序列化和反序列化。

序列化结果:

反序列化结果:没实现序列化接口的父类被显示调用构造函数

【反序列化时】,向上递归调用构造函数会从【可序列化的一级父类结束】。即谁实现了可序列化(包括继承实现的),谁的构造函数就不会调用。

总结:

1)父类实现了serializable接口,子类继承就可序列化。

子类在反序列化时,父类实现了序列化接口,则不会递归调用其构造函数。

2)父类未实现serializable接口,子类自行实现可序列化

子类在反序列化时,父类没有实现序列化接口,则会递归调用其构造函数。

本文来自 java入门 栏目,欢迎学习!

以上就是Java之对象的序列化和反序列化的详细内容,更多请关注其它相关文章!


  • 上一条:
    深入学习java之泛型
    下一条:
    详解Java8 Stream Api中map和flatMap操作
  • 昵称:

    邮箱:

    0条评论 (评论内容有缓存机制,请悉知!)
    最新最热
    • 分类目录
    • 人生(杂谈)
    • 技术
    • linux
    • Java
    • php
    • 框架(架构)
    • 前端
    • ThinkPHP
    • 数据库
    • 微信(小程序)
    • Laravel
    • Redis
    • Docker
    • Go
    • swoole
    • Windows
    • Python
    • 苹果(mac/ios)
    • 相关文章
    • 在java中实现的脱敏工具类代码示例分享(0个评论)
    • zookeeper安装流程步骤(0个评论)
    • 在java中你背的“八股文”可能已经过时了(2个评论)
    • 在php8.0+版本中使用属性来增加值代码示例(3个评论)
    • java 正则表达式基础,实例学习资料收集大全 原创(0个评论)
    • 近期文章
    • 在go中实现一个常用的先进先出的缓存淘汰算法示例代码(0个评论)
    • 在go+gin中使用"github.com/skip2/go-qrcode"实现url转二维码功能(0个评论)
    • 在go语言中使用api.geonames.org接口实现根据国际邮政编码获取地址信息功能(1个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf分页文件功能(0个评论)
    • gmail发邮件报错:534 5.7.9 Application-specific password required...解决方案(0个评论)
    • 欧盟关于强迫劳动的规定的官方举报渠道及官方举报网站(0个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf文件功能(0个评论)
    • Laravel从Accel获得5700万美元A轮融资(0个评论)
    • 在go + gin中gorm实现指定搜索/区间搜索分页列表功能接口实例(0个评论)
    • 在go语言中实现IP/CIDR的ip和netmask互转及IP段形式互转及ip是否存在IP/CIDR(0个评论)
    • 近期评论
    • 122 在

      学历:一种延缓就业设计,生活需求下的权衡之选中评论 工作几年后,报名考研了,到现在还没认真学习备考,迷茫中。作为一名北漂互联网打工人..
    • 123 在

      Clash for Windows作者删库跑路了,github已404中评论 按理说只要你在国内,所有的流量进出都在监控范围内,不管你怎么隐藏也没用,想搞你分..
    • 原梓番博客 在

      在Laravel框架中使用模型Model分表最简单的方法中评论 好久好久都没看友情链接申请了,今天刚看,已经添加。..
    • 博主 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 @1111老铁这个不行了,可以看看近期评论的其他文章..
    • 1111 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 网站不能打开,博主百忙中能否发个APP下载链接,佛跳墙或极光..
    • 2016-11
    • 2018-03
    • 2020-03
    • 2023-05
    • 2023-11
    • 2024-01
    Top

    Copyright·© 2019 侯体宗版权所有· 粤ICP备20027696号 PHP交流群

    侯体宗的博客