为什么JTable在渲染时使TableModel不可序列化?

所以最近我在这里为我们开发了一个工具来配置某些应用程序。 它不需要真正棒极了,只需要生成一些SQL脚本的基本工具,并创建几个XML文件。 在此期间,我使用自己的AbstractTableModel实现创建了一系列JTable对象。 在我构建了所有内容之后,我开始使用AbstractTableModel测试保存和加载(仅使用ObjectStreamWriter写入磁盘)序列化失败。 我几乎整天都在弄清楚发生了什么。 当我尝试序列化它们时,我会在java.lang.reflect.Constructor上得到一个NotSerializableException。 我不知道这是什么,因为我的表模型只包含可序列化的实体,我附加的所有监听器也是可序列化的,父类也是可序列化的。 经过大量的挖掘和一些有用的post后,我发现当你将一个TableModelListener添加到一个AbstractTableModel实现时,除了你添加的一个监听器之外还添加了另一个监听器,类型为javax.swing.event.TableModelListener,它不是t serializable(参见http://docs.oracle.com/javase/7/docs/api/javax/swing/event/TableModelListener.html的接口,我不知道实现)。 编辑模型不会添加这个非可序列化的侦听器,JTable会这样做。 我的问题基本上是,为什么这个对象会在内部添加自己的非可序列化对象,从而否定事实上它实际上实现了Serializable? 这是我应该报告的错误吗?

仅供参考我的工作就是简单地删除所有的监听器,序列化,然后重新添加监听器。 反序列化时我只需要添加我创建的那个,模型再次创建另一个。

编辑尝试使用通过调用setValueAt()方法提供的序列化程序类序列化此模型。

import java.io.Serializable; import javax.swing.event.TableModelEvent; import javax.swing.event.TableModelListener; import javax.swing.table.AbstractTableModel; public class BlankTableModel extends AbstractTableModel implements Serializable { /** * */ private static final long serialVersionUID = 6063143451207205385L; public BlankTableModel() { this.addTableModelListener(new InnerTableModelListener()); } @Override public void setValueAt(Object o, int x, int y) { this.fireTableChanged(new TableModelEvent(this, x, y)); } public int getColumnCount() { // TODO Auto-generated method stub return 2; } public int getRowCount() { // TODO Auto-generated method stub return 2; } public Object getValueAt(int arg0, int arg1) { // TODO Auto-generated method stub return "Test Data"; } private void save() { Serializer.SerializeObject(this); } @Override public boolean isCellEditable(int rowindex, int colindex) { return true; } private class InnerTableModelListener implements TableModelListener, Serializable { @Override public void tableChanged(TableModelEvent arg0) { save(); } } } import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectOutputStream; import java.io.Serializable; public class Serializer { public static void SerializeObject(Serializable object) { File out = new File("USE A VALID PATH"); if (!out.exists()) { try { out.createNewFile(); } catch (IOException e1) { e1.printStackTrace(); } } else { out.delete(); try { out.createNewFile(); } catch (IOException e) { e.printStackTrace(); } } try (FileOutputStream fos = new FileOutputStream(out); ObjectOutputStream oos = new ObjectOutputStream(fos)) { oos.writeObject(object); }catch (Exception e) { e.printStackTrace(); } } } 

然后尝试用这个替换save方法

 private void save() { for (TableModelListener l : this.getTableModelListeners()) { this.removeTableModelListener(l); } Serializer.SerializeObject(this); this.addTableModelListener(new InnerTableModelListener()); } 

这是一个简单的gui

 import java.awt.Dimension; import javax.swing.JFrame; import javax.swing.JTable; public class MainForm extends JFrame { public static void main(String[] args) { MainForm form = new MainForm(); form.show(); } public MainForm() { this.setBounds(100, 100, 600, 600); BlankTableModel model = new BlankTableModel(); JTable table = new JTable(model); table.setPreferredSize(new Dimension(500,500)); this.getContentPane().add(table); } } 

JTable ,它自己的TableModel ,是Serializable 。 您的自定义TableModelSerializable 。 添加另一个也是Serializable TableModelListener应该没有区别,如下所示。 一些建议:

  • validationTableModel包含的数据结构本身是否可Serializable 。 如果这代表一个错误 ,则可以仅序列化模型的内部数据结构。 例如,

     System.out.println(copyObject(data)); 
  • 批判性地检查您在此上下文中使用序列化的选择; 另见Effective Java:第11章。序列化

附录:我更新了示例以实例化JTable ,使用序列化克隆表,更新副本并显示两者。

屏幕:

iamge

安慰:

新数据

SSCCE:

 import java.awt.BorderLayout; import java.awt.EventQueue; import java.awt.GridLayout; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import javax.swing.JFrame; import javax.swing.JTable; import javax.swing.event.TableModelEvent; import javax.swing.event.TableModelListener; import javax.swing.table.AbstractTableModel; /* @see http://stackoverflow.com/a/19300995/230513 */ public class SerializationTest { public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { @Override public void run() { JTable table = new JTable(new BlankTableModel()); JTable copy = copyObject(table); copy.setValueAt("New data", 0, 0); JFrame f = new JFrame("SerializationTest"); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); f.setLayout(new GridLayout(0, 1, 5, 5)); f.add(table, BorderLayout.NORTH); f.add(copy, BorderLayout.SOUTH); f.pack(); f.setLocationRelativeTo(null); f.setVisible(true); } }); } private static class BlankTableModel extends AbstractTableModel implements Serializable { private static final long serialVersionUID = 3141592653589793L; private String data = "Test data"; public BlankTableModel() { this.addTableModelListener(new InnerTableModelListener()); } @Override public void setValueAt(Object o, int row, int col) { data = o.toString(); this.fireTableCellUpdated(row, col); } @Override public int getColumnCount() { return 2; } @Override public int getRowCount() { return 2; } @Override public Object getValueAt(int row, int col) { return data; } private void save() { BlankTableModel model = copyObject(this); System.out.println(model.getValueAt(0, 0)); } @Override public boolean isCellEditable(int row, int col) { return true; } private class InnerTableModelListener implements TableModelListener, Serializable { private static final long serialVersionUID = 2718281828459045L; @Override public void tableChanged(TableModelEvent e) { save(); } } } private static  T copyObject(final T source) { try { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(source); ObjectInputStream ois = new ObjectInputStream( new ByteArrayInputStream(baos.toByteArray())); final T copy = (T) ois.readObject(); return copy; } catch (Exception e) { throw new AssertionError("Error copying: " + source); } } }