深入了解java序列化

本文涉及的产品
系统运维管理,不限时长
简介: 在日常开发中,前端与后端的交互,系统之间的远程调用都需要使用到序列化技术,在java中使用序列化非常简单,只需要将被序列化的对象的类实现Java.io.Serializable接口即可。

在日常开发中,前端与后端的交互,系统之间的远程调用都需要使用到序列化技术,在java中使用序列化非常简单,只需要将被序列化的对象的类实现Java.io.Serializable接口即可。
对于实现序列化接口的类,我们需要注意两点:

  1. 类中的静态变量我们是无法序列化的,因为序列化只是针对对象,而静态变量是类级别的。
  2. 当子类实现序列化接口,而父类没有实现序列化接口时,将子类进行序列化,再反序列化回来后,发现父类中的属性会被重新初始化,也就是说会调用父类的无参构造。如果没有无参构造,则会抛出异常。

下面是通过两个小案例来证明:

public class SeriaTest implements Serializable{

    private static final long serialVersionUID = 1L;

    private static Integer count = 10;

    public static void main(String[] args) {
        try(ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("D:\\test.obj"));
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D:\\test.obj"))) {
            oos.writeObject(new SeriaTest());
            count = 20;
            SeriaTest seriaTest = (SeriaTest) ois.readObject();
            System.out.println(seriaTest.count);
        } catch (Exception e) {
            System.out.println("序列化反序列化时出现异常:"+e);
        }
    }
}

这段代码的运行结果为20,说明静态变量不会被序列化。

public class Fu {

    public String fu;
    public String getFu() {
        return fu;
    }
    public void setFu(String fu) {
        this.fu = fu;
    }
}

public class Zi extends Fu implements Serializable{

    private static final long serialVersionUID = 1L;
    public String zi;

    public Zi(){
        fu = "fu";
        zi = "zi";
    }
    public String getZi() {
        return zi;
    }
    public void setZi(String zi) {
        this.zi = zi;
    }
}

public class SeriaTest{

    public static void main(String[] args) {
        public static void main(String[] args) {
        try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("D:\\test.obj"));
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D:\\test.obj"))){
            oos.writeObject(new Zi());
            Zi zi = (Zi) ois.readObject();
            System.out.println(zi.fu);
            System.out.println(zi.zi);
        } catch (Exception e) {
        }
    }
}

这段代码的运行结果为null,zi。验证了上面的第二点。

将对象序列化实际上调用的就是ObjectOutputStream的wirteObject方法,跟踪源代码可知能被序列化的对象除了是实现Serializable接口的对象,还可以是String,数组或者枚举对象:

if (paramObject instanceof String) {
        writeString((String) paramObject, paramBoolean);
} else if (((Class) localObject2).isArray()) {
        writeArray(paramObject, localObjectStreamClass, paramBoolean);
} else if (paramObject instanceof Enum) {
        writeEnum((Enum) paramObject, localObjectStreamClass,paramBoolean);
} else if (paramObject instanceof Serializable) {
        writeOrdinaryObject(paramObject, localObjectStreamClass,paramBoolean);
}

再继续跟进writeOrdinaryObject方法,我们会发现会有这么一段逻辑:

if ((paramObjectStreamClass.isExternalizable()) && (!(paramObjectStreamClass.isProxy())))
    writeExternalData((Externalizable) paramObject);
else
    writeSerialData(paramObject, paramObjectStreamClass);

先判断是不是实现了Externalizable接口(这个是Serializable的子接口,这里先不管,后面会解释),如果不是,则执行writeSerialData方法。跟进去:

if (localObjectStreamClass.hasWriteObjectMethod()) {
    ********省略*******       
    localObjectStreamClass.invokeWriteObject(paramObject, this);
    ********省略*******   
} else {
    defaultWriteFields(paramObject, localObjectStreamClass);
}

我们发现源代码先判断了一下这个需要被序列化的类中有没有writeObject这个方法,如果有,那么就执行,如果没有,那么就执行默认的序列化方法。对于这个writeObject方法,有以下几个要求:

  • 方法名必须叫writeObject
  • 必须是私有的方法
  • 返回值类型必须为void

    只有满足了以上三点,在序列化对象的时候,才会执行我们自定义的序列化方法。当然,我们除了可以重写writeObject方法,我们还可以重写readObject,readObjectNoData,writeReplace,readResolve等方法,这些方法之间有什么联系,有什么作用呢?我们通过一段代码来探索一下:

