当前位置: 首页 > article >正文

Java基础夯实——1.7 序列化

序列化

序列化(Serialization)是将对象的状态转换为字节流的过程,方便将对象在网络中传输或存储在磁盘中。反序列化(Deserialization)则是将字节流恢复为原始对象的过程。Java通过java.io.Serializable接口来实现序列化。

1. Serializable接口

要使一个Java对象能够被序列化,该对象的类必须实现Serializable接口。这个接口是一个标记接口,表示该类的对象可以被序列化,但没有任何方法需要实现。

import java.io.Serializable;

public class Person implements Serializable {
    private String name;
    private int age;

    // 构造函数、getter、setter等
}

2. 序列化的流程

  1. 序列化对象:使用ObjectOutputStream将对象转换为字节流并写入文件或网络。
  2. 反序列化对象:使用ObjectInputStream从字节流恢复对象。

示例:对象序列化

import java.io.*;

public class SerializeExample {
    public static void main(String[] args) {
        Person person = new Person("Alice", 30);
        try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("person.ser"))) {
            out.writeObject(person);
            System.out.println("对象已序列化");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

示例:对象反序列化

import java.io.*;

public class DeserializeExample {
    public static void main(String[] args) {
        try (ObjectInputStream in = new ObjectInputStream(new FileInputStream("person.ser"))) {
            Person person = (Person) in.readObject();
            System.out.println("反序列化后的对象:" + person.getName() + ", " + person.getAge());
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

3. 序列化的注意事项

  • UID(序列化版本ID):为了确保反序列化时类的兼容性,可以定义一个serialVersionUID字段。这个字段是一个静态常量,用于验证类版本的一致性。如果在反序列化时发现serialVersionUID不一致,会抛出InvalidClassException

  • transient关键字:如果某个字段不需要序列化,可以使用transient关键字进行标记。这样,序列化时该字段会被忽略。

  • 父类的序列化:如果一个对象包含父类字段,父类必须也实现Serializable接口,否则在序列化时会抛出java.io.NotSerializableException异常。

  • 序列化的性能问题:序列化和反序列化操作可能比较消耗性能,尤其是对于大对象。为了优化性能,可以考虑使用自定义的序列化方法,或者使用更高效的序列化框架(如Google的Protobuf或Apache Avro)。

4. 自定义序列化

如果希望控制对象的序列化过程,可以重写writeObjectreadObject方法。

private void writeObject(ObjectOutputStream out) throws IOException {
    out.defaultWriteObject();
    // 自定义序列化逻辑
}

private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
    in.defaultReadObject();
    // 自定义反序列化逻辑
}

5. 序列化的应用场景

  • 对象持久化:将Java对象保存到文件系统中,以便后续加载。
  • 远程通信:通过网络将对象传输到远程计算机(如RMI)。
  • 缓存:将对象序列化存储到缓存中(如Redis),以加速应用程序。
  • 对象复制:序列化和反序列化可以用于深度复制对象。

介绍三种常见的序列化方法:

  1. Java原生序列化 (java.io.Serializable)
  2. JSON序列化(例如使用JacksonGson
  3. Protobuf序列化(Protocol Buffers)

1. Java原生序列化 (java.io.Serializable)

简介

Java的原生序列化机制通过实现Serializable接口,允许Java对象转换为字节流并保存或传输。反序列化则是将字节流重新转换为原始的Java对象。这个机制由java.io.ObjectOutputStreamjava.io.ObjectInputStream提供支持。

示例

序列化示例:

import java.io.*;

public class JavaSerializationExample {
    public static void main(String[] args) {
        Person person = new Person("John", 25);

        // 序列化对象到文件
        try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("person.ser"))) {
            out.writeObject(person);
            System.out.println("对象已序列化");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

反序列化示例:

import java.io.*;

public class JavaDeserializationExample {
    public static void main(String[] args) {
        // 反序列化从文件读取对象
        try (ObjectInputStream in = new ObjectInputStream(new FileInputStream("person.ser"))) {
            Person person = (Person) in.readObject();
            System.out.println("反序列化后的对象: " + person.getName() + ", " + person.getAge());
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

2. JSON序列化(使用Jackson或Gson)

简介

JSON序列化是将Java对象转换为JSON格式的过程,JSON是一种轻量级的数据交换格式,易于阅读和编写。常用的JSON库包括JacksonGson。这种方式特别适合需要与前端或其他语言(如JavaScript、Python)进行数据交换的场景。

使用示例(使用Jackson)

序列化示例:

import com.fasterxml.jackson.databind.ObjectMapper;

public class JacksonSerializationExample {
    public static void main(String[] args) throws Exception {
        Person person = new Person("Alice", 30);

        // 创建Jackson的ObjectMapper
        ObjectMapper mapper = new ObjectMapper();
        String json = mapper.writeValueAsString(person);  // 将对象转为JSON字符串
        System.out.println("JSON: " + json);
    }
}

反序列化示例:

import com.fasterxml.jackson.databind.ObjectMapper;

public class JacksonDeserializationExample {
    public static void main(String[] args) throws Exception {
        String json = "{\"name\":\"Alice\",\"age\":30}";

        // 创建Jackson的ObjectMapper
        ObjectMapper mapper = new ObjectMapper();
        Person person = mapper.readValue(json, Person.class);  // 将JSON字符串转回对象
        System.out.println("反序列化后的对象: " + person.getName() + ", " + person.getAge());
    }
}

使用注意事项

  • 性能:相比Java原生序列化,JSON序列化和反序列化通常较快,并且可以跨平台使用,尤其适合需要进行数据交换的场景。
  • 数据大小:JSON格式通常比Java的原生序列化更加紧凑和轻量级,但相比其他二进制格式如Protobuf,JSON的体积通常较大。
  • 库选择Jackson是功能强大且广泛使用的库,支持各种高级功能,如自定义序列化、日期格式化、字段忽略等。而Gson是一个轻量级的JSON库,使用简单,适合简单的JSON序列化任务。

3. Protobuf序列化(Protocol Buffers)

简介

Protocol Buffers(简称Protobuf)是由Google开发的语言中立、平台中立、可扩展的序列化机制。它用于将结构化数据序列化为紧凑的二进制格式,适用于高效的网络传输和存储。

使用示例

Protobuf序列化需要首先定义一个.proto文件,描述数据的结构。

定义Protobuf数据结构:

syntax = "proto3";

message Person {
    string name = 1;
    int32 age = 2;
}

然后使用Protobuf编译器生成Java类。

序列化示例:

import com.example.protobuf.PersonOuterClass.Person;
import java.io.*;

public class ProtobufSerializationExample {
    public static void main(String[] args) throws IOException {
        // 创建一个Person对象
        Person person = Person.newBuilder().setName("Bob").setAge(40).build();

        // 序列化到文件
        try (FileOutputStream fos = new FileOutputStream("person.pb")) {
            person.writeTo(fos);
            System.out.println("对象已序列化");
        }
    }
}

反序列化示例:

import com.example.protobuf.PersonOuterClass.Person;
import java.io.*;

public class ProtobufDeserializationExample {
    public static void main(String[] args) throws IOException {
        // 从文件反序列化
        try (FileInputStream fis = new FileInputStream("person.pb")) {
            Person person = Person.parseFrom(fis);
            System.out.println("反序列化后的对象: " + person.getName() + ", " + person.getAge());
        }
    }
}

注意事项

  • 效率和体积:Protobuf提供了最紧凑的二进制序列化格式,特别适用于低带宽网络通信或需要大规模数据传输的场景。它在数据传输的效率和存储空间的占用上都比JSON和Java原生序列化要优秀。
  • 跨语言支持:Protobuf支持多种编程语言,包括Java、C++、Python等,非常适合需要多语言环境的应用。
  • 学习成本:与JSON和Java原生序列化相比,Protobuf需要先定义.proto文件,并通过工具生成代码,这增加了使用的复杂度。
  • 版本兼容性:Protobuf支持强大的版本管理机制,可以通过字段编号来保持不同版本之间的兼容性。

总结

序列化方法优点缺点
Java原生序列化简单易用,Java原生支持性能差,文件体积大,不适合跨平台或远程通信
JSON序列化轻量级,跨语言,广泛应用,易于调试相比Protobuf体积大,效率较低,适用于简单数据交换
Protobuf序列化高效、紧凑、跨语言,适合大规模数据交换或网络通信使用相对复杂,需要定义.proto文件

常见问题

1. 什么是Java序列化?如何实现Java序列化?

Java序列化是将Java对象的状态转换为字节流的过程,从而能够存储或传输对象。反序列化是将字节流恢复为对象的过程。

实现方法

  • 要实现Java序列化,类需要实现java.io.Serializable接口。
  • 使用ObjectOutputStream进行对象序列化,ObjectInputStream进行反序列化。

示例:

public class Person implements Serializable {
    private String name;
    private int age;
}

序列化:

ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("person.ser"));
out.writeObject(person);

反序列化:

ObjectInputStream in = new ObjectInputStream(new FileInputStream("person.ser"));
Person person = (Person) in.readObject();

2. 除了Java自带序列化,还有哪些常见序列化框架?

常见的序列化框架包括:

  • JSON序列化:如JacksonGson,用于将Java对象转换为JSON格式,广泛用于Web应用和跨语言通信。
  • Protocol Buffers (Protobuf):由Google开发,支持高效的二进制序列化,适用于大规模数据传输和跨语言系统。
  • Avro:由Apache开发,适用于大数据场景,支持跨语言和模式管理。
  • Kryo:高效的Java序列化框架,适用于高性能应用。

3. ProtoBuf如何使用?该序列化框架特性是什么?

使用方法

  1. 定义.proto文件,描述数据结构:

    syntax = "proto3";
    message Person {
        string name = 1;
        int32 age = 2;
    }
    
  2. 使用Protobuf编译器生成Java类。

  3. 在代码中序列化和反序列化:

    Person person = Person.newBuilder().setName("John").setAge(30).build();
    FileOutputStream fos = new FileOutputStream("person.proto");
    person.writeTo(fos);
    

特性

  • 高效的二进制格式,比JSON更紧凑。
  • 支持跨语言(Java、C++、Python等)。
  • 易于版本控制,可以在不破坏旧版应用的情况下修改数据结构。

4. 如果不想对某些字段进行序列化应该如何实现?

在Java中,可以使用transient关键字标记不需要序列化的字段:

private transient String password;

这样,password字段在序列化时会被忽略,不会存储到字节流中。

5. serialVersionUID是什么?有什么作用?

**serialVersionUID**是一个静态常量,用于验证类的版本一致性。它是序列化和反序列化时,确保类结构兼容的标识符。

  • 作用:在反序列化时,JVM会检查序列化流中的serialVersionUID与类中的`serialVe

http://www.kler.cn/a/397980.html

相关文章:

  • 023、ELK 从入门到实践
  • Cartographer激光雷达slam -20241116
  • JS学习日记(jQuery库)
  • 环境贴图选用方式
  • 【日常记录-Git】git log
  • Neo4j Desktop 和 Neo4j Community Edition 区别
  • Py之pymupdf:基于langchain框架结合pymupdf库实现输出每个PDF页面的文本内容、元数据等
  • 快速上手STL中list的使用
  • 新日撸java三百行` 新手小白java学习记录 `Day1
  • thinkphp 连表查询
  • 【大数据学习 | flume】flume之常见的sink组件
  • 解析 Android WebChromeClient:提升 WebView 用户体验的关键组件
  • RabbitMQ入门:“Hello World!“ 教程(一)
  • Uniapp踩坑input自动获取焦点ref动态获取实例不可用
  • docker启动训练容器教程
  • html数据类型
  • 【项目设计技巧】客户端SDK的开发
  • Linux驱动开发——pinctrl 和 和 gpio 子系统实验
  • 前缀和算法习题篇(上)
  • 【点云上采样】最近邻插值上采样算法 增加点云密度
  • C++ 编程基础(5)类与对象 | 5.8、面向对象五大原则
  • vue3中将在线url地址转为图片显示方法教程
  • RabbitMQ 通道(Channel)详解:方法使用、消息确认与拒绝
  • 零基础怎么开始学网络安全(非常详细)零基础入门到精通
  • Mac Java 使用 tesseract 进行 ORC 识别
  • [Qt] Qt删除文本文件中的某一行