Java 为什么JTables在呈现时使TableModels不可序列化?

Java 为什么JTables在呈现时使TableModels不可序列化?,java,swing,serialization,jtable,abstracttablemodel,Java,Swing,Serialization,Jtable,Abstracttablemodel,因此,最近我正在为我们设计一个工具来配置某些应用程序。它不需要什么了不起的东西,只需要一个基本工具,生成一些SQL脚本,并创建几个XML文件。在此期间,我使用自己的AbstractTableModel实现创建了一系列JTable对象。在我构建完所有内容,并使用AbstractTableModel(刚刚使用ObjectStreamWriter写入磁盘)测试保存和加载之后,序列化失败。我几乎花了一整天的时间才弄清楚到底发生了什么。当我尝试序列化它们时,我会在java.lang.reflect.Con

因此,最近我正在为我们设计一个工具来配置某些应用程序。它不需要什么了不起的东西,只需要一个基本工具,生成一些SQL脚本,并创建几个XML文件。在此期间,我使用自己的AbstractTableModel实现创建了一系列JTable对象。在我构建完所有内容,并使用AbstractTableModel(刚刚使用ObjectStreamWriter写入磁盘)测试保存和加载之后,序列化失败。我几乎花了一整天的时间才弄清楚到底发生了什么。当我尝试序列化它们时,我会在java.lang.reflect.Constructor上得到一个NotSerializableException。我不知道这是怎么回事,因为我的表模型只包含可序列化的实体,我附加的所有侦听器都是可序列化的,父类也是可序列化的。经过大量的挖掘和一些有用的文章,我发现当您向AbstractTableModel实现添加TableModelListener时,除了您添加的一个监听器之外,还会添加另一个监听器,类型为javax.swing.event.TableModelListener,它是不可序列化的(请参阅接口,我不知道实现)编辑模型不会添加此非序列化侦听器,JTable会添加。我的问题本质上是,为什么此对象会在内部添加自己的非序列化对象,从而否定它实际上实现了可序列化的事实?这是我应该报告的bug吗

仅供参考,我所做的工作只是删除所有侦听器,序列化,然后重新添加侦听器。当反序列化时,我只需要添加我创建的一个,而模型又自己创建了另一个

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

然后尝试用以下方法替换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
tablemodelistener
可序列化的。您的自定义
TableModel
是可序列化的
。添加一个附加的
TableModelListener
,它也是
可序列化的
,应该没有什么区别,如下所示。一些建议:

  • 验证
    表格模型
    中包含的数据结构本身是可序列化的。在某种程度上,这代表了一个模型,可以只序列化模型的内部数据结构。比如说,

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

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

屏幕:

控制台:

New data 新数据 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 extends Serializable> 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);
        }
    }
}
导入java.awt.BorderLayout;
导入java.awt.EventQueue;
导入java.awt.GridLayout;
导入java.io.ByteArrayInputStream;
导入java.io.ByteArrayOutputStream;
导入java.io.ObjectInputStream;
导入java.io.ObjectOutputStream;
导入java.io.Serializable;
导入javax.swing.JFrame;
导入javax.swing.JTable;
导入javax.swing.event.TableModelEvent;
导入javax.swing.event.TableModelListener;
导入javax.swing.table.AbstractTableModel;
/*@见http://stackoverflow.com/a/19300995/230513 */
公共类序列化测试{
公共静态void main(字符串[]args){
invokeLater(新的Runnable(){
@凌驾
公开募捐{
JTable table=newjtable(newblanktablemodel());
JTable copy=copyObject(表);
copy.setValueAt(“新数据”,0,0);
JFrame f=新JFrame(“序列化测试”);
f、 setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f、 setLayout(新的GridLayout(0,1,5,5));
f、 添加(表格,BorderLayout.NORTH);
f、 添加(副本,BorderLayout.SOUTH);
f、 包装();
f、 setLocationRelativeTo(空);
f、 setVisible(真);
}
});
}
私有静态类BlankTableModel扩展AbstractTableModel实现可序列化{
私有静态最终长serialVersionUID=3141592653589793L;
私有字符串data=“测试数据”;
公共模型(){
this.addTableModelListener(新的InnerTableModelListener());
}
@凌驾
public void setValueAt(对象o,整数行,整数列){
数据=o.toString();
此.firetablecell已更新(行、列);
}
@凌驾
public int getColumnCount(){
返回2;
}
@凌驾
public int getRowCount(){
返回2;
}
@凌驾
公共对象getValueAt(整数行,整数列){
返回数据;
}
私有void save(){
BlankTableModel=copyObject(此);
System.out.println(model.getValueAt(0,0));
}
@凌驾
公共布尔值可编辑(int行,int列){
返回true;
}
私有类InnerTableModelListener实现TableModelListener,可序列化{
私有静态最终长serialVersionUID=2718281828459045L;
@凌驾
公共作废表已更改(TableModelEvent e){
save();
}
}
}
私有静态T copyObject(最终T源){
试一试{
ByteArrayOutputStream bas=新的ByteArrayOutputStream();
ObjectOutputStream oos=新的ObjectOutputStream(BAS);
oos.writeObject(源);
ObjectInputStream ois=新ObjectInputStream(
新的ByteArrayInputStream(baos.toByteArray());
final T copy=(T)ois.readObject();
返回副本;
}抓住
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 extends Serializable> 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);
        }
    }
}