public class SeriaTest implements Serializable{

    private static final long serialVersionUID = 1L;

    public Integer count = 10;

    public SeriaTest(Integer count){
        this.count = count;
    }

    public static void main(String[] args) {
        try(ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("D:\\test.obj"));
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D:\\test.obj"))) {
            oos.writeObject(new SeriaTest(20));
            SeriaTest seriaTest = (SeriaTest) ois.readObject();
            System.out.println(seriaTest.count);
        } catch (Exception e) {
            System.out.println("序列化反序列化时出现异常:"+e);
        }
    }

    private void writeObject(ObjectOutputStream oos) throws IOException{
        System.out.println("序列化前执行了自定义的writeObject方法");
        oos.defaultWriteObject();
        System.out.println("序列化后执行了自定义的writeObject方法");
    }

    private Object writeReplace() {
        System.out.println("序列化时执行了自定义的writeReplace方法");
        SeriaTest s = new SeriaTest(10);
        return s;
    }
}

执行结果如下:

序列化时执行了自定义的writeReplace方法
序列化前执行了自定义的writeObject方法
序列化后执行了自定义的writeObject方法
10

根据执行结果我们我们可以知道,在对象序列化的时候,会先去执行被序列化的类中的writeReplace方法,再执行writeObject方法,如果重写了writeReplace方法,那么被序列化的对象就是这个方法的返回值,而writeObject方法主要作用就是在序列化前后可以做处理操作。
对应的读操作的方法分别是readResolve方法和readObject方法,将上面的代码再完善一下:

public class SeriaTest implements Serializable{

    private static final long serialVersionUID = 1L;

    public Integer count = 10;

    public SeriaTest(Integer count){
        this.count = count;
    }

    public static void main(String[] args) {
        try(ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("D:\\test.obj"));
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D:\\test.obj"))) {
            oos.writeObject(new SeriaTest(20));
            SeriaTest seriaTest = (SeriaTest) ois.readObject();
            System.out.println(seriaTest.count);
        } catch (Exception e) {
            System.out.println("序列化反序列化时出现异常:"+e);
        }
    }

    private void writeObject(ObjectOutputStream oos) throws IOException{
        System.out.println("序列化前执行了自定义的writeObject方法");
        oos.defaultWriteObject();
        System.out.println("序列化后执行了自定义的writeObject方法");
    }

    private Object writeReplace() {
        System.out.println("序列化时执行了自定义的writeReplace方法");
        SeriaTest s = new SeriaTest(10);
        return s;
    }

    private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException{
        System.out.println("反序列化前执行了自定义的readObject方法");
        ois.defaultReadObject();
        System.out.println("反序列化后执行了自定义的readObject方法");
    }

    private Object readResolve(){
        System.out.println("反序列化前执行了自定义的readReslove方法");
        SeriaTest s = new SeriaTest(30);
        return s;
    }
}

执行结果符合我们的预期:

序列化时执行了自定义的writeReplace方法
序列化前执行了自定义的writeObject方法
序列化后执行了自定义的writeObject方法
反序列化前执行了自定义的readObject方法
反序列化后执行了自定义的readObject方法
反序列化前执行了自定义的readReslove方法
30

至于readObjectNoData方法,这个比较少见,这个方法的作用就是当一个对象被序列化后,反序列化时可以添加属性。(具体可参考:readObjectNoData方法作用

下面我们再来解析一下上面提到的Externalizable接口,这个接口是Serializable接口的子接口,这个接口有两个抽象方法:writeExternal和readExternal,这个两个方法分别对应于writeObject和readObject,不同点在于writeObject和readObject都是私有方法,所以其子类不能复用并且不能复写,而writeExternal和readExternal是共有方法,其子类可以复用并且复写。

public class SeriaTest implements Externalizable{

    private String name;
    private Integer age;

    public static void main(String[] args) {
        try(ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("D:\\test.obj"));
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D:\\test.obj"))
            ) {
            oos.writeObject(new SeriaTest());
            SeriaTest seriaTest = (SeriaTest) ois.readObject();
            System.out.println(seriaTest.name);
            System.out.println(seriaTest.age);
        } catch (Exception e) {
            System.out.println("序列化反序列化时出现异常:"+e);
        }
    }

    @Override
    public void writeExternal(ObjectOutput paramObjectOutput)
            throws IOException {
        System.out.println("writeExternal.....");
        paramObjectOutput.writeInt(18);
        paramObjectOutput.write("zhangsan".getBytes(Charset.forName("UTF-8")));
    }

    @Override
    public void readExternal(ObjectInput paramObjectInput) throws IOException,
            ClassNotFoundException {
        System.out.println("readExternal.....");
        age = paramObjectInput.readInt();
        name = paramObjectInput.readLine();
    }

}

