线程中JFrame无法销毁及名称异常问题求助
问题分析与修复方案
核心问题原因
你遇到的frame名称不一致、窗口无法销毁的问题,根源在于错误地创建了多个RegisterExercise实例:
- 在
RegisterExercise的initializeFrame方法中,你通过new RegisterExercise().mainPanel创建了一个全新的RegisterExercise对象,这个新对象会自动生成自己的JFrame(即日志中的frame1)。 - 你把原本的
frame0的内容面板替换成了新实例的mainPanel,导致用户实际点击的btnCancel属于这个新实例,它的监听器绑定的是新实例的frame1,而显示的窗口却是最初的frame0。调用frame.dispose()只会销毁看不见的frame1,frame0依然存在。 - 即使
frame被final修饰,这里的问题是你混淆了两个不同实例的frame,它们完全是两个独立对象。
另外,Menu类的run方法中也存在同样的错误:通过new Menu().mainPanel创建了新的Menu实例,导致当前实例的监听器无法生效。
修复后的代码
修复RegisterExercise类
public class RegisterExercise extends Thread { private final JFrame frame; private JButton btnSave; private JPanel mainPanel; private JButton btnCancel; private JTextField txtName; private JTextField txtEasyName; private JTextField txtDescription; private JTextField txtMachineName; private JLabel lblName; private JLabel lblEasyName; private JLabel lblDescription; private JLabel lblMachineName; public RegisterExercise() { frame = new JFrame("Register an exercise"); // 给当前实例的btnCancel绑定监听器 btnCancel.addActionListener(e -> { System.out.println("Frame:" + frame.getName()); frame.dispose(); }); } @Override public void run() { try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException e) { throw new RuntimeException(e); } initializeFrame(frame); super.run(); } private void initializeFrame(JFrame frame) { // 使用当前实例的mainPanel,避免创建新的RegisterExercise对象 frame.setContentPane(this.mainPanel); frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); frame.pack(); frame.setVisible(true); System.out.println("This is frame:" + frame.getName()); } }
修复Menu类
public class Menu extends Thread { private JButton btnExit; private JPanel mainPanel; private JButton btnRegisterExercise; private JTable menuTable; private JLabel lblVersion; private RegisterExercise registerExercise; public Menu() { btnExit.addActionListener(e -> { int answer = JOptionPane.showConfirmDialog(null, "Do you want to leave?", "Leaving", JOptionPane.YES_NO_OPTION); if (answer == JOptionPane.YES_OPTION) { System.exit(0); } }); btnRegisterExercise.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { // 允许窗口销毁后重新打开 if (registerExercise == null || !registerExercise.frame.isDisplayable()) { registerExercise = new RegisterExercise(); registerExercise.start(); } } }); } @Override public void run() { try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException e) { throw new RuntimeException(e); } JFrame frame = new JFrame("My Move Gym"); // 使用当前实例的mainPanel,避免创建新的Menu对象 frame.setContentPane(this.mainPanel); frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); frame.pack(); frame.setVisible(true); super.run(); } }
额外说明
- 修复后,点击
btnCancel时会直接操作显示的frame0,调用dispose()可以正常销毁窗口,无需使用System.exit(0)。 Menu类中添加了窗口状态判断,允许用户在窗口销毁后再次点击按钮重新打开注册窗口。- (可选优化)Swing组件的创建和更新应该在事件调度线程(EDT)中执行,你可以将窗口初始化代码包装在
SwingUtilities.invokeLater()中,避免线程安全问题。
内容的提问来源于stack exchange,提问作者Alfredo Marin
相关产品推荐
相关产品推荐

