Java notSerializableExceptionError

Cuando intento guardar una lista de matrices de mi clase Click, aparece este error: java.io.NotSerializableException:javax.swing.text.DefaultHighlighter$LayeredHighlightInfo en esta línea de código: os.writeObject(saveList);. A pesar de que hice que mi clase Click implemente serializable. ¿Alguien sabe la causa de esto?

Aquí está mi método de guardado:

public static void saveArray(ArrayList<Click> saveList) {
        JFileChooser c = new JFileChooser();
        c.showSaveDialog(new JFrame());
        File f = c.getSelectedFile();
        try {
            ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream(
                                f.getAbsolutePath()));
            os.writeObject(saveList);
        } catch (IOException e1) {
        e1.printStackTrace();
    }
}

Y aquí está mi clase Click:

public static class Click implements Serializable {
    JTextField xClickField;
    JTextField yClickField;
    JTextField clickIntervalField;
    JTextField repeatTimesField;
    boolean isLeft;
    Integer clickX;
    Integer clickY;
    Integer clickInterval;
    Integer clickTimes;

    public Click(boolean left){
        xClickField = new JTextField();
        yClickField = new JTextField();
        clickIntervalField = new JTextField();
        repeatTimesField = new JTextField();
        clickX = 0;
        clickY = 0;
        clickInterval = 0;
        clickTimes = 0;
        isLeft = left;
        addToJPanel();
    }

    public void addToJPanel() {
        xClickField.setText(clickX.toString());
        yClickField.setText(clickY.toString());
        clickIntervalField.setText(clickInterval.toString());
        repeatTimesField.setText(clickTimes.toString());
        panel.add(xClickField);
        panel.add(yClickField);
        panel.add(clickIntervalField);
        panel.add(repeatTimesField);

        frame.setVisible(false);
        frame.setVisible(true);
    }

    public void removeFromJPanel() {
        panel.remove(xClickField);
        panel.remove(yClickField);
        panel.remove(clickIntervalField);
        panel.remove(repeatTimesField);

        frame.setVisible(false);
        frame.setVisible(true);
    }
}

Por cierto, saqué un trozo de código de la clase Click. Entonces, si cree que el error podría estar en esa parte del código, con gusto lo agregaré.

Gracias de antemano!

preguntado el 30 de junio de 12 a las 18:06

El error le indica exactamente qué componente de Click no se está serializando correctamente, pero lo que es más importante: ¿por qué querría serializar los componentes de Swing en lugar del modelo lógico que subyace a su GUI? Esto no tiene sentido para mí. -

¿Es el jtextfield el que no se serializa correctamente? ¿Y qué quieres decir con "el modelo lógico que subyace en tu Gui"? -

Está serializando JTextFields y otros componentes de Swing, lo que es una pérdida de tiempo y recursos y es completamente innecesario. Debería serializar el estado de su GUI, los datos que contienen los campos de la clase. Si entiende MVC, debe serializar el modelo, no la vista. Si no entiende MVC, búsquelo en Google y aprenda los conceptos clave, ya que son clave para crear programas GUI en cualquier idioma. -

que version de java estas usando? El objeto resaltador en JTextComponent es transitorio tanto en Java 6 como en 7. De todos modos, todos los demás tienen razón, serializar componentes swing es inútil y probablemente esté mal. -

ok, busqué MVC - controlador de vista de modelo, ¿verdad? Ok y entendí lo que decía. Debería guardar los datos, o en este caso, los valores int en JTextFields, en lugar de guardar JTextField. -

4 Respuestas

Implementar Serializable no es suficiente para hacer un objeto serializable. Por ejemplo, un Socket no es serializable: no tiene sentido serializar un socket. Entonces, si tienes un Foo clase que tiene un campo de tipo Socket y que implementa Serializable, ¿cómo pretende serializar un Foo ejemplo. No funcionará. Todos los campos de un objeto serializable deben ser también serializables, recursivamente.

Y, como dice Hovercraft en su comentario, debe serializar datos, no balancear componentes.

Respondido el 30 de junio de 12 a las 18:06

Está serializando JTextFields y otros componentes de Swing, lo que es una pérdida de tiempo y recursos y es completamente innecesario. Debería serializar el estado de su GUI, los datos que contienen los campos de la clase. Si entiende MVC, debe serializar el modelo, no la vista. Si no entiende MVC, búsquelo en Google o lea este artículo y aprenda los conceptos clave, ya que son clave para crear programas GUI en cualquier idioma.

Además, por mi dinero, usaría JAXB o alguna otra herramienta basada en XML para serializar sus datos, ya que se guardan en formato de texto y, por lo tanto, son comprensibles cuando se leen.

Ejemplo de cómo separar la GUI del modelo y usar un detector de cambio de propiedad para escuchar y responder a los cambios de propiedad:

import java.awt.Dimension;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.Serializable;

import javax.swing.*;
import javax.swing.event.SwingPropertyChangeSupport;

