Java中用ObjectInputStream无法反序列化含嵌套对象的对象的解决方案
解决包含嵌套对象的Java网络对象传输问题
原问题根源
你的People类实现了Serializable,但嵌套的Player类未实现该接口,直接使用ObjectInputStream/ObjectOutputStream序列化时会抛出NotSerializableException,这是反序列化失败的核心原因。如果不想依赖Java原生序列化,以下是几种可靠的替代方案:
方案1:让嵌套类实现Serializable(原生直接修复)
这是最省事的修复方式,不需要改动传输逻辑,只需给Player类添加Serializable接口:
public class Player implements Serializable { // 建议显式声明序列化ID,避免类结构变化导致反序列化失败 private static final long serialVersionUID = 123456789L; private String name; // 构造方法、getter等原有逻辑保留 }
方案2:JSON序列化(通用跨语言方案)
将对象序列化为JSON字符串传输,接收端再反序列化为对象,无需依赖Serializable接口,可读性强且支持跨语言。常用库有Jackson、Gson,以下是Jackson的示例:
- 添加Maven依赖:
<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.15.2</version> </dependency>
- 修改发送端(ServerMulticast)的序列化逻辑:
// 替换原ObjectOutputStream相关代码 ObjectMapper mapper = new ObjectMapper(); String jsonStr = mapper.writeValueAsString(p1); byte[] data = jsonStr.getBytes(StandardCharsets.UTF_8); // 后续DatagramPacket发送逻辑不变
- 修改接收端(ClientMulticast)的反序列化逻辑:
// 替换原ObjectInputStream相关代码 String jsonStr = new String(packet.getData(), 0, packet.getLength(), StandardCharsets.UTF_8); ObjectMapper mapper = new ObjectMapper(); People readObject = mapper.readValue(jsonStr, People.class);
方案3:Protobuf序列化(高性能二进制方案)
Protobuf是Google推出的二进制序列化协议,体积小、传输效率高,适合游戏这类对性能敏感的场景。
- 定义
.proto描述文件:
syntax = "proto3"; message Player { string name = 1; // 按需添加Player的其他字段 } message People { string ip = 1; Player player = 2; }
- 用Protobuf编译器生成Java类,之后修改传输逻辑:
- 发送端:
// 将自定义People对象转换为Protobuf生成的对象 PeopleProto.People protoPeople = PeopleProto.People.newBuilder() .setIp(p1.getIp()) .setPlayer(PeopleProto.Player.newBuilder().setName(p1.getPlayer().getName()).build()) .build(); byte[] data = protoPeople.toByteArray(); // 发送DatagramPacket
- 接收端:
// 解析二进制数据为Protobuf对象,再转换为自定义People对象 PeopleProto.People protoPeople = PeopleProto.People.parseFrom(packet.getData(), 0, packet.getLength()); Player player = new Player(protoPeople.getPlayer().getName()); People readObject = new People(player, protoPeople.getIp());
方案4:手动序列化/反序列化(完全自定义)
自己实现对象与字节数组的转换逻辑,完全控制序列化过程,适合字段简单的对象:
在People类中添加序列化、反序列化方法:
public byte[] toBytes() throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); DataOutputStream dos = new DataOutputStream(baos); // 按顺序写入字段 dos.writeUTF(ip); dos.writeUTF(player.getName()); dos.close(); return baos.toByteArray(); } public static People fromBytes(byte[] data) throws IOException { ByteArrayInputStream bais = new ByteArrayInputStream(data); DataInputStream dis = new DataInputStream(bais); // 按写入顺序读取字段 String ip = dis.readUTF(); String playerName = dis.readUTF(); dis.close(); return new People(new Player(playerName), ip); }
发送端调用p1.toBytes()获取字节数组,接收端调用People.fromBytes(packet.getData())还原对象即可。
内容的提问来源于stack exchange,提问作者Akashiro
相关产品推荐
相关产品推荐