执行结果如下:

writeExternal.....
readExternal.....
zhangsan
18
目录
相关文章
|
4月前
|
存储 Java
【IO面试题 四】、介绍一下Java的序列化与反序列化
Java的序列化与反序列化允许对象通过实现Serializable接口转换成字节序列并存储或传输,之后可以通过ObjectInputStream和ObjectOutputStream的方法将这些字节序列恢复成对象。
|
1月前
|
存储 安全 Java
🌟Java零基础-反序列化:从入门到精通
【10月更文挑战第21天】本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
80 5
|
1月前
|
存储 缓存 安全
🌟Java零基础:深入解析Java序列化机制
【10月更文挑战第20天】本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
29 3
|
1月前
|
存储 安全 Java
Java编程中的对象序列化与反序列化
【10月更文挑战第22天】在Java的世界里,对象序列化和反序列化是数据持久化和网络传输的关键技术。本文将带你了解如何在Java中实现对象的序列化与反序列化,并探讨其背后的原理。通过实际代码示例,我们将一步步展示如何将复杂数据结构转换为字节流,以及如何将这些字节流还原为Java对象。文章还将讨论在使用序列化时应注意的安全性问题,以确保你的应用程序既高效又安全。
|
3月前
|
JSON NoSQL Java
redis的java客户端的使用(Jedis、SpringDataRedis、SpringBoot整合redis、redisTemplate序列化及stringRedisTemplate序列化)
这篇文章介绍了在Java中使用Redis客户端的几种方法,包括Jedis、SpringDataRedis和SpringBoot整合Redis的操作。文章详细解释了Jedis的基本使用步骤,Jedis连接池的创建和使用,以及在SpringBoot项目中如何配置和使用RedisTemplate和StringRedisTemplate。此外,还探讨了RedisTemplate序列化的两种实践方案,包括默认的JDK序列化和自定义的JSON序列化,以及StringRedisTemplate的使用,它要求键和值都必须是String类型。
redis的java客户端的使用(Jedis、SpringDataRedis、SpringBoot整合redis、redisTemplate序列化及stringRedisTemplate序列化)
|
2月前
|
存储 Java
Java编程中的对象序列化与反序列化
【10月更文挑战第9天】在Java的世界里,对象序列化是连接数据持久化与网络通信的桥梁。本文将深入探讨Java对象序列化的机制、实践方法及反序列化过程,通过代码示例揭示其背后的原理。从基础概念到高级应用,我们将一步步揭开序列化技术的神秘面纱,让读者能够掌握这一强大工具,以应对数据存储和传输的挑战。
|
2月前
|
存储 安全 Java
Java编程中的对象序列化与反序列化
【10月更文挑战第3天】在Java编程的世界里,对象序列化与反序列化是实现数据持久化和网络传输的关键技术。本文将深入探讨Java序列化的原理、应用场景以及如何通过代码示例实现对象的序列化与反序列化过程。从基础概念到实践操作,我们将一步步揭示这一技术的魅力所在。
|
2月前
|
消息中间件 存储 Java
大数据-58 Kafka 高级特性 消息发送02-自定义序列化器、自定义分区器 Java代码实现
大数据-58 Kafka 高级特性 消息发送02-自定义序列化器、自定义分区器 Java代码实现
56 3
|
2月前
|
分布式计算 资源调度 Hadoop
Hadoop-10-HDFS集群 Java实现MapReduce WordCount计算 Hadoop序列化 编写Mapper和Reducer和Driver 附带POM 详细代码 图文等内容
Hadoop-10-HDFS集群 Java实现MapReduce WordCount计算 Hadoop序列化 编写Mapper和Reducer和Driver 附带POM 详细代码 图文等内容
109 3
|
2月前
|
Java 数据库 对象存储
Java 序列化详解
本文详细解析了Java序列化的概念与应用。通过具体实例,深入探讨了其在对象存储和传输中的作用及实现方法,帮助读者理解如何有效利用这一特性来简化数据交换,并对其实现机制有了更深入的认识。
55 9
下一篇
DataWorks