一、什么是序列化?什么是反序列化?
如果我们需要持久化 Java 对象比如将 Java 对象保存在文件中,或者在网络传输 Java 对象,这些场景都需要用到序列化。
简单来说:
序列化:将数据结构或对象转换成可以存储或传输的形式,通常是二进制字节流,也可以是 JSON, XML 等文本格式
反序列化:将在序列化过程中所生成的数据转换为原始数据结构或者对象的过程
对于 Java 这种面向对象编程语言来说,我们序列化的都是对象(Object)也就是实例化后的类(Class),但是在 C++这种半面向对象的语言中,struct(结构体)定义的是数据结构类型,而 class 对应的是对象类型。
下面是序列化和反序列化常见应用场景:
对象在进行网络传输(比如远程方法调用 RPC 的时候)之前需要先被序列化,接收到序列化的对象之后需要再进行反序列化;
将对象存储到文件之前需要进行序列化,将对象从文件中读取出来需要进行反序列化;
将对象存储到数据库(如 Redis)之前需要用到序列化,将对象从缓存数据库中读取出来需要反序列化;
将对象存储到内存之前需要进行序列化,从内存中读取出来之后需要进行反序列化。
维基百科是如是介绍序列化的:
序列化(serialization)在计算机科学的数据处理中,是指将数据结构或对象状态转换成可取用格式(例如存成文件,存于缓冲,或经由网络中发送),以留待后续在相同或另一台计算机环境中,能恢复原先状态的过程。依照序列化格式重新获取字节的结果时,可以利用它来产生与原始对象相同语义的副本。对于许多对象,像是使用大量引用的复杂对象,这种序列化重建的过程并不容易。面向对象中的对象序列化,并不概括之前原始对象所关系的函数。这种过程也称为对象编组(marshalling)。从一系列字节提取数据结构的反向操作,是反序列化(也称为解编组、deserialization、unmarshalling)。
综上:序列化的主要目的是通过网络传输对象或者说是将对象存储到文件系统、数据库、内存中。
https://www.corejavaguru.com/java/serialization/interview-questions-1
序列化协议对应于 TCP/IP 4 层模型的哪一层?
我们知道网络通信的双方必须要采用和遵守相同的协议。TCP/IP 四层模型是下面这样的,序列化协议属于哪一层呢?
- 应用层
- 传输层
- 网络层
- 网络接口层
TCP/IP 四层模型
如上图所示,OSI 七层协议模型中,表示层做的事情主要就是对应用层的用户数据进行处理转换为二进制流。反过来的话,就是将二进制流转换成应用层的用户数据。这不就对应的是序列化和反序列化么?
因为,OSI 七层协议模型中的应用层、表示层和会话层对应的都是 TCP/IP 四层模型中的应用层,所以序列化协议属于 TCP/IP 协议应用层的一部分。
二、某些字段不想进行序列化怎么办?
对于不想进行序列化的变量,使用 transient
关键字修饰。
transient
关键字的作用是:阻止实例中那些用此关键字修饰的的变量序列化;当对象被反序列化时,被 transient
修饰的变量值不会被持久化和恢复。
关于 transient
还有几点注意:
transient
只能修饰变量,不能修饰类和方法。transient
修饰的变量,在反序列化后变量值将会被置成类型的默认值。例如,如果是修饰int
类型,那么反序列后结果就是0
。static
变量因为不属于任何对象(Object),所以无论有没有transient
关键字修饰,均不会被序列化。
三、在Java中,如何对包含不想序列化字段的对象进行持久化存储?
- 使用数据库进行持久化存储
- 原理:将对象的属性映射到数据库表的列中,对于不想序列化的字段,可以在数据库存储过程中进行特殊处理,如使用加密算法存储敏感信息,或者直接忽略存储某些非关键字段。
- 步骤示例(以 Java JDBC 和 MySQL 为例):
- 创建数据库表:假设我们有一个
User
对象,其中password
字段不想序列化。首先创建一个数据库表来存储用户信息。
- 创建数据库表:假设我们有一个
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(255),
password VARCHAR(255)
);
- 定义 Java 类和持久化方法:
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
class User {
private String username;
private transient String password;
public User(String username, String password) {
this.username = username;
this.password = password;
}
public void saveToDatabase() {
try (Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb", "root", "password");
PreparedStatement preparedStatement = connection.prepareStatement("INSERT INTO users (username, password) VALUES (?,?)")) {
preparedStatement.setString(1, username);
// 对密码进行加密后存储(假设使用简单的哈希函数)
preparedStatement.setString(2, hashPassword(password));
preparedStatement.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}
}
private String hashPassword(String password) {
// 简单的哈希示例,实际应用中应该使用更安全的哈希算法
return Integer.toString(password.hashCode());
}
}
- 使用示例:
public class Main {
public static void main(String[] args) {
User user = new User("john", "secret");
user.saveToDatabase();
}
}
- 优点:可以利用数据库的强大功能,如事务处理、数据备份、索引等。并且可以灵活地处理不想序列化的字段,如加密存储敏感信息。
- 缺点:需要额外的数据库管理和配置工作,如安装数据库服务器、创建数据库和表等。并且对于复杂的对象关系,映射到数据库可能会比较复杂。
- 使用文件存储(不依赖序列化)
- 原理:以自定义的文本格式或二进制格式将对象的属性存储到文件中,对于不想序列化的字段,可以选择不写入文件或者以其他特殊方式处理。
- 步骤示例(以文本文件存储为例):
- 定义存储格式和持久化方法:假设我们有一个
Book
对象,其中publisherNotes
字段不想序列化。
- 定义存储格式和持久化方法:假设我们有一个
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
class Book {
private String title;
private String author;
private transient String publisherNotes;
public Book(String title, String author, String publisherNotes) {
this.title = title;
this.author = author;
this.publisherNotes = publisherNotes;
}
public void saveToFile(String filePath) {
try (BufferedWriter writer = new BufferedWriter(new FileWriter(filePath))) {
writer.write("Title: " + title + "\n");
writer.write("Author: " + author + "\n");
// 省略publisherNotes字段的存储
} catch (IOException e) {
e.printStackTrace();
}
}
}
- 使用示例:
public class Main {
public static void main(String[] args) {
Book book = new Book("Java in Depth", "John Doe", "Internal publisher notes");
book.saveToFile("book.txt");
}
}
- 优点:简单直接,不需要额外的数据库等复杂环境。对于小型应用或者简单的数据存储需求比较方便。
- 缺点:缺乏数据库的高级功能,如查询效率可能较低,数据安全性和完整性保障相对较弱。并且如果存储格式复杂,读写文件的代码可能会比较繁琐。
- 使用 NoSQL 数据库进行持久化存储
- 原理:NoSQL 数据库(如 MongoDB、Redis 等)有自己独特的存储方式,对于不想序列化的字段,可以根据数据库的特点进行处理。例如在 MongoDB 中,可以在插入文档时选择不包含某些字段。
- 步骤示例(以 MongoDB 为例):
- 添加 MongoDB 依赖并连接数据库(使用 MongoDB Java Driver):
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongodb - driver - sync</artifactId>
<version>4.9.1</version>
</dependency>
import com.mongodb.MongoClient;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
import org.bson.Document;
class Product {
private String name;
private transient int internalStockCode;
public Product(String name, int internalStockCode) {
this.name = name;
this.internalStockCode = internalStockCode;
}
public void saveToMongoDB() {
MongoClient mongoClient = new MongoClient("localhost", 27017);
MongoDatabase database = mongoClient.getDatabase("mydb");
MongoCollection<Document> collection = database.getCollection("products");
Document document = new Document("name", name);
// 不包含internalStockCode字段
collection.insertOne(document);
mongoClient.close();
}
}
- 使用示例:
public class Main {
public static void main(String[] args) {
Product product = new Product("Smartphone", 12345);
product.saveToMongoDB();
}
}
- 优点:NoSQL 数据库通常具有高可扩展性和灵活性,能够很好地处理非结构化或半结构化数据,对于一些复杂的对象模型或者大数据量存储有优势。
- 缺点:不同的 NoSQL 数据库有不同的查询语言和数据模型,需要学习和适应。并且在数据一致性等方面可能不如传统关系型数据库。