为什么JTable在渲染时使TableModel不可序列化? [英] Why do JTables make TableModels non serializable when rendered?

查看:193
本文介绍了为什么JTable在渲染时使TableModel不可序列化?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

So recently I was working on an a tool for us here to configure certain applications. It didn't need to be anything really awesome, just a basic tool with some SQL script generation, and creating a couple of XML files. During this I created a series of JTable objects with my own implementation of the AbstractTableModel. After I had built everything, and got to the point where I was testing saving and loading using the AbstractTableModel (just written to disk using the ObjectStreamWriter) serialization failed. It took me almost all day to figure out what was going on. When I would try to serialize them I would get a NotSerializableException on java.lang.reflect.Constructor. I didn't know what this was all about because my table model only contained serializable entities, and all of the listeners I had attached were also serializable, and the parent class is also serializable. After a lot of digging, and a few helpful posts from here I discovered that when you add a TableModelListener to an AbstractTableModel implementation, another listener is added in addition to the one you added, of type javax.swing.event.TableModelListener which isn't serializable (see http://docs.oracle.com/javase/7/docs/api/javax/swing/event/TableModelListener.html for the interface, I don't know the implementation). EDIT The Model doesn't add this non serializable listener, the JTable does. My question is essentially, why would this object add its own nonserializable object internally, thus negating the fact that it does in fact implement Serializable? Is this something I should report as a bug?

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

FYI the work around I had was to simply remove all of the listeners, serialize, then re-add the listeners. When deserializing I only needed to add the one I created, and the model created the other one on its own again.

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

Edit Try serializing this Model with the serializer class provided by invoking the setValueAt() method.

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方法

then try replacing the save method with this

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

这里是一个简单的gui

here is a simple 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 ,一个 TableModelListener 到它自己的 TableModel Serializable 。您的自定义 TableModel Serializable 。添加另外的 TableModelListener 也是 Serializable 应该没有区别,如下所示。一些建议:

JTable, a TableModelListener to it's own TableModel, is Serializable. Your custom TableModel is Serializable. Adding an additional TableModelListener that is also Serializable should make no difference, as shown below. Some suggestions:


  • 验证 TableModel 中包含的数据结构本身 Serializable 。如果这代表错误,则可能仅序列化模型的内部数据结构。例如,

  • Verify that the data structure contained in your TableModel is itself Serializable. To the extent that this represents a bug, it may be possible to serialize just the model's internal data structure. For example,

System.out.println(copyObject(data));


  • 严格检查您在此上下文中使用序列化的选择;另请参阅 Effective Java:第11章序列化

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

    Addendum: I updated the example to instantiate JTable, clone the table using serialization, update the copy and display both.

    屏幕:

    控制台:

    
    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);
            }
        }
    }
    

    这篇关于为什么JTable在渲染时使TableModel不可序列化?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

  • 查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