【问题标题】:Why does my JTable throw intermittent exceptions?为什么我的 JTable 会抛出间歇性异常?
【发布时间】:2015-07-20 02:39:02
【问题描述】:

我正在开发一个使用 JTable 向用户显示信息的 Swing 程序。该表目前有 700 多个条目,但我发现了一个非常奇怪的错误,似乎可以随机重现。有时JTable 会导致IndexOutOfBoundsException 索引:0,大小:0。我已经多次编译并运行我的程序而没有改变任何东西,这个异常会随机出现,当异常没有出现时,程序行为正常。

我无法为这个问题提供代码,因为它需要超过 10 个文件才能运行。为什么JTable 会发生这种情况?我发现一个程序如何在程序的一次运行期间抛出异常并且在同一程序的另一次运行时不抛出异常而没有代码更改真的很奇怪......是否有任何众所周知的错误导致这种行为程序?

我不确定我应该包含代码的哪些部分,但这里有一些附加信息。

我创建了一个名为PlayerTableModel 的自定义TableModel,用于渲染JTable,数据存储在另一个类中的ArrayLists 中。这是TableModel的代码

public class PlayerTableModel extends AbstractTableModel {

    ArrayList<User> users = FileHandler.getAllPlayers();
    ArrayList<PlayerSummary.Player> summaries = FileHandler.getAllSummaries();

    @Override
    public int getColumnCount() {

        return 7;
    }

    @Override
    public int getRowCount() {

        return users.size();
    }

    @Override
    public Object getValueAt(int rowIndex, int columnIndex) {

        User user = users.get(rowIndex);

        switch (columnIndex) {
        case 0:
            if (user.getSteamId().equalsIgnoreCase(summaries.get(rowIndex).getSteamID())) {  // This is line 39
                return summaries.get(rowIndex).getPersonaName();
            } else {
                return null;
            }
        case 1:
            return user.getDateAdded();
        case 2:
            return user.getDateUpdated();
        case 3:
            return user.getNumberOfBans();
        case 4:
            return user.getNumberOfGameBans();
        case 5:
            return user.getDaysSinceLastBan();
        case 6:
            return user.getSteamId();
        }

        return null;
    }

    public String getColumnName(int columnIndex) {

        switch (columnIndex) {
        case 0:
            return "ID";
        case 1:
            return "Date added";
        case 2:
            return "Date updated";
        case 3:
            return "VAC bans";
        case 4:
            return "Game bans";
        case 5:
            return "Last ban (days)";
        case 6:
            return "64-Bit SteamID";
        }
        return null;

    }

    public boolean isCellEditable(int row, int column) {
        return false;
    }

}

在运行程序时,有时会出现这个异常,这似乎是某种线程问题?

