面试题答案
一键面试选择合适的序列化框架
- 原理:
- Kryo:Kryo是一个快速高效的Java序列化框架。它采用了二进制编码,并且在序列化过程中会为对象分配唯一的ID,后续序列化相同类型对象时,只需写入ID,从而减少序列化后的数据量。同时,它对集合等复杂数据结构有较好的优化,能快速处理嵌套对象和集合属性。
- Protobuf:Protocol Buffers(简称Protobuf)是Google开发的一种轻便高效的结构化数据存储格式。它通过定义数据结构的描述文件(
.proto
),生成对应的Java代码。在序列化时,按照定义的结构紧凑地编码数据,反序列化时也能快速解析,极大地提高了序列化和反序列化的速度,并且生成的字节码非常小,适合网络传输。
- 实施步骤:
- Kryo:
- 引入依赖:在
pom.xml
中添加Kryo的依赖:
- 引入依赖:在
- Kryo:
<dependency>
<groupId>com.esotericsoftware</groupId>
<artifactId>kryo</artifactId>
<version>5.1.0</version>
</dependency>
<dependency>
<groupId>com.esotericsoftware</groupId>
<artifactId>minlog</artifactId>
<version>1.3.0</version>
</dependency>
- 注册类:在使用Kryo序列化`Order`对象前,需要注册`Order`及其内部嵌套的自定义对象类。例如:
Kryo kryo = new Kryo();
kryo.register(Order.class);
kryo.register(NestedObject1.class);
// 注册所有嵌套的自定义对象类
- 序列化与反序列化:
Output output = new Output(new FileOutputStream("order.bin"));
kryo.writeObject(output, orderObject);
output.close();
Input input = new Input(new FileInputStream("order.bin"));
Order deserializedOrder = kryo.readObject(input, Order.class);
input.close();
- Protobuf:
- 定义
.proto
文件:假设Order
对象包含orderId
(int
类型)、customer
(自定义Customer
对象)和orderItems
(OrderItem
对象的集合)。在.proto
文件中定义如下:
- 定义
syntax = "proto3";
message Customer {
string name = 1;
int32 age = 2;
}
message OrderItem {
string itemName = 1;
double price = 2;
}
message Order {
int32 orderId = 1;
Customer customer = 2;
repeated OrderItem orderItems = 3;
}
- 生成Java代码:使用Protobuf编译器(`protoc`)根据`.proto`文件生成Java代码。在命令行中执行(假设`.proto`文件在`src/main/proto`目录下):
protoc --java_out=src/main/java src/main/proto/order.proto
- 序列化与反序列化:
Order.Builder orderBuilder = Order.newBuilder();
orderBuilder.setOrderId(1);
Customer.Builder customerBuilder = Customer.newBuilder();
customerBuilder.setName("John").setAge(30);
orderBuilder.setCustomer(customerBuilder.build());
OrderItem.Builder itemBuilder = OrderItem.newBuilder();
itemBuilder.setItemName("Product 1").setPrice(10.0);
orderBuilder.addOrderItems(itemBuilder.build());
Order order = orderBuilder.build();
ByteString byteString = order.toByteString();
Order deserializedOrder = Order.parseFrom(byteString);
优化对象结构
- 原理:
- 减少嵌套层次:过多的嵌套层次会增加序列化和反序列化的复杂度。通过扁平化对象结构,将嵌套对象的部分属性提升到外层对象,减少对象间的依赖关系,从而降低序列化时需要处理的层次,提高性能。
- 精简集合属性:对于集合属性,如果其中有些元素在序列化和反序列化过程中并非必需,或者可以通过其他方式重建,可以考虑在序列化时省略这些元素,在反序列化后再根据需要重建,这样能减少序列化的数据量。
- 实施步骤:
- 减少嵌套层次:
- 分析
Order
对象的嵌套结构,找出可以提升的属性。例如,如果NestedObject
中有一些经常使用且与Order
紧密相关的属性,如NestedObject
中的status
属性,可以将其提升到Order
对象中。 - 修改
Order
类的结构,添加提升后的属性,并调整相关的业务逻辑,确保功能不受影响。 - 在序列化和反序列化过程中,由于对象结构更扁平,操作会更简单高效。
- 分析
- 精简集合属性:
- 确定集合中可省略的元素。例如,
Order
中的orderItems
集合,如果其中有些OrderItem
对象在某些场景下不需要传输(如一些统计信息相关的OrderItem
,可以在接收端重新计算),可以在序列化时过滤掉这些元素。 - 在
Order
类中添加方法,用于在序列化前过滤集合元素,在反序列化后根据需要重建这些元素。例如:
- 确定集合中可省略的元素。例如,
- 减少嵌套层次:
public void filterOrderItemsForSerialization() {
List<OrderItem> filteredItems = new ArrayList<>();
for (OrderItem item : orderItems) {
if (item.isEssentialForSerialization()) {
filteredItems.add(item);
}
}
orderItems = filteredItems;
}
public void rebuildOrderItemsAfterDeserialization() {
// 根据业务逻辑重建被省略的OrderItem
}
在序列化前调用filterOrderItemsForSerialization
方法,在反序列化后调用rebuildOrderItemsAfterDeserialization
方法。