【Java基础】序列化与反序列化深入分析 (下)

简介:  复习Java基础知识点的序列化与反序列化过程,整理了如下学习笔记。

六、序列化问题


  6.1 采用默认序列化机制,类的静态字段会被序列化吗?


  采用默认序列化机制进行序列化时,类的静态字段会被序列化吗,此时类的静态字段不会被序列化,当然,我们可以采用自定义序列化逻辑对静态变量进行序列化。


  6.2 父类序列化问题


  采用默认序列化机制序列化子类时,其父类的字段会被序列化吗?可以分为如下情形


  1. 父类没有实现Serializable接口,没有提供默认构造函数


  这时,反序列化会出错,提示没有提供正确的构造函数。修改Person类,代码如下

package com.hust.grid.leesf.serializable;
import java.io.Serializable;
class Human {
    private int number;
    public Human(int number) {
        this.number = number;
    }
    public int getNumber() {
        return number;
    }
    public void setNumber(int number) {
        this.number = number;
    }
    public String toString() {
        return "number = " + number;
    }
}
public class Person extends Human implements Serializable {
    /**
     * 
     */
    private static final long serialVersionUID = 1L;
    private String name;
    private String gender;
    private int age;
    private transient Person friend;
    public Person(int number, String name) {
        super(number);
        this.name = name;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getGender() {
        return gender;
    }
    public void setGender(String gender) {
        this.gender = gender;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public Person getFriend() {
        return friend;
    }
    public void setFriend(Person friend) {
        this.friend = friend;
    }
    @Override
    public String toString() {
        return super.toString() + ", name = " + name + ", gender = " + gender + ", age = " + age
                + ", friend info is [" + friend + "]";
    }    
}

 测试类的的代码如下

复制代码
package com.hust.grid.leesf.serializable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class SerializableDemo {
    public static void main(String[] args) throws Exception {
        Person leesf = new Person(1, "leesf");
        Person dyd = new Person(2, "dyd");
        leesf.setAge(24);
        leesf.setGender("man");
        leesf.setName("leesf");
        dyd.setAge(24);
        dyd.setGender("woman");
        dyd.setName("dyd");    
        leesf.setFriend(dyd);
        dyd.setFriend(null);
        File file = new File("test");
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file));
        oos.writeObject(leesf);
        oos.flush();
        oos.close();
        System.out.println(leesf);
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
        leesf = (Person) ois.readObject();
        ois.close();    
        System.out.println(leesf);    
    }
}

  运行结果 

number = 1, name = leesf, gender = man, age = 24, friend info is [number = 2, name = dyd, gender = woman, age = 24, friend info is [null]]
Exception in thread "main" java.io.InvalidClassException: com.hust.grid.leesf.serializable.Person; no valid constructor
    at java.io.ObjectStreamClass$ExceptionInfo.newInvalidClassException(ObjectStreamClass.java:150)
    at java.io.ObjectStreamClass.checkDeserialize(ObjectStreamClass.java:768)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1775)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1351)
    at java.io.ObjectInputStream.readObject(ObjectInputStream.java:371)
    at com.hust.grid.leesf.serializable.SerializableDemo.main(SerializableDemo.java:32)

说明:可以看出是没有提供合法的构造函数。


  2. 父类没有实现Serializable接口,提供默认构造函数


  第一步中出现了错误,此时,我们修改Person类,代码如下 

