You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用DocumentListener实现IPv4校验时遇异常及循环问题

解决DocumentListener在IPv4输入框中的异常与循环问题

我来帮你梳理并解决这个问题,你遇到的IllegalStateException和循环执行都是Swing监听器场景下的典型坑,咱们一步步拆解搞定:

1. 先搞懂问题根源

(1)IllegalStateException为啥会出现?

这个异常的全称是Attempt to mutate in notification,本质原因是:在DocumentListener的回调执行期间,Swing的文档处于写锁定状态。如果你的classMain方法里修改了txt_ip4class的文本内容,就会触发这个异常——因为你在文档变更的通知流程里,试图再次修改它。

你已经用了SwingUtilities.invokeLater把逻辑放到事件队列后面,这确实解决了异常,但又引入了新问题:修改文本会再次触发DocumentListener,导致逻辑反复执行,形成循环。

(2)循环执行的核心原因

当输入内容匹配IPv4正则时,classMain会修改box_ip4class的选中项;如果classMain同时还修改了txt_ip4class的文本(比如格式化IP),或者box_ip4class的变更事件又触发了修改txt_ip4class的逻辑,就会再次触发DocumentListener,无限循环下去。


2. 具体解决方案

方案一:用标志位跳过程序触发的监听器回调

最常用的方法是加一个布尔标志,标记当前是否是程序主动修改文本,此时跳过业务逻辑,避免循环:

// 定义一个类级别的标志位,标记是否是程序主动修改文本
private boolean isProgrammaticChange = false;

txt_ip4class.getDocument().addDocumentListener(new DocumentListener() {
    @Override
    public void insertUpdate(DocumentEvent e) { logUpdate(); }
    @Override
    public void removeUpdate(DocumentEvent e) { logUpdate(); }
    @Override
    public void changedUpdate(DocumentEvent e) { logUpdate(); }
    
    void logUpdate() {
        // 如果是程序主动修改,直接跳过监听器逻辑
        if (isProgrammaticChange) {
            return;
        }
        
        Runnable doHighlight = new Runnable() {
            @Override
            public void run() {
                String aTxt = txt_ip4class.getText();
                // 只做一次正则匹配,避免重复计算
                boolean isIpValid = aTxt.matches("\\b((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\\.|$)){4}\\b");
                
                try {
                    // 设置标志位,告诉监听器接下来是程序主动修改
                    isProgrammaticChange = true;
                    if (isIpValid) {
                        classValue = 1;
                        classW.classMain(classValue, box_ip4class, txt_ip4class, txt_rangeclass, txt_bitclass);
                    } else {
                        classValue = 0;
                        classW.classMain(classValue, box_ip4class, txt_ip4class, txt_rangeclass, txt_bitclass);
                    }
                } finally {
                    // 不管有没有异常,都要重置标志位,保证后续用户输入能正常触发监听器
                    isProgrammaticChange = false;
                }
            }
        };
        SwingUtilities.invokeLater(doHighlight);
    }
});

方案二:临时移除再重新添加监听器

另一种思路是在修改文本前,先把DocumentListener移除,修改完成后再重新添加,这样程序修改文本时就不会触发监听器回调:

// 先把监听器抽成独立变量,方便后续移除和添加
DocumentListener ipDocListener = new DocumentListener() {
    @Override
    public void insertUpdate(DocumentEvent e) { logUpdate(); }
    @Override
    public void removeUpdate(DocumentEvent e) { logUpdate(); }
    @Override
    public void changedUpdate(DocumentEvent e) { logUpdate(); }
    
    void logUpdate() {
        Runnable doHighlight = new Runnable() {
            @Override
            public void run() {
                String aTxt = txt_ip4class.getText();
                boolean isIpValid = aTxt.matches("\\b((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\\.|$)){4}\\b");
                
                // 临时移除监听器,避免程序修改文本时触发回调
                txt_ip4class.getDocument().removeDocumentListener(ipDocListener);
                try {
                    if (isIpValid) {
                        classValue = 1;
                        classW.classMain(classValue, box_ip4class, txt_ip4class, txt_rangeclass, txt_bitclass);
                    } else {
                        classValue = 0;
                        classW.classMain(classValue, box_ip4class, txt_ip4class, txt_rangeclass, txt_bitclass);
                    }
                } finally {
                    // 重新添加监听器,恢复用户输入的监听
                    txt_ip4class.getDocument().addDocumentListener(ipDocListener);
                }
            }
        };
        SwingUtilities.invokeLater(doHighlight);
    }
};

// 初始给输入框添加监听器
txt_ip4class.getDocument().addDocumentListener(ipDocListener);

3. 额外优化建议

  • 你原来的代码里重复写了两次完全一样的正则匹配,我已经合并成了isIpValid变量,减少不必要的性能消耗。
  • 检查下classMain方法的逻辑:如果classValue=1时只需要设置box_ip4class的选中项,不需要修改txt_ip4class的文本,那循环的概率会大大降低——尽量避免不必要的文本修改。
  • 对于box_ip4class的itemStateChanged事件,如果这个事件里也会修改txt_ip4class的文本,那也要用类似的标志位/移除监听器的方式,避免反向触发DocumentListener形成循环。

内容的提问来源于stack exchange,提问作者Sayful Adrian

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 07:20:08