首页 > 编程语言 >Java中的序列化和反序列化

Java中的序列化和反序列化

时间:2024-09-25 22:23:41浏览次数:7  
标签:Java 变量 对象 static 序列化 String

Java中序列化和反序列化的区别

序列化和反序列化的定义

序列化(Serialization)与反序列化(Deserialization)是编程中常见的两个概念,他们主要涉及到将数据结构或对象状态转换为可以存储或传输的格式,以及将存储或传输的格式转换回原始的数据结构或对象状态的过程。

这两个过程在数据持久化,网络通信,对象深拷贝等多个场景中发挥着重要作用。

一.序列化(Serialization)

序列化是指将数据结构或对象状态转换成可以存储或传输的格式的过程。这个格式通常是和平台无关的,比如JSON,XML,二进制格式等,以便可以在不同的系统或环境中进行交换。序列化后的数据可以存储在文件中,或者通过网络发送给其他系统。

为什么需要序列化?

  • 数据持久化:将对象状态保存到文件中,以便在程序关闭或系统重启后能够重新加载这些数据。
  • 网络传输:在网络通信中,数据需要在客户端和服务端之间传输,序列化可以将对象转换成字节流,便于在网络中传输。
  • 对象深拷贝:通过序列化一个对象,然后立即进行反序列化,可以实现对象的深拷贝。

二.反序列化(Deserialization)

反序列化是序列化的逆过程,即将序列化后的数据(如JSON,XML,二进制格式等)转换回原始的数据结构或对象状态。这个过程允许对象在需要时重新构建对象,并使用其原始数据。

为什么需要反序列化?

  • 数据恢复:在程序启动时,从文件中读取序列化后的数据,反序列化成对象,以恢复程序运行前的状态。
  • 数据接收:在网络通信中,接收到的序列化数据需要被反序列化成对象,以便在本地系统中使用。

三.Java的序列化

Java序列化是指将Java对象的状态转换为可以保存或传输的格式的过程。Java提供了多种序列化的方式,以满足不同的需求和场景。

1.实现Serialization接口:

  • 特点:这是Java默认的序列化方式,通过实现java.io.Serialization接口来标记一个类可以被序列化。这种序列化是隐式的,会自动序列化所有非static和transient关键字修饰的成员变量。
  • 使用场景:适用于大多数需要序列化的场景,特别是当对象的状态需要被持久化或在网络间传输时。

2.实现Externalizable接口:

  • 特点:与Serialization接口类似,但Externalizable接口提供了更高的灵活性。实现该类接口的类必须手动实现writeExternal()和readExternal()方法,以控制序列化和反序列化的过程。
  • 使用场景:适用于需要精确控制序列化过程的场景,比如只序列化对象的某些部分,或者需要对序列化数据进行加密等。

3.使用JSON序列化库:

  • 常用库:如Jackson,Gson等。
  • 特点:将对象转换为JSON格式的字符串进行序列化,可以跨语言,跨平台进行数据交换。JSON格式易于阅读和编写,也易于机器解析和生成。
  • 使用场景:适用于需要与其它语言或系统交互的场景,或者当对象需要被存储为文本格式时。

4.使用XML序列化库:

  • 常用库:如JAXN,XStream等。
  • 特点:将对象序列化为二进制格式进行传输和存储,效率较高。这些库通常提供了丰富的数据结构和高效的编码解码算法。
  • 使用场景:适用于对性能要求较高的场景,比如需要频繁序列化和反序列化大量数据的系统。

5.其他序列化方式:

  • Hessian序列化:Hessian是一种通过网络传输和存储Java对象的二进制格式,可以跨语言,跨平台,效率较高。
  • Kryo序列化:Kryo是一种高性能的Java序列化库,序列化和反序列化速度都很快,但只能用于Java环境。

Java提供了多种序列化方式,每种方式都有其特点和适用场景。在实际应用中,可以根据具体需求和场景选择合适的方式进行Java对象的序列化。需要注意的是,除了Java默认的序列化方式外,其他方式通常需要引入相应的第三方库。

四.Java默认序列化与反序列化示例

我们先将一些Java的变量和一个Car对象序列化到 d盘的一个名为 data.dat 的文件中。

/**
 * ObjectOutputStream 的使用,完成数据的序列化
 * ObjectOutputStream 是序列化
 */
public class ObjectOutputStreamDemo {

