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

如何在Java Swing GUI初始化后更新组件以避免NPE

开发Swing应用时的MVP模式空指针问题及优化方案

我在开发Swing应用时遇到了无法自行解决的问题,尝试遵循Model-Viewer-Presenter(MVP)模式,但可能对该模式的使用存在误解。在Presenter层执行读取文件内容的I/O操作(耗时较长),希望读取完成后将JLabel的内容设置为读取到的文件内容,但文件读取代码执行过快,会在GUI构建完成、JLabel字段初始化前尝试设置其内容,从而引发空指针异常(NPE)。


初始代码示例

Presenter类

import javax.swing.SwingUtilities;

public class Presenter {

   private final View view;
   private final Model model;

   @Inject
   public Presenter(final View view, final Model model) {
      this.view = view;
      this.model = model;
      /**
       * 实际场景中这里是读取文件,并用读取到的内容调用
       * view.updateStatusLabel()。
       * 下面的代码会触发NPE,因为view还未完成初始化。
       * */
      SwingUtilities.invokeLater(() -> view.updateStatusLabel("test"));
   }

}

View类(GUI初始化)

public class View {

   private final Presenter presenter;

   private JLabel statusLabel;
   private JTextField inputField;

   public View() {
      this.presenter = new Presenter(this, new Model());
   }


   public void createUI() {
      this.statusLabel = new JLabel();
      // 其他GUI初始化操作
      // ...
   }
   // 由Presenter调用,更新状态标签
   public void updateStatusLabel(final String text) {
        statusLabel.setText(text);
   }
}

应用入口Main类

import com.google.inject.Guice;
import com.google.inject.Inject;
import lombok.RequiredArgsConstructor;

import javax.swing.SwingUtilities;

@RequiredArgsConstructor(onConstructor = @__(@Inject))
public class Main {

   private final View view;

   public static void main(final String[] args) {

      final Main main = Guice.createInjector().getInstance(Main.class);

      SwingUtilities.invokeLater(main.view::createUI);
   }
}

依赖配置

<guice.version>7.0.0</guice.version>
<dependency>
      <groupId>com.google.inject</groupId>
      <artifactId>guice</artifactId>
      <version>${guice.version}</version>
    </dependency>

    <dependency>
      <groupId>com.google.inject.extensions</groupId>
      <artifactId>guice-assistedinject</artifactId>
      <version>${guice.version}</version>
    </dependency>

    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <version>1.18.28</version>
      <scope>provided</scope>
    </dependency>

优化后的代码(移除Guice依赖)

感谢建议后,我重写了代码移除Guice依赖以提升可读性,目前代码可正常运行,但作为Swing新手,希望得到指正。

应用入口Main类

import javax.swing.SwingUtilities;

public class Main {

   public static void main(final String[] args) {

      SwingUtilities.invokeLater(new View()::createUI);
   }
}

Model类

public class Model {
    
   private String password;
   private boolean initialized;

   // 此处省略getter和setter方法
}

Observer设计模式接口

interface Observer {
   void guiInitialized();
}

interface Subject {

   void registerObserver(Observer observer);

   void notifyObservers();
}

Presenter类

public class Presenter implements Observer {

   private final View view;
   private final Model model;

   public Presenter(final View view, final Model model) {
      this.view = view;
      this.model = model;
      new Thread(() -> {
         try {
            Thread.sleep(5_000);
            model.setPassword("password");
            model.setInitialized(true);
         } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
         }
      }).start();
   }

   @Override
   public void guiInitialized() {
      // 等待GUI初始化完成
      new Thread(() -> {
         while ( !model.isInitialized() ) {
            try {
               Thread.sleep(500);
            } catch (InterruptedException e) {
               Thread.currentThread().interrupt();
            }
         }
         // 此时GUI已完成初始化
         view.updateStatusLabel("init");
      }).start();
   }

}

View类

import java.util.ArrayList;
import java.util.List;

public class View implements Subject {

   private final Presenter presenter;

   private final List<Observer> observers = new ArrayList<>();
   

   private JLabel statusLabel;
   private JTextField inputField;

   public View() {
      this.presenter = new Presenter(this, new Model());
   }

   @Override
   public void registerObserver(final Observer observer) {
      this.observers.add(observer);
   }

   @Override
   public void notifyObservers() {
      observers.forEach(Observer::guiInitialized);
   }


   public void createUI() {
      this.statusLabel = new JLabel();
      // 其他GUI初始化操作
      // ...

      registerObserver(presenter);

      notifyObservers();
   }

   // 由Presenter调用,更新状态标签
   public void updateStatusLabel(final String text) {
      statusLabel.setText(text);
   }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 18:34:56