package com.hust.grid.leesf.serializable;
import java.io.Serializable;
class Human {
    private int number;
    public Human() {
    }
    public Human(int number) {
        this.number = number;
    }
    public int getNumber() {
        return number;
    }
    public void setNumber(int number) {
        this.number = number;
    }
    public String toString() {
        return "number = " + number;
    }
}
public class Person extends Human implements Serializable {
    /**
     * 
     */
    private static final long serialVersionUID = 1L;
    private String name;
    private String gender;
    private int age;
    private transient Person friend;
    public Person() {
        super();
    }
    public Person(int number, String name) {
        super(number);
        this.name = name;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getGender() {
        return gender;
    }
    public void setGender(String gender) {
        this.gender = gender;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public Person getFriend() {
        return friend;
    }
    public void setFriend(Person friend) {
        this.friend = friend;
    }
    @Override
    public String toString() {
        return super.toString() + ", name = " + name + ", gender = " + gender + ", age = " + age
                + ", friend info is [" + friend + "]";
    }    
}

 说明:给Human类提供了无参构造函数。测试类代码不变,运行结果如下

number = 1, name = leesf, gender = man, age = 24, friend info is [number = 2, name = dyd, gender = woman, age = 24, friend info is [null]]
number = 0, name = leesf, gender = man, age = 24, friend info is [null]

说明:此时,我们可以看到,可以进行反序列化了,但是父类的number字段被赋值为int的默认值0,Person类的transient字段没有被序列化。


  3. 父类实现Serializable接口


  当父类实现Serializable接口时,修改Person类代码如下 

package com.hust.grid.leesf.serializable;
import java.io.Serializable;
class Human implements Serializable {
    /**
     * 
     */
    private static final long serialVersionUID = 1L;
    private int number;
    public Human() {
    }
    public Human(int number) {
        this.number = number;
    }
    public int getNumber() {
        return number;
    }
    public void setNumber(int number) {
        this.number = number;
    }
    public String toString() {
        return "number = " + number;
    }
}
public class Person extends Human implements Serializable {
    /**
     * 
     */
    private static final long serialVersionUID = 1L;
    private String name;
    private String gender;
    private int age;
    private transient Person friend;
    public Person() {
        super();
    }
    public Person(int number, String name) {
        super(number);
        this.name = name;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getGender() {
        return gender;
    }
    public void setGender(String gender) {
        this.gender = gender;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public Person getFriend() {
        return friend;
    }
    public void setFriend(Person friend) {
        this.friend = friend;
    }
    @Override
    public String toString() {
        return super.toString() + ", name = " + name + ", gender = " + gender + ", age = " + age
                + ", friend info is [" + friend + "]";
    }    
}

  测试类的代码不变,运行结果如下 

number = 1, name = leesf, gender = man, age = 24, friend info is [number = 2, name = dyd, gender = woman, age = 24, friend info is [null]]
number = 1, name = leesf, gender = man, age = 24, friend info is [null]

 说明:从结果可知,已经可以进行正确的序列化与反序列化了,子类的transient字段没有被序列化。


  6.3 共享对象序列化问题  


  当序列化的两个对象都包含另外一个对象的引用时,在反序列化时,另外一个对象只会出现一次吗?修改Person类代码如下 

package com.hust.grid.leesf.serializable;
import java.io.Serializable;
public class Person implements Serializable {
    /**
     * 
     */
    private static final long serialVersionUID = 1L;
    private String name;
    private String gender;
    private int age;
    private transient Person friend;
    public Person() {
    }
    public Person(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getGender() {
        return gender;
    }
    public void setGender(String gender) {
        this.gender = gender;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public Person getFriend() {
        return friend;
    }
    public void setFriend(Person friend) {
        this.friend = friend;
    }
}

 测试类代码如下 

package com.hust.grid.leesf.serializable;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.List;
public class SerializableDemo {
    @SuppressWarnings("unchecked")
    public static void main(String[] args) throws Exception {
        Person leesf = new Person("leesf");    
        Person dyd = new Person("dyd");
        Person lr = new Person("lr");
        leesf.setAge(24);
        leesf.setGender("man");
        dyd.setAge(24);
        dyd.setGender("woman");
        lr.setAge(25);
        lr.setGender("man");
        leesf.setFriend(dyd);
        lr.setFriend(dyd);
        dyd.setFriend(null);
        List<Person> persons = new ArrayList<Person>();
        persons.add(leesf);
        persons.add(dyd);
        persons.add(lr);
        ByteArrayOutputStream bos1 = new ByteArrayOutputStream();
        ByteArrayOutputStream bos2 = new ByteArrayOutputStream();
        ObjectOutputStream oos1 = new ObjectOutputStream(bos1);
        oos1.writeObject(persons);
        oos1.writeObject(persons);
        ObjectOutputStream oos2 = new ObjectOutputStream(bos2);
        oos2.writeObject(persons);
        ByteArrayInputStream bis1 = new ByteArrayInputStream(bos1.toByteArray());
        ByteArrayInputStream bis2 = new ByteArrayInputStream(bos2.toByteArray());
        ObjectInputStream ois1 = new ObjectInputStream(bis1);
        ObjectInputStream ois2 = new ObjectInputStream(bis2);
        List<Person> persons1 = (List<Person>) ois1.readObject();
        List<Person> persons2 = (List<Person>) ois1.readObject();
        List<Person> persons3 = (List<Person>) ois2.readObject();
        System.out.println(persons1);
        System.out.println(persons2);
        System.out.println(persons3);    
    }
}

 运行结果如下  

[com.hust.grid.leesf.serializable.Person@7f31245a, com.hust.grid.leesf.serializable.Person@6d6f6e28, com.hust.grid.leesf.serializable.Person@135fbaa4]
[com.hust.grid.leesf.serializable.Person@7f31245a, com.hust.grid.leesf.serializable.Person@6d6f6e28, com.hust.grid.leesf.serializable.Person@135fbaa4]
[com.hust.grid.leesf.serializable.Person@45ee12a7, com.hust.grid.leesf.serializable.Person@330bedb4, com.hust.grid.leesf.serializable.Person@2503dbd3]

 说明:从结果可知,oos1执行的writeObject是向同一个内存空间写了两次,从结果可看出,两次写入的对象的地址空间都是一样的,即进行了浅拷贝。而oos2执行的writeObject是向另外一个内存空间写了一次,从结果可看出,因为对象的地址不同于之前的对象地址,即采用了深拷贝。


七、总结


  写到这里,关于Java中的序列化与反序列化机制就已经分析完了,经过此次分析,对序列化机制的认识更加深刻。学习一个知识点,就要认认真真,踏踏实实的弄懂一个知识点,写博客就是一个特别好的方式。谢谢各位园友的观看~

目录
相关文章
|
20天前
|
缓存 算法 搜索推荐
Java中的算法优化与复杂度分析
在Java开发中,理解和优化算法的时间复杂度和空间复杂度是提升程序性能的关键。通过合理选择数据结构、避免重复计算、应用分治法等策略,可以显著提高算法效率。在实际开发中,应该根据具体需求和场景,选择合适的优化方法,从而编写出高效、可靠的代码。
29 6
|
2月前
|
监控 算法 Java
jvm-48-java 变更导致压测应用性能下降,如何分析定位原因?
【11月更文挑战第17天】当JVM相关变更导致压测应用性能下降时,可通过检查变更内容(如JVM参数、Java版本、代码变更)、收集性能监控数据(使用JVM监控工具、应用性能监控工具、系统资源监控)、分析垃圾回收情况(GC日志分析、内存泄漏检查)、分析线程和锁(线程状态分析、锁竞争分析)及分析代码执行路径(使用代码性能分析工具、代码审查)等步骤来定位和解决问题。
|
2月前
|
JSON 数据格式 索引
Python中序列化/反序列化JSON格式的数据
【11月更文挑战第4天】本文介绍了 Python 中使用 `json` 模块进行序列化和反序列化的操作。序列化是指将 Python 对象(如字典、列表)转换为 JSON 字符串,主要使用 `json.dumps` 方法。示例包括基本的字典和列表序列化,以及自定义类的序列化。反序列化则是将 JSON 字符串转换回 Python 对象,使用 `json.loads` 方法。文中还提供了具体的代码示例,展示了如何处理不同类型的 Python 对象。
|
2月前
|
存储 安全 Java
🌟Java零基础-反序列化:从入门到精通
【10月更文挑战第21天】本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
95 5
|
2月前
|
存储 Java 关系型数据库
在Java开发中,数据库连接是应用与数据交互的关键环节。本文通过案例分析,深入探讨Java连接池的原理与最佳实践
在Java开发中,数据库连接是应用与数据交互的关键环节。本文通过案例分析,深入探讨Java连接池的原理与最佳实践,包括连接创建、分配、复用和释放等操作,并通过电商应用实例展示了如何选择合适的连接池库(如HikariCP)和配置参数,实现高效、稳定的数据库连接管理。
73 2
|
2月前
|
存储 缓存 安全
🌟Java零基础:深入解析Java序列化机制
【10月更文挑战第20天】本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
36 3
|
2月前
|
存储 安全 Java
Java编程中的对象序列化与反序列化
【10月更文挑战第22天】在Java的世界里,对象序列化和反序列化是数据持久化和网络传输的关键技术。本文将带你了解如何在Java中实现对象的序列化与反序列化,并探讨其背后的原理。通过实际代码示例,我们将一步步展示如何将复杂数据结构转换为字节流,以及如何将这些字节流还原为Java对象。文章还将讨论在使用序列化时应注意的安全性问题,以确保你的应用程序既高效又安全。
|
2月前
|
Java 关系型数据库 数据库
面向对象设计原则在Java中的实现与案例分析
【10月更文挑战第25天】本文通过Java语言的具体实现和案例分析,详细介绍了面向对象设计的五大核心原则:单一职责原则、开闭原则、里氏替换原则、接口隔离原则和依赖倒置原则。这些原则帮助开发者构建更加灵活、可维护和可扩展的系统,不仅适用于Java,也适用于其他面向对象编程语言。
47 2
|
2月前
|
存储 缓存 NoSQL
一篇搞懂!Java对象序列化与反序列化的底层逻辑
本文介绍了Java中的序列化与反序列化,包括基本概念、应用场景、实现方式及注意事项。序列化是将对象转换为字节流,便于存储和传输;反序列化则是将字节流还原为对象。文中详细讲解了实现序列化的步骤,以及常见的反序列化失败原因和最佳实践。通过实例和代码示例,帮助读者更好地理解和应用这一重要技术。
63 0
|
5月前
|
存储 Java
【IO面试题 四】、介绍一下Java的序列化与反序列化
Java的序列化与反序列化允许对象通过实现Serializable接口转换成字节序列并存储或传输,之后可以通过ObjectInputStream和ObjectOutputStream的方法将这些字节序列恢复成对象。