搜索
首页JavaJava入门Java之对象的序列化和反序列化

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

Nov 27, 2019 pm 05:59 PM
java反序列化对象序列化

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

对象的序列化和反序列化

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之对象的序列化和反序列化的详细内容。更多信息请关注PHP中文网其他相关文章!

声明
本文转载于:cnblogs。如有侵权,请联系admin@php.cn删除

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热门文章

R.E.P.O.能量晶体解释及其做什么(黄色晶体)
4 周前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳图形设置
4 周前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您听不到任何人,如何修复音频
4 周前By尊渡假赌尊渡假赌尊渡假赌
WWE 2K25:如何解锁Myrise中的所有内容
1 个月前By尊渡假赌尊渡假赌尊渡假赌

热工具

VSCode Windows 64位 下载

VSCode Windows 64位 下载

微软推出的免费、功能强大的一款IDE编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

Dreamweaver Mac版

Dreamweaver Mac版

视觉化网页开发工具

mPDF

mPDF

mPDF是一个PHP库,可以从UTF-8编码的HTML生成PDF文件。原作者Ian Back编写mPDF以从他的网站上“即时”输出PDF文件,并处理不同的语言。与原始脚本如HTML2FPDF相比,它的速度较慢,并且在使用Unicode字体时生成的文件较大,但支持CSS样式等,并进行了大量增强。支持几乎所有语言,包括RTL(阿拉伯语和希伯来语)和CJK(中日韩)。支持嵌套的块级元素(如P、DIV),

Atom编辑器mac版下载

Atom编辑器mac版下载

最流行的的开源编辑器