TCP流数据编组异常排查:子类继承引发NullPointerException
解决TCP流编组时的NullPointerException问题
看起来你遇到的核心问题是子类定义的私有字段没有正确初始化父类的同名私有字段,导致父类的getBytes()方法访问identifier时触发空指针异常。让我一步步帮你拆解和解决:
问题根源分析
你在MyWireFormat里定义了private String identifier = "here is my custom wireformat";,但Java中私有成员变量不会被继承——这个identifier是子类自己的独立字段,和父类WriteFormatWidget里的private String identifier完全是两个东西。父类的getBytes()方法只会访问自己的私有identifier,而它从始至终都是null,自然会抛出NullPointerException。
具体解决方案
方案1:给父类添加构造器(推荐)
修改WriteFormatWidget,添加一个构造器来初始化所有私有字段,这样子类可以通过调用父类构造器来给父类的字段赋值:
public class WriteFormatWidget { private int type; private long timestamp; private String identifier; private int tracker; // 添加构造器 public WriteFormatWidget(int type, long timestamp, String identifier, int tracker) { this.type = type; this.timestamp = timestamp; this.identifier = identifier; this.tracker = tracker; } // 原有的getBytes方法保持不变 public byte[] getBytes() throws IOException { byte[] marshalledBytes = null; ByteArrayOutputStream baOutputStream = new ByteArrayOutputStream(); DataOutputStream dout = new DataOutputStream(new BufferedOutputStream(baOutputStream)); dout.writeInt(type); dout.writeLong(timestamp); System.out.println("getBytes using identifier: " + identifier); byte[] identifierBytes = identifier.getBytes(); int elementLength = identifierBytes.length; dout.writeInt(elementLength); dout.write(identifierBytes); dout.writeInt(tracker); dout.flush(); marshalledBytes = baOutputStream.toByteArray(); baOutputStream.close(); dout.close(); return marshalledBytes; } }
然后在子类MyWireFormat中,通过父类构造器传入你要的identifier值:
public class MyWireFormat extends WriteFormatWidget implements Event { // 不需要再定义子类自己的identifier了 public MyWireFormat(int type, long timestamp, int tracker) { // 调用父类构造器,传入硬编码的identifier super(type, timestamp, "here is my custom wireformat", tracker); } }
方案2:给父类添加Setter方法(如果不能修改构造器)
如果因为某些原因不能修改父类的构造器,可以给WriteFormatWidget添加setIdentifier等Setter方法:
public class WriteFormatWidget { // ... 原有字段 public void setIdentifier(String identifier) { this.identifier = identifier; } public void setType(int type) { this.type = type; } public void setTimestamp(long timestamp) { this.timestamp = timestamp; } public void setTracker(int tracker) { this.tracker = tracker; } // ... 原有getBytes方法 }
然后在创建MyWireFormat对象后,调用Setter初始化字段:
MyWireFormat wireFormat = new MyWireFormat(); wireFormat.setType(1); // 根据你的消息类型设置具体值 wireFormat.setTimestamp(System.currentTimeMillis()); // 用当前时间戳填充 wireFormat.setIdentifier("here is my custom wireformat"); wireFormat.setTracker(0); // 根据业务逻辑设置tracker值
额外注意事项
- 除了
identifier,也要确保type、timestamp、tracker这几个字段都被正确初始化,不然后续编组时也可能出现数据错误或者其他异常。 - 你的解组逻辑要严格对应
getBytes()里的写入顺序:int type→long timestamp→int identifier长度→byte[] identifier→int tracker,否则解组后的数据会混乱。
内容的提问来源于stack exchange,提问作者Cit5
相关产品推荐
相关产品推荐

