2013-10-09 2 views
2

Так недавно я работал над инструментом для нас здесь, чтобы настроить определенные приложения. Это не должно было быть чем-то действительно потрясающим, просто базовым инструментом с некоторым созданием сценариев SQL и созданием нескольких файлов XML. Во время этого я создал серию объектов JTable с собственной реализацией AbstractTableModel. После того, как я все построил, и дошел до того, что тестировал сохранение и загрузку, используя неудачную сериализацию AbstractTableModel (просто записанной на диск с использованием ObjectStreamWriter). Мне потребовался почти весь день, чтобы понять, что происходит. Когда я попытаюсь их сериализовать, я получаю NotSerializableException в java.lang.reflect.Constructor. Я не знал, что это такое, потому что моя модель таблицы содержала только сериализуемые сущности, и все слушатели, которые я подключил, также были сериализованы, а родительский класс также сериализуем. После многократного поиска и нескольких полезных сообщений отсюда я обнаружил, что при добавлении TableModelListener в реализацию AbstractTableModel добавляется еще один прослушиватель в дополнение к тому, который вы добавили, типа javax.swing.event.TableModelListener, который isn ' t serializable (см. http://docs.oracle.com/javase/7/docs/api/javax/swing/event/TableModelListener.html для интерфейса, я не знаю реализации). EDIT Модель не добавляет этот несериализуемый прослушиватель, JTable делает. Мой вопрос в основном, почему этот объект будет добавлять свой собственный несериализуемый объект изнутри, тем самым отрицая тот факт, что он действительно реализует Serializable? Это что-то, что я должен сообщить в качестве ошибки?Почему JTables делает TableModels не сериализуемыми при визуализации?

FYI Работаю я должен был просто удалить всех слушателей, сериализовать, а затем снова добавить слушателей. При десериализации я должен был только добавить тот, который я создал, и модель снова создала другую.

Редактировать Попробуйте сериализовать эту модель с классом сериализатора, вызванным вызовом метода 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(); 
    } 
} 

} 

попробуйте заменить метод сохранения этой

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

здесь простой графический интерфейс

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

} 
+1

Вы уверены в этом пункте? [Исходный код] (http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/7-b147/javax/swing/table/AbstractTableModel.java#AbstractTableModel) не показывает это , а в [javadoc] (http://docs.oracle.com/javase/7/docs/api/javax/swing/table/AbstractTableModel.html) указано, что это 'Serializable'. Конечно, когда вы устанавливаете свою модель на 'JTable',' JTable' добавит слушателя (сама таблица) к модели, которая не является 'Serializable' – Robin

+0

Да, я уверен. Попробуйте сами. Создайте простую форму с помощью JTable, создайте ее AbstractTableModel, а затем сериализуйте ее с помощью ObjectOutputStream. Теперь добавьте к нему слушателя (обратите внимание на количество слушателей в коллекции слушателей до и после) и попробуйте снова сериализовать его. Вы получите NotSerializableException –

+1

звучит как-то неправильно с кодом, который вы не показываете – kleopatra

ответ

3

JTable, TableModelListener к его собственной TableModel, является Serializable. Ваш пользовательский TableModel - Serializable. Добавление дополнительного TableModelListener, которое также равно Serializable, не должно иметь значения, как показано ниже. Некоторые предложения:

  • Убедитесь, что структура данных, содержащихся в вашем TableModel сама Serializable. В той степени, в которой это представляет bug, может быть возможным сериализовать только внутреннюю структуру данных модели. Например,

    System.out.println(copyObject(data)); 
    
  • Критически изучите ваш выбор, чтобы использовать сериализацию в этом контексте; см. также Effective Java: Chapter 11. Serialization.

Добавление: Я обновил пример создания экземпляра JTable, клонировать таблицу, используя сериализацию, обновить копию и отображения обоих.

экрана:

iamge

консоли:

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

Trashgod, если вы используете код, который я поставил выше, ** и поместили реализацию AbstractTableModel в JTable **, вы не сможете его сериализовать, особенно при использовании ObjectOutputStream над FileOutputStream. Я знаю, что название должно быть изменением в вопросе, но это по сути тот же вопрос. Почему объект JTable делает свою модель не сериализуемой, если обе они позволяют сериализовать сами по себе? Я проверил свой код выше двумя способами. После использования с добавлением модели в JTable (сбой сериализации) и после того, как вы не используете JTable (завершение сериализации). –

+0

Также просто понял, что вы используете JTable, единственное реальное различие между вашим кодом и моим (когда я использовал таблицу) заключается в том, что я фактически отобразил таблицу в форме. –

+0

Я добавил 'JTable copy = copyObject (table)' to 'main()' без ошибок. Я смущен. – trashgod