线程“AWT-EventQueue-0”中的异常 java.lang.IndexOutOfBoundsException:索引:0,大小:0 在 java.util.ArrayList.rangeCheck(未知来源)在 java.util.ArrayList.get(未知来源)在 PlayerTableModel.getValueAt(PlayerTableModel.java:39) 在 javax.swing.JTable.getValueAt(Unknown Source) at javax.swing.JTable.prepareRenderer(未知来源)在 javax.swing.plaf.synth.SynthTableUI.paintCell(未知来源)在 javax.swing.plaf.synth.SynthTableUI.paintCells(未知来源)在 javax.swing.plaf.synth.SynthTableUI.paint(Unknown Source) at javax.swing.plaf.synth.SynthTableUI.update(未知来源)在 javax.swing.JComponent.paintComponent(Unknown Source) 在 javax.swing.JComponent.paint(Unknown Source) 在 javax.swing.JComponent.paintChildren(未知来源)在 javax.swing.JComponent.paint(Unknown Source) 在 javax.swing.JViewport.paint(未知来源)在 javax.swing.JComponent.paintChildren(未知来源)在 javax.swing.JComponent.paint(Unknown Source) 在 javax.swing.JComponent.paintChildren(未知来源)在 javax.swing.JComponent.paint(Unknown Source) 在 javax.swing.JComponent.paintChildren(未知来源)在 javax.swing.JComponent.paint(Unknown Source) 在 javax.swing.JComponent.paintChildren(未知来源)在 javax.swing.JComponent.paint(Unknown Source) 在 javax.swing.JComponent.paintToOffscreen(未知来源)在 javax.swing.RepaintManager$PaintManager.paintDoubleBuffered(未知 来源)在 javax.swing.RepaintManager$PaintManager.paint(Unknown 源)在 javax.swing.RepaintManager.paint(未知源)在 javax.swing.JComponent._paintImmediately(Unknown Source) at javax.swing.JComponent.paintImmediately(Unknown Source) at javax.swing.RepaintManager$4.run(Unknown Source) at javax.swing.RepaintManager$4.run(Unknown Source) at java.security.AccessController.doPrivileged(Native Method) 在 java.security.ProtectionDomain$1.doIntersectionPrivilege(未知 来源)在 javax.swing.RepaintManager.paintDirtyRegions(未知 来源)在 javax.swing.RepaintManager.paintDirtyRegions(未知 来源)在 javax.swing.RepaintManager.prePaintDirtyRegions(未知 来源)在 javax.swing.RepaintManager.access$1300(未知来源)在 javax.swing.RepaintManager$ProcessingRunnable.run(Unknown Source) at java.awt.event.InvocationEvent.dispatch(未知来源)在 java.awt.EventQueue.dispatchEventImpl(未知来源)在 java.awt.EventQueue.access$500(未知来源)在 java.awt.EventQueue$3.run(Unknown Source) at java.awt.EventQueue$3.run(Unknown Source) at java.security.AccessController.doPrivileged(Native Method) 在 java.security.ProtectionDomain$1.doIntersectionPrivilege(未知 源)在 java.awt.EventQueue.dispatchEvent(未知源)在 java.awt.EventDispatchThread.pumpOneEventForFilters(未知来源) 在 java.awt.EventDispatchThread.pumpEventsForFilter(未知来源) 在 java.awt.EventDispatchThread.pumpEventsForHierarchy(未知 来源)在 java.awt.EventDispatchThread.pumpEvents(未知来源) 在 java.awt.EventDispatchThread.pumpEvents(Unknown Source) 在 java.awt.EventDispatchThread.run(未知来源)

使用FileHandler类从文件中写入和读取数据:

public class FileHandler implements Serializable {

    private static ArrayList<User> tracked = new ArrayList<User>();

    private static ArrayList<PlayerSummary.Player> trackedSummaries = new ArrayList<PlayerSummary.Player>();

    private static ArrayList<PlayerSummary.Player> trackedSummariesUnsorted = new ArrayList<PlayerSummary.Player>();

    private static ArrayList<String[]> games = new ArrayList<String[]>();

    private static String savedString = "";

    private static String APIKEY = "";

