本文目录导读:

这是一个典型的 Java Swing 中 JEditorPane 配合 HTMLEditorKit 解析 HTML 时出现的 NegativeArraySizeException(负数组大小异常) 问题。
问题原因
这个异常通常是由于 HTML 解析器在解析包含特定格式错误的 HTML 内容时,计算出错误的数组长度 导致的。
- HTML 内容中包含过大的数字或特殊字符(如表格
colspan、rowspan、width等属性值异常) - 解析器在计算数组大小时得到负数(如
new int[-1]) - HTML 标签嵌套不正确 或 属性值格式异常
解决方案
验证并清理 HTML 内容
public class SafeHtmlEditorPane extends JEditorPane {
public void setSafeHtml(String html) {
try {
// 清理可能导致问题的属性值
String cleanedHtml = cleanProblematicAttributes(html);
this.setContentType("text/html");
this.setText(cleanedHtml);
} catch (NegativeArraySizeException e) {
System.err.println("HTML解析错误,使用备用显示方式");
this.setText("内容格式错误,无法正确显示");
}
}
private String cleanProblematicAttributes(String html) {
// 替换异常的colspan/rowspan值
html = html.replaceAll("colspan=\"(\\d+)\"", match -> {
String numStr = match.replaceAll("[^0-9]", "");
int num = Integer.parseInt(numStr);
if (num < 1 || num > 100) {
return "colspan=\"1\""; // 替换为安全值
}
return match;
});
// 清除超大width/height值
html = html.replaceAll("width=\"(\\d+)\"", match -> {
String numStr = match.replaceAll("[^0-9]", "");
int num = Integer.parseInt(numStr);
if (num < 0 || num > 10000) {
return "width=\"100\""; // 默认安全值
}
return match;
});
return html;
}
}
使用自定义 HTMLEditorKit
public class SafeHTMLEditorKit extends HTMLEditorKit {
@Override
public Document createDefaultDocument() {
HTMLDocument doc = (HTMLDocument) super.createDefaultDocument();
doc.setAsynchronousLoadPriority(-1); // 同步加载
return doc;
}
}
// 使用方式
JEditorPane editorPane = new JEditorPane();
editorPane.setEditorKit(new SafeHTMLEditorKit());
异常捕获处理
public void loadHtmlContent(JEditorPane editorPane, String htmlContent) {
try {
// 设置HTML内容
editorPane.setContentType("text/html");
// 分步设置以减少解析压力
SwingUtilities.invokeLater(() -> {
try {
editorPane.setText(htmlContent);
} catch (NegativeArraySizeException e) {
handleParsingError(editorPane, e);
}
});
} catch (Exception e) {
handleParsingError(editorPane, e);
}
}
private void handleParsingError(JEditorPane pane, Exception e) {
System.err.println("HTML解析异常:");
e.printStackTrace();
// 显示纯文本版本
pane.setContentType("text/plain");
pane.setText("【HTML解析错误】内容无法正常显示");
// 或者显示错误信息
pane.setText("Error: " + e.getMessage());
}
使用第三方库进行预处理
import org.jsoup.Jsoup;
import org.jsoup.safety.Whitelist;
public String sanitizeHtml(String dirtyHtml) {
// 使用 Jsoup 清理和标准化 HTML
org.jsoup.nodes.Document doc = Jsoup.parse(dirtyHtml);
// 修复常见问题
doc.outputSettings().prettyPrint(false);
// 获取清理后的HTML
String cleanHtml = doc.body().html();
// 移除可能导致问题的属性
cleanHtml = cleanHtml.replaceAll("colspan=\"-?\\d+\"", "colspan=\"1\"");
cleanHtml = cleanHtml.replaceAll("rowspan=\"-?\\d+\"", "rowspan=\"1\"");
return cleanHtml;
}
完整的解决示例
import javax.swing.*;
import javax.swing.text.html.*;
import java.awt.*;
public class SafeHtmlViewer extends JFrame {
private JEditorPane editorPane;
public SafeHtmlViewer() {
editorPane = new JEditorPane();
editorPane.setEditable(false);
// 使用安全的 EditorKit
editorPane.setEditorKit(createSafeHtmlKit());
JScrollPane scrollPane = new JScrollPane(editorPane);
add(scrollPane, BorderLayout.CENTER);
setSize(600, 400);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
private HTMLEditorKit createSafeHtmlKit() {
return new HTMLEditorKit() {
@Override
public Document createDefaultDocument() {
HTMLDocument doc = (HTMLDocument) super.createDefaultDocument();
// 设置解析器属性
doc.setParser(new HTMLDocument.HTMLReader.Parser() {
@Override
public void parse(String in) {
try {
super.parse(in);
} catch (NegativeArraySizeException e) {
System.err.println("解析器捕获负数组大小异常,尝试继续处理");
}
}
});
return doc;
}
};
}
public void displayHtml(String html) {
try {
// 预处理 HTML
String safeHtml = preprocessHtml(html);
editorPane.setText(safeHtml);
} catch (Exception e) {
// 最后保底方案
editorPane.setText("内容显示异常");
System.err.println("HTML显示失败: " + e.getMessage());
}
}
private String preprocessHtml(String html) {
if (html == null || html.isEmpty()) {
return "<html><body></body></html>";
}
// 确保有基本结构
if (!html.toLowerCase().startsWith("<html")) {
html = "<html><body>" + html + "</body></html>";
}
return html;
}
}
推荐的最佳实践
- 使用 Jsoup 库 预处理 HTML 内容
- 捕获异常并优雅降级,而不是让程序崩溃
- 限制输入内容的大小,避免超大 HTML 文件
- 在 EDT 线程中操作 UI 组件
- 测试各种异常 HTML 格式 提前发现问题
这个问题的根本原因是 JDK 内置的 HTML 解析器对某些格式错误的处理不够健壮,所以最好的策略是 在传给 JEditorPane 之前就清理和验证 HTML 内容。