在匿名内部类

时间:2016-11-02 02:16:27

标签: java multithreading swing anonymous-inner-class

编辑:我已经修复了它,这要归功于rsutormin关于使最简单的程序可能出现同样错误的链接。我这样做了,它继续工作。我从原作中添加了越来越多的东西,我可以想象它会有所贡献,它只是继续工作。起初很生气,但后来我意识到,如果它继续工作,最终我只有一份我的程序,除了工作。所以这很好。 最终我发现添加的东西导致了与以前相同的行为。在动作事件监听器方法中,其他情况(除了按钮)用于排序(按字母顺序,按大小等)。切换语句后我有

setTableRows();
update();

并且只有在添加了这个后面(update()修复了列宽,并且setTableRows()重新调整新顺序中的内容(如果已经使用),问题再次出现。特别是setTableRows()。我仍然不知道究竟是什么会导致新线程出现问题但是当它不存在时(我正在考虑与线程在执行点运行的事实有关的事情)继续这个位 - 但该方法不会编辑thingList,只读它...?),但这是方法,以防其他人有类似的问题,并可以识别与他们自己的代码共同的东西。

private void setTableRows()
{
    DefaultTableModel dtm = new DefaultTableModel(0, 0);
    String[] header = new String[]{"Name", "Size"};
    dtm.setColumnIdentifiers(header);
    for(Thing thing : thingList.getList())
    {
        String sizeColumn = Integer.toString(thing.getSize()) + "MB";
        if(sizeColumn.equals("0MB"))
        {
            sizeColumn = "?";
        }
        dtm.addRow(new Object[]{thing.getTitle(), sizeColumn});
    }
    thingTable.setModel(dtm);
}

要点: 我有一个Java Swing程序,只需单击一个按钮就可以执行一个缓慢,长时间运行的作业。用户界面很冷,所以我认为我应该在一个新线程中进行。但是,主窗口的成员变量未正确传递给线程。只有在构造中创建的部件可用,之后没有其他任何改变。

最相关的部分:

public class MainWindow extends JPanel implements ActionListener
{

    ......

    @Override
    public void actionPerformed(ActionEvent e)
    {

        switch(e.getActionCommand())
        {
            ...
          case "go":
          Thread t = new Thread()
            {
                @Override
                public void run()
                {
                    try
                    {
                        DoStuff d = new DoStuff(statusLabel);
                        d.doStuff(thingList);
                    }
                    catch (IOException | InterruptedException e1)
                    {
                        e1.printStackTrace();
                    }
                }
            };
            t.start();

            break;
        }
    ....
}

MainWindow(扩展JPanel实现ActionListener)具有以下成员变量:

ThingList thingList

JTextArea statusLabel

(以及其他许多不相关的内容)

ThingList是一个包含两个相关成员变量的类:

ArrayList<Thing> list

ArrayList<Thing> selectedList

列表填充在ThingList的构造函数中,而thingList是在MainWindow的构造函数中创建的。当用户在其侦听器中单击JTable时,将填充selectList,并在ThingList中添加如下内容:

public void setSelection(int[] rows)
{
    selectedList.clear();
    for(int i = 0; i < rows.length; i ++)
    {
        selectedList.add(list.get(rows[i]));
    }
}

传递的行是点击的行。

Thing只是一个包含getter和setter的数据保持类,而且都是。

...

实际行为

  • 在doStuff中,传入的ThingList具有正确填充的列表,但是空的selectedList

  • 如果你在run()中断点,并将鼠标悬停在其中一个变量上,它们会显示为粗体,就像你在执行点上超出范围的变量(这是在Eclipse中)一样。 / p>

  • 如果你在Thread定义之前创建一个局部变量并使用它,那么它在悬停时不会显示为粗体(并且它有一个id,你可以进入这两个列表并查看它们的数据),但是当selectedList为空时,列表仍然填充

  • 如果给Thread一个名称及其自己的成员变量,并在构造函数中传递真实的变量,则其行为如上所述。

  • 如果在填充列表的方法内向selectedList添加虚拟Thing,则会在传入的ThingList中显示为doStuff。但我事先无法知道用户会点击什么,因此我无法在构造函数中填充selectedList作为解决方案。

我一直在尝试阅读很多关于内部类的内容,而且似乎所有内容都表明他们应该能够使用封闭类的成员变量而不会出现问题,但只是局部变量可能必须是最终的。我猜它是不同的,因为作为一个线程不仅仅是一个普通的普通内部类。但我似乎找不到任何有同样问题的人。

提前感谢您的帮助。

编辑: 只是有一个想法尝试打印出对象的内存地址,看看它们是否是相同的(我假设没有)。我得到了这个结果:

thingList address outside of the new thread: main.ThingList@332f9531
selectedList address outside of the new thread: [main.Thing@3f12d523]
thingList address inside run(): main.ThingList@332f9531
selectedList address inside run(): []

来自

System.out.println("thingList address outside of the new thread: " + thingList);
            System.out.println("selectedList address outside of the new thread: " + thingList.getSelectedList());

就在Thread t = new Thread()和

之前
System.out.println("thingList address inside run(): " + thingList);
                    System.out.println("selectedList address inside run(): " + thingList.getSelectedList());

在run()内(在try {)

之前

所以它们是同一个对象(我假设它是哈希代码所代表的 - 它具有相同的内存位置?)但是当它在线程内时,selectedList是空白的(或者[]表示的任何东西)。 / p>

1 个答案:

答案 0 :(得分:1)

也许您需要“SwingUtilities.invokeLater()”才能正确更改与Swing相关的组件的属性?无论如何,准备最小,完整和可验证的例子(https://stackoverflow.com/help/mcve)要好得多。这是我的方式:

import java.awt.Color;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class ColorFrame extends JFrame {
    private JPanel innerPanel = new JPanel();

    public static void main(String[] args) {
        new ColorFrame().setVisible(true);
    }

    public ColorFrame() {
        this.add(innerPanel);
        this.setSize(500, 500);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        new Thread() {
            @Override
            public void run() {
                while(true) {
                    try {
                        Thread.sleep(1000);
                    } catch(Exception ex) {
                        break;
                    }
                    SwingUtilities.invokeLater(new Runnable() {
                        @Override
                        public void run() {
                            int r = (int)(255 * Math.random());
                            int g = (int)(255 * Math.random());
                            int b = (int)(255 * Math.random());
                            ColorFrame.this.innerPanel.setBackground(new Color(r, g, b));
                        }
                    });
                }
            }
        }.start();
    }
}

请您检查我的示例是否涵盖您的问题?