title: Why do JTables make TableModels non serializable when rendered?
body: <p>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 <a href="http://docs.oracle.com/javase/7/docs/api/javax/swing/event/TableModelListener.html" rel="nofollow">http://docs.oracle.com/javase/7/docs/api/javax/swing/event/TableModelListener.html</a> for the interface, I don't know the implementation). <strong>EDIT The Model doesn't add this non serializable listener, the JTable does.</strong> 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?</p>
  
  <p>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.</p>
  
  <p>Edit
  Try serializing this Model with the serializer class provided by invoking the setValueAt() method.</p>
  
  <pre><code>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();
      }
  }
  
  }
  </code></pre>
  
  <p>then try replacing the save method with this</p>
  
  <pre><code>private void save()
  {   
      for (TableModelListener l : this.getTableModelListeners())
      {
          this.removeTableModelListener(l);
      }
      Serializer.SerializeObject(this);
      this.addTableModelListener(new InnerTableModelListener());
  }
  </code></pre>
  
  <p>here is a simple gui</p>
  
  <pre><code>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);
  }
  
  }
  </code></pre>