浅析关于java的序列化和反序列化

 更新时间:2023年07月26日 11:53:52   作者:yasooooo  
这篇文章主要介绍了浅析关于java的序列化和反序列化,所谓序列化,就是把要传输的对象以及相关信息转换成字节数组进行存储的过程,而反序列化就是将字节数组再转回对象的过程,需要的朋友可以参考下

对于序列化和反序列化,大家或多或少都会听过一点。

所谓序列化,就是把要传输的对象以及相关信息转换成字节数组进行存储的过程。

而反序列化就是将字节数组再转回对象的过程。

对于序列化和反序列化总结了几点需要注意的地方,

1、实现Serializable接口的类才能够序列化,如果是父类实现了该接口,子类也可以进行序列化

这点不过多解释,规定就是这样。

2、静态成员不能被序列化、方法不能被序列化,关键字transient修饰的属性不能序列化

话不多说,上代码。

import java.io.*;
/**
 * 测试静态成员、方法、transient修饰的属性不能序列化
 */
public class SerializeTest {
    public static void main(String[] args) throws IOException {
        Test test = new Test(); // 创建被序列化的对象
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("test.data"));
        oos.writeObject(test);
        oos.close(); //这里记得序列化完成之后要关流,和io流一样,不多解释
    }
}
class Test implements Serializable{ //创建一个类用于创建序列化对象
    static int a;
    transient private int b;
    private int c;
    public void m(){}
}

以上代码就是创建了一个类,然后对该类的对象进行序列化,序列化后的文件为test.data.然后查看了一个这个文件的大小为53字节。

下面我将这个类中的静态成员、transient修饰的成员和m()方法都注释掉再进行一次序列化

import java.io.*;
/**
 * 测试静态成员、方法、transient修饰的属性不能序列化
 */
public class SerializeTest {
    public static void main(String[] args) throws IOException {
        Test test = new Test(); // 创建被序列化的对象
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("test.data"));
        oos.writeObject(test);
        oos.close(); //这里记得序列化完成之后要关流,和io流一样,不多解释
    }
}
class Test implements Serializable{ // 将static属性、transient属性和方法都注释掉之后在进行一次序列化
//    static int a;
//    transient private int b;
    private int c;
//    public void m(){}
}

此时,看到再序列化之后的文件大小依然还是53字节,但是如果再将成员变量c注释掉再进行序列化就会发现,这个文件变小了

由此可以说明,静态成员、transient修饰的成员和方法都不能序列化。

3、对于向上造型的对象进行序列化,实际序列化的对象是实际创建类

上代码

 
import java.io.*;
/**
 * 测试向上造型的对象实际序列化的是实际创建类
 */
public class SerializeTest {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        Test test = new Test02();
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("test02.data"));
        oos.writeObject(test);
        oos.close();
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("test02.data"));
        System.out.println(ois.readObject()); //这里直接打印反序列化之后的对象信息
        ois.close();
    }
}
class Test implements Serializable{ //创建一个父类类用于创建序列化对象
}
class Test02 extends Test{    // 子类继承父类
}

上面代码的执行结果:Test02@4f3f5b24,可以看到打印的就是子类的对象,so,这个就证明了向上造型的对象,实际序列化的是实际创建类,同时也证明了如果父类实现了Serializable接口,子类也是可以序列化滴。

4、对于已经序列化完的对象,在进行反序列化时,如果对原类信息进行了修改(包含不能被序列化的属性和方法),此时将不能进行反序列化

直接上代码。

 
import java.io.*;
/**
 * 测试反序列化之前如果修改原类信息将不能进行反序列化
 * 
 */
public class SerializeTest {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("test02.data"));
        System.out.println(ois.readObject()); //这里直接打印反序列化之后的对象信息
        ois.close();
    }
}
class Test implements Serializable{ //创建一个父类类用于创建序列化对象
}
class Test02 extends Test{    // 子类继承父类
    // test02.data这个文件就是第三条中创建对象序列化之后的文件
    static int a = 1;    //这里我随便添加了一个静态属性,然后执行代码
}