    public static void main(String[] args) throws IOException {
        //序列化后,保存到文本格式不是纯文本txt,而是按照它的格式来保存
        String filePath = "d:\\data.dat";

        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filePath));
        oos.writeInt(100);//int -> Integer(实现了Serializable)
        oos.writeBoolean(true);//boolean -> Boolean(实现了 Serializable)
        oos.writeChar('o');//char -> Character(实现了Serializable)
        oos.writeDouble(4.4);//double -> Double(实现了 Serializable)
        oos.writeUTF("对象输出流");//String (实现了Serializable)
        oos.writeObject(new Car("xiaomi su7","Pro","Gulf Blue","300,000"));
        oos.close();
        System.out.println("数据以序列化的形式保存完成");
        /*
        如果Car没有实现 Serializable接口,会报错如下:
        Exception in thread "main" java.io.NotSerializableException: org.example.io.demo.lession20.Car
        	at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184)
	        at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348)
	        at org.example.io.demo.lession20.ObjectOutputStreamDemo.main(ObjectOutputStreamDemo.java:24)
         */
    }
}

@Data
class Car implements Serializable {
    private String name;
    private String type;
    private static String color ;
    private transient String price ;

    public Car(String name,String type,String color,String price){
        this.name = name;
        this.type = type;
        this.color = color;
        this.price = price;
    }
}

执行main方法后,会在d盘根目录下看到一个 data.dat的文件。

下面我们将这个文件反序列化至程序中并打印出来:

/**
 * 读取序列化文件,并通过反序列化恢复数据
 * ObjectInputStream 是反序列化
 */
public class ObjectInputStreamDemo {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        //指定反序列化的文件
        String filePath = "d:\\data.dat";
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filePath));
        //读取(即反序列化)的顺序需要和保存数据(即序列化)的顺序一致,否则会有异常
        System.out.println(ois.readInt());
        System.out.println(ois.readBoolean());
        System.out.println(ois.readChar());
        System.out.println(ois.readDouble());
        System.out.println(ois.readUTF());
        System.out.println(ois.readObject().toString());
        ois.close();
    }
}

输出结果为:

100
true
o
4.4
对象输出流
Car(name=xiaomi su7, type=Pro, price=null)

Java序列化和反序列化注意事项:

  • 读写顺序要一致。
  • 序列化和反序列化对象要实现Serialization接口。
  • 序列化中的类中,建议添加SerialVersionUID,为了提高版本的兼容性。
  • 序列化对象时,默认将里面所有的属性都进行序列化,除了stati,transient修饰的成员。
  • 序列化对象时,要求里面属性的类型也需要实现序列化接口。
  • 序列化具备可继承性,也就是如果某类已经了序列化,则它的所有子类也默认实现了序列化。

五.static 和 transient 在序列化中的区别

static关键字:

  • 定义:static关键字用于修饰类的成员变量和方法,使其成为类变量或类方法,而不是实例变量或实例方法。类变量属于类本身,而非类的任何特定实例。
  • 序列化行为:在序列化过程中,被static修饰的变量(即类变量)不会被序列化。这是因为static变量是类级别的,他们不属于任何特定的对象实例,而是由所有的实例共享。因此,序列化一个对象时,不会包含其类变量的值,因为类变量的值在JVM中是全局的,与特定的序列化实例无关。
  • 反序列化影响:饭序列化一个对象时,被static修饰的变量会保持其在JVM中的当前值,而不是从序列化数据中恢复。这是因为静态变量是全局的,不受单个对象实例的序列化/反序列化过程影响。

Transient关键字:

  • 定义:transient关键字用于修饰类的成员变量,以指示改变量在序列化时不应被序列化。
  • 序列化行为:当一个对象被序列化时,所有非transient修饰的成员变量都会被序列化,而transient修饰的变量则会被忽略。这意味着,在序列化后的数据中,不会包含任何被transieent修饰的变量的值。
  • 反序列化影响:在序列化过程中,被transient修饰的变量将不会被恢复其序列化前的值。相反,它们会被初始化位默认值(例如,数值类型的变量会被初始化为0或false,对象类型的变量会被初始化为null)。因此,开发者需要在反序列化后,根据需要手动为这些变量赋值。
关键字 定义 序列化行为 反序列化影响
static 修饰类变量或类方法,属于类本身 不被序列化 保持JVM中的当前值
transient 修饰成员变量,指示在序列化时忽略该变量 被忽略,不被序列化 被初始化为默认值

标签:Java,变量,对象,static,序列化,String
From: https://www.cnblogs.com/chenlei210162701002/p/18432401

相关文章

  • javaScript 值的比较
    值的比较值的比较是指判断两个数的大小,返回一个布尔值。  比较运算符列表:   大于>  小于<  大于等于>= 小于等于<= 等于== 严格等于===不进行类型转换不等于!= 严格不等于!==不进行类型转换 字符串比较大小字符串间的比较大小遵循以下规则:1比较字符串首字母的大小。......
  • JavaScript中if嵌套 assert
    在JavaScript中,通常我们不会直接使用assert这个词,因为JavaScript标准库中并没有直接提供assert函数(尽管在一些测试框架如Jest、Mocha中经常看到)。但是,我们可以模拟一个assert函数的行为,即当某个条件不满足时抛出一个错误。结合if语句进行嵌套判断时,可以在每个需要断言的地方调用这......
  • 【Java】JVM垃圾收集器深入解析:原理与实践
    目录一、判断对象是否存活1.引用计数算法2.可达性计数算法3.Java中的四种引用 3.1强引用(StrongReference)3.2软引用(SoftReference)3.3弱引用(WeakReference)3.4虚引用(PhantomReference)3.5小结二、垃圾收集算法1.分代收集理论1.1分代存储1.2分......
  • 04 JSON 序列化 反序列化
    `#!/usr/bin/envpython#-*-coding:utf-8-*-#@File:json字符串-序列化-反序列化.py#@Author:jhchena#@Date:2024/8/18#@Desc:#@Contact:[email protected]=[11,22,33,44]#python转换成json格式-序列化dic_string=......
  • JavaScript中if嵌套assert的方法
    在JavaScript中,通常我们不会直接使用assert这个词,因为JavaScript标准库中并没有直接提供assert函数(尽管在一些测试框架如Jest、Mocha中经常看到)。但是,我们可以模拟一个assert函数的行为,即当某个条件不满足时抛出一个错误。结合if语句进行嵌套判断时,可以在每个需要断言的地方调用这......
  • java项目发布后到Tomcat时,总是带一层路径解决方案
    java项目发布后到Tomcat时,总是带一层路径参考文章:java线上项目访问项目会多一层项目根路径根据参考文章写的这篇文章,部分文章细节有完善和改动在JavaWeb应用中,当你把应用发布到Tomcat时,如果应用的web.xml配置文件中的<context-root>元素被设置成了非根路径,或者你......
  • Java毕业设计:基于Springboo律师事务所预约网站毕业设计源代码作品和开题报告怎么写
     博主介绍:黄菊华老师《Vue.js入门与商城开发实战》《微信小程序商城开发》图书作者,CSDN博客专家,在线教育专家,CSDN钻石讲师;专注大学生毕业设计教育和辅导。所有项目都配有从入门到精通的基础知识视频课程,学习后应对毕业设计答辩。项目配有对应开发文档、开题报告、任务书、P......
  • Java毕业设计:基于Springboot网球场地预约网站管理系统毕业设计源代码作品和开题报告怎
     博主介绍:黄菊华老师《Vue.js入门与商城开发实战》《微信小程序商城开发》图书作者,CSDN博客专家,在线教育专家,CSDN钻石讲师;专注大学生毕业设计教育和辅导。所有项目都配有从入门到精通的基础知识视频课程,学习后应对毕业设计答辩。项目配有对应开发文档、开题报告、任务书、P......
  • java中的接口
    接口表示一个类额外功能的实现,其作用是为了降低耦合。接口的使用注意事项1.接口中只能存在抽象方法,jvm默认会在方法前使用publicabstract进行修饰,刚学java推荐加上2.类和接口是实现关系可以通过关键字implements实现接口3.当一个具体的类实现一个接口的时候,......
  • JAVA基础:lock锁底层机制
    目录lock锁底层机制乐观锁lock锁底层机制lock锁底层使用的是CAS+AQS,在lock底层有一个计数器,记录锁被获取的状态,起初为0,当被抢占的时候变为1当我们调用lock.lock()方法,就是将状态从0改为1的过程。当我们调用lock,unlock()方法时,就是将状态从1改为0的过程当我们调用......