Java模拟项目CSVHelper写入时循环中途触发NullPointerException问题
问题分析与解决方案
看起来你已经摸到了问题的核心——null值引发的空指针异常,再加上Writer的缓冲机制,才导致了CSV写入中断、最后一条记录不完整的情况。我来帮你把修复方案拆解清楚:
1. 为什么会出问题?
当异常没有消息时,e.getMessage()会返回null,这个null被加入到了日志列表里。等到CSVHelper.writeLine处理这个null值时,调用val.length()直接触发了NullPointerException。而因为异常是在写入过程中抛出的,Writer的缓冲区还没来得及把所有缓存的内容刷进文件就中断了,所以最后一条记录只写了一半。
2. 具体修复步骤
方法一:在生成CSV行时提前处理null值
最直接的方式是在把字段加入row列表时,就把null转成空字符串,从源头避免问题。修改saveData里的日志内容添加逻辑:
// 原代码:row.add(entry.getLog()); // 修改为: row.add(entry.getLog() == null ? "" : entry.getLog());
顺便检查下其他可能出现null的字段(比如getMessageType()如果也有返回null的风险,也做同样处理),确保所有要写入CSV的内容都是非null的字符串。
方法二:给CSVHelper加一层null防护
如果不想改动业务代码,也可以在CSVHelper.writeLine里提前处理null值,让工具类更健壮:
public static void writeLine(Writer w, ArrayList<String> values) throws Exception { boolean firstVal = true; for (String val : values) { if (!firstVal) { w.write(","); } // 先把null转为空字符串,避免后续调用length()报错 String safeVal = val == null ? "" : val; w.write("\""); for (int i=0; i<safeVal.length(); i++) { char ch = safeVal.charAt(i); if (ch=='\"') { w.write("\""); // 转义引号 } w.write(ch); } w.write("\""); firstVal = false; } w.write("\n"); }
方法三:异常时确保缓冲区被刷新
为了避免异常发生时文件内容残缺,建议给saveData方法加上try-catch-finally块,确保无论是否出错,Writer都能被正确刷新和关闭:
public static void saveData(List<TimeLog> data, String fileName, Simulator simulator, ArrayList<String> header) throws Exception { File csvFile = new File(simulator.getOutputFolder()+fileName); FileOutputStream fos = new FileOutputStream(csvFile); Writer fw = new OutputStreamWriter(fos, "UTF-8"); try { CSVHelper.writeLine(fw, header); for (TimeLog entry : data) { ArrayList<String> row = new ArrayList<>(); row.add(String.valueOf(entry.getTime())); row.add(String.valueOf(entry.getMessageType())); row.add(entry.getEvent() == null ? "" : entry.getEvent().getTypeName()); // 处理null日志内容 row.add(entry.getLog() == null ? "" : entry.getLog()); CSVHelper.writeLine(fw, row); } fw.flush(); } catch (Exception e) { // 异常时先尝试刷新缓冲区,尽量保留已写入的内容 try { fw.flush(); } catch (Exception flushEx) { flushEx.printStackTrace(); } e.printStackTrace(); throw e; } finally { // 确保Writer最终被关闭,释放资源 try { fw.close(); } catch (Exception closeEx) { closeEx.printStackTrace(); } } }
3. 额外小建议
- 对于可能返回null的方法(比如
e.getMessage()),在业务逻辑里就提前处理成空字符串,减少下游代码的异常风险。 - 如果用的是Java 8及以上版本,可以用
Optional让代码更简洁:row.add(Optional.ofNullable(entry.getLog()).orElse(""))。
内容的提问来源于stack exchange,提问作者MRommers
相关产品推荐
相关产品推荐