    public static void writeToFile(String fileName, Object objToWrite) {

        try {
            // Write object to file.
            FileOutputStream fOS = new FileOutputStream(fileName);
            ObjectOutputStream oOS = new ObjectOutputStream(fOS);
            oOS.writeObject(objToWrite);
            oOS.close();

            // Write string to text file so that it can be displayed.
            if (fileName.equalsIgnoreCase("apikey.txt")) {
                File file = new File(fileName);
                PrintWriter pw = new PrintWriter(file);
                pw.print(objToWrite);
                pw.close();
            }
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    public static void readFromFile(String fileName, String objType) {
        FileInputStream fIS;
        try {

            fIS = new FileInputStream(fileName);
            ObjectInputStream oIS = new ObjectInputStream(fIS);

            if (objType.equalsIgnoreCase("ArrayList<User>")) {
                tracked = (ArrayList<User>) oIS.readObject();
            } else if (objType.equalsIgnoreCase("ArrayList<PlayerSummary.Player>")) {
                if (fileName.equalsIgnoreCase("summaries.tmp")) {
                    trackedSummaries = (ArrayList<PlayerSummary.Player>) oIS.readObject();
                }
                if (fileName.equalsIgnoreCase("s_unsorted.tmp")) {
                    trackedSummariesUnsorted = (ArrayList<PlayerSummary.Player>) oIS.readObject();
                }
            } else if (objType.equalsIgnoreCase("ArrayList<String[]>")) {
                games = (ArrayList<String[]>) oIS.readObject();
            } else if (objType.equalsIgnoreCase("apikey")) {
                APIKEY = (String) oIS.readObject();
            }else {
                System.out.println("Object type " + objType + " needs to be implemented!");
            }

            oIS.close();

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }

    public static ArrayList<User> getAllPlayers() {
        return tracked;
    }

    public static ArrayList<PlayerSummary.Player> getAllSummaries() {
        return trackedSummaries;
    }

    public static ArrayList<PlayerSummary.Player> getAllSummariesUnsorted() {
        return trackedSummariesUnsorted;
    }

    public static ArrayList<String[]> getGames() {
        return games;
    }

    public static String getAPIKey() {

        try {

            BufferedReader br = new BufferedReader(new FileReader("apikey.txt"));
            APIKEY = br.readLine();

        } catch (FileNotFoundException e) {
            JOptionPane.showMessageDialog(BanTracker.getFrames()[0], "File not found! Did you save your API key?");
            e.printStackTrace();

        } catch (IOException e) {

            e.printStackTrace();

        }
        return APIKEY;
    }

    public static void updateArrayList(ArrayList<User> a) {
        tracked = a;
    }

}

【问题讨论】:

  • 根据我的经验,发生这种情况是因为您正在从除 EDT 之外的线程中更改 TableModel。我在使用某种TableRowSorter 时特别看到了这一点
  • “我觉得很奇怪一个程序如何在程序的一次运行期间抛出异常,而不在同一程序的另一次运行中抛出异常而无需更改代码” - 你能说“你好比赛条件”
  • 如果没有 runnable example 来证明您的问题,这一切都是猜测,这不是代码转储,而是您正在做的一个示例,它突出了您遇到的问题。这将导致更少的混乱和更好的响应
  • @MadProgrammer 我以前没有使用过线程,我没有在我的程序中启动任何新线程。此外,我不确定如何使用代码的高亮部分重现此问题,因为我不确定是哪个部分导致它。
  • 正如我所说,不知道错误是如何产生的或者你的代码是如何工作的,这一切都只是猜测。我们使用JTable ALOT(有时使用 1, 000 行)并且在人们开始从 EDT 外部弄乱模型/表格之前没有任何问题

标签: java swing exception jtable


【解决方案1】:

您的 FileHandler 类没有加载 Event Dispatch Thread 上的文件,因此您在该线程和 Event Dispatch 线程之间造成了竞争条件。

无论如何,您都不应该静态访问您的文件;在创建时将用户和摘要注入到表模型中,甚至在呈现 JTable 之前。换句话说,只需创建一个构造函数:

public class PlayerTableModel extends AbstractTableModel {
  private final List<User> users;
  private final List<PlayerSummary.Player> summaries;

  public PlayerTableModel(List<User> users, List<PlayerSummary.Player> summaries) {
    this.users = new ArrayList<User>(users);
    this.summaries = new ArrayList<PlayerSummary.Player>(summaries);
  }
}

然后,在构建 JTable 之前,确保文件已加载:

TableModel model = new PlayerTableModel(FileHandler.getAllPlayers(),
             FileHandler.getAllSummaries());
JTable table = new JTable(model);

这应该确保所有加载都在预期的时候发生(当程序启动时),并且错误应该消失。

另外,尝试修改您的 TableModel 以执行此操作:

@Override
public int getRowCount() {
    return Math.min(users.size(), summaries.size());
}

我不确定它是否能解决您的问题,但我认为如果您这样做,您会更快乐:

public class CompletePlayer {
  public final User user;
  public final PlayerSummer.Player summary;
}

然后,只使用一个 ArrayList 而不是两个。阅读欧内斯特弗里德曼的帖子here about parallel arrays

【讨论】:

  • 第 39 行将是 if (user.getSteamId().equalsIgnoreCase(summaries.get(rowIndex).getSteamID())) {
  • 我不确定您将 getValueAt 中的所有内容重构为使用局部变量是什么意思。我目前正在使用本地定义的变量user
  • 我仍然遇到同样的异常,尽管它发生的频率要低得多。
  • 我已将您建议的更改添加到getRowCount()。现在不是抛出异常,而是表只是空的(我认为这是因为用户的大小仍然是 0)。
  • 我认为您应该更改代码的所有部分以使用CompletePlayer 的统一对象概念(并添加任何其他相关数据类)而不是并行ArrayLists,我认为您的问题会消失。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-03-12
  • 2021-04-28
  • 2013-09-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多