///下面是执行结果
Exception in thread "main" java.io.InvalidClassException: night.Test02; local class incompatible: stream classdesc serialVersionUID = -4461189770521473347, local class serialVersionUID = -7303293535763263875
at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:616)
    at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1829)
    at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1713)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1986)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1535)
    at java.io.ObjectInputStream.readObject(ObjectInputStream.java:422)
    at night.SerializeTest.main(SerializeTest.java:23)

居然报了异常?

这里为什么会报异常呢,简单讲一哈:序列化的时候会计算一个序列化版本号-----serialVersionUID,并且将这个版本号一同存放到文件中,在反序列化的时候会获取此序列号,并且会计算当前类的serialVersionUID,如果两个版本号不相等就会报如上异常,反之反序列化就可以成功。~

那么对于这种情况就没有解决办法了? 当然是有的。

我们可以在类中直接声明serialVersionUID为 final属性,并让其自动赋初始值(这个值jvm肯定是计算过的了),格式如下:

private static final long serialVersionUID = 362498820763181265L;

这样不管怎样修改类的属性,这个serialVersionUID都不会改变,这样问题就解决了。

其实对于private static final long serialVersionUID = 362498820763181265L;这个东西,大家应该都见过的,比如我这里拿的就是HashMap类源码中的那个值。

以上就是浅析关于java的序列化和反序列化的详细内容,更多关于java序列化和反序列化的资料请关注脚本之家其它相关文章!

相关文章

  • 使用LambdaQueryWrapper动态加过滤条件 动态Lambda

    使用LambdaQueryWrapper动态加过滤条件 动态Lambda

    这篇文章主要介绍了使用LambdaQueryWrapper动态加过滤条件 动态Lambda,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教。
    2022-01-01
  • Spring中如何动态注入Bean实例教程

    Spring中如何动态注入Bean实例教程

    这篇文章主要给大家介绍了关于Spring中如何动态注入Bean的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧。
    2017-12-12
  • spring mvc配置bootstrap教程

    spring mvc配置bootstrap教程

    这篇文章主要为大家详细介绍了spring mvc配置bootstrap,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-07-07
  • Java容器类源码详解 Deque与ArrayDeque

    Java容器类源码详解 Deque与ArrayDeque

    这篇文章主要介绍了Java容器类源码详解 Deque与ArrayDeque,Deque 接口继承自 Queue接口,但 Deque 支持同时从两端添加或移除元素,因此又被成为双端队列。,需要的朋友可以参考下
    2019-06-06
  • Java使用正则提取字符串中的内容的详细步骤

    Java使用正则提取字符串中的内容的详细步骤

    这篇文章主要介绍了Java中使用正则表达式提取字符串内容的方法,通过Pattern和Matcher类实现,涵盖编译正则、查找匹配、分组捕获、数字与邮箱提取场景,以及命名分组、非贪婪匹配等技巧,需要的朋友可以参考下
    2025-08-08
  • Java比较两个对象是否相等的方法

    Java比较两个对象是否相等的方法

    这篇文章主要介绍了Java比较两个对象是否相等的方法,文中给出了三种方法,并通过代码讲解的非常详细,对大家的学习或工作有一定的帮助,需要的朋友可以参考下
    2024-03-03
  • 详解Java中数组判断元素存在几种方式比较

    详解Java中数组判断元素存在几种方式比较

    这篇文章主要介绍了Java中数组判断元素存在几种方式比较,非常不错,具有一定的参考借鉴价值,需要的朋友参考下吧
    2018-07-07
  • Java实现求二叉树的深度和宽度

    Java实现求二叉树的深度和宽度

    这篇文章主要介绍了Java实现求二叉树的深度和宽度,本文分别给出代码实例,需要的朋友可以参考下
    2015-06-06
  • 深入了解Java语言中的并发性选项有何不同

    深入了解Java语言中的并发性选项有何不同

    这篇文章主要介绍了深入了解Java语言中的并发性选项有何不同,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,,需要的朋友可以参考下
    2019-06-06
  • Java Spring AOP源码解析之事务实现原理

    Java Spring AOP源码解析之事务实现原理

    这篇文章主要为大家介绍了Java Spring AOP事务实现原理,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助
    2022-01-01

最新评论