public class SimpleClickEg {
   private static void createAndShowGui() {
      SimpleClickPanel clickPanel = new SimpleClickPanel();

      JFrame frame = new JFrame("SimpleClickEg");
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.getContentPane().add(clickPanel);
      frame.pack();
      frame.setLocationRelativeTo(null);
      frame.setVisible(true);
   }

   public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            createAndShowGui();
         }
      });
   }
}

class SimpleClickPanel extends JPanel {
   private static final int PREF_WIDTH = 800;
   private static final int PREF_HIEGHT = 600;
   private JTextField clickCountField = new JTextField(5);
   private JTextField clickXField = new JTextField(5);
   private JTextField clickYField = new JTextField(5);
   private SimpleClick click = new SimpleClick();

   public SimpleClickPanel() {
      add(new JLabel("Click X:"));
      add(clickXField);
      add(new JLabel("Click Y:"));
      add(clickYField);
      add(new JLabel("Click Count:"));
      add(clickCountField);

      addMouseListener(new MouseAdapter() {
         @Override
         public void mousePressed(MouseEvent e) {
            click.setClickPoint(e.getPoint());
         }
      });

      click.addPropertyChangeListener(new PropertyChangeListener() {

         @Override
         public void propertyChange(PropertyChangeEvent evt) {
            if (SimpleClick.CLICK_COUNT.equals(evt.getPropertyName())) {
               clickCountField.setText(String.valueOf(click.getClickCount()));
            } else if (SimpleClick.CLICK_X.equals(evt.getPropertyName())) {
               clickXField.setText(String.valueOf(click.getClickX()));
            } else if (SimpleClick.CLICK_Y.equals(evt.getPropertyName())) {
               clickYField.setText(String.valueOf(click.getClickY()));
            }
         }
      });
   }

   @Override
   public Dimension getPreferredSize() {
      return new Dimension(PREF_WIDTH, PREF_HIEGHT);
   }

   public SimpleClick getClick() {
      return click;
   }
}

class SimpleClick implements Serializable {
   private static final long serialVersionUID = 1L;
   public static final String CLICK_COUNT = "click count";
   public static final String CLICK_X = "click x";
   public static final String CLICK_Y = "click y";

   private int clickCount;
   private int clickX;
   private int clickY;
   private transient SwingPropertyChangeSupport spcSupport = new SwingPropertyChangeSupport(
         this);

   public int getClickCount() {
      return clickCount;
   }

   public void setClickCount(int clickCount) {
      Integer oldValue = this.clickCount;
      Integer newValue = clickCount;
      this.clickCount = newValue;
      spcSupport.firePropertyChange(CLICK_COUNT, oldValue, newValue);
   }

   public void incrementClickCount() {
      setClickCount(getClickCount() + 1);
   }

   public void setClickPoint(Point p) {
      setClickX(p.x);
      setClickY(p.y);
      incrementClickCount();
   }

   public int getClickX() {
      return clickX;
   }

   public void setClickX(int clickX) {
      Integer oldValue = this.clickX;
      Integer newValue = clickX;
      this.clickX = newValue;
      spcSupport.firePropertyChange(CLICK_X, oldValue, newValue);
   }

   public int getClickY() {
      return clickY;
   }

   public void setClickY(int clickY) {
      Integer oldValue = this.clickY;
      Integer newValue = clickY;
      this.clickY = newValue;
      spcSupport.firePropertyChange(CLICK_Y, oldValue, newValue);
   }

   public void addPropertyChangeListener(PropertyChangeListener listener) {
      spcSupport.addPropertyChangeListener(listener);
   }

   public void removePropertyChangeListener(PropertyChangeListener listener) {
      spcSupport.removePropertyChangeListener(listener);
   }

}

Respondido el 30 de junio de 12 a las 19:06

Como puede ver, el error establece claramente que javax.swing.text.DefaultHighlighter no es serializable.

Ahora bien, esta clase es utilizada por la composición dentro de la JTextField, que es un componente de GUI y no está destinado a ser serializado. Según su código, parece que no necesita serializar los campos en sí, así que simplemente márquelos como transient y has terminado.

Como nota al margen: es siempre es bueno dividir cuáles son sus datos de cuál es su GUI para que pueda serializar fácilmente solo datos y olvidar cualquier cosa relacionada con la GUI. Esto ayuda en general, no solo en la serialización, para preservar la encapsulación y usar OOP como debe usarse.

Respondido el 30 de junio de 12 a las 18:06

El problema es que tu Click class tiene referencias a instancias de JTextField, y estas (presumiblemente) tienen referencias a alguna clase Swing llamada DefaultHighlighter.LayeredHighlightInfo ... y eso no es serializable.

Probablemente necesites declarar los 4 JTextField variables como transient. Como regla general, las clases de la GUI de Java, como los componentes de Swing, no son efectivamente serializables.

Respondido el 30 de junio de 12 a las 18:06

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.