0

When trying to save an arraylist of my class Click, I get this error: java.io.NotSerializableException:javax.swing.text.DefaultHighlighter$LayeredHighlightInfo on this line of code: os.writeObject(saveList);. Even though I made my Click class implement serializable. Does anyone know the cause of this?

Here is my save Method:

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

And here is my Click class:

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

By the way I took out a chunk of code from the Click class. So if you think that the error could be in that portion of the code, I will gladly add it in.

Thanks in advance!

  • 2
    The error tells you exactly which component of Click is not serializing correctly, but more importantly -- why would you want to serialize Swing components rather than the logical model that underlies your GUI? This doesn't make sense to me. – Hovercraft Full Of Eels Jun 30 '12 at 17:05
  • it's the jtextfield thats's not serializing correctly? And what do you mean with "the logical model that underlies your Gui?" – AlphaUserGuru Jun 30 '12 at 17:08
  • You're serializing JTextFields and other Swing components which is a waste of time and resources and is *completely* unnecessary. You should be serializing the state of your GUI, the data held by the class's fields. If you understand MVC, you should be serializing the model, not the view. If you don't understand MVC, Google it and learn the key concepts as they are key to creating GUI programs in any language. – Hovercraft Full Of Eels Jun 30 '12 at 17:10
  • which version of java are you using? highlighter object in JTextComponent is transient in both java 6 and 7. Anyways, everybody else is right, serializing swing components is useless and is probably wrong. – Denis Tulskiy Jun 30 '12 at 17:21
  • ok I looked up MVC - model view controller, right? Ok and I got what it said. I should save the data, or in this case, the int values in the JTextFields, instead of saving the JTextField. – AlphaUserGuru Jun 30 '12 at 17:22
  • the reason I had my JTextFields in my Click class, however was because I wanted each individual Click object to be able to retrieve data from their own JTextFields whenever they were modified. So should I make a new class ClickData and save an ArrayList of that? Then when opening the ArrayList somehow put expand the ClickData into a full-blown Click object with the JTextFields in it? How should I structure this? – AlphaUserGuru Jun 30 '12 at 17:30
  • See edit to answer for e.g. of use of listeners to respond to property changes. – Hovercraft Full Of Eels Jun 30 '12 at 18:02

4 Answers4

2

Implementing Serializable is not sufficient to make an object serializable. For example, a Socket is not serializable: it doesn't make sense to serialize a socket. So, if you have a Foo class that has a field of type Socket and that implements Serializable, how do you intend to serialize a Foo instance. It won't work. All the fields of a serializable object msut also be serializable, recursively.

And, as Hovercraft says in his comment, you should serialize data, not swing components.

JB Nizet
  • 678,734
  • 91
  • 1,224
  • 1,255
2

You're serializing JTextFields and other Swing components which is a waste of time and resources and is completely unnecessary. You should be serializing the state of your GUI, the data held by the class's fields. If you understand MVC, you should be serializing the model, not the view. If you don't understand MVC, Google it or read this article and learn the key concepts as they are key to creating GUI programs in any language.

Also, for my money, I'd use JAXB or some other XML-based tool to serialize your data as it is saved in text format and thus understandable when read.

Example of separating GUI from model and using a property change listener to listen and respond to property changes:

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

}
Hovercraft Full Of Eels
  • 283,665
  • 25
  • 256
  • 373
1

As you can see the error clearly states that javax.swing.text.DefaultHighlighter is not serializable.

Now this class is used by composition inside the JTextField, which is a GUI component and it is not meant to be serialized. From your code it seems that you don't need to serialize the fields themselves, so just mark them as transient and you are done.

As a side note: it is always good to split what is your data from what is your GUI so that you can easily serialize just data and foget about anything concerning the GUI. This helps in general, not just in serialization, to preserve encapsulation and use OOP as it is meant to be used.

Jack
  • 131,802
  • 30
  • 241
  • 343
1

The problem is that your Click class has references to JTextField instances, and these (presumably) have references to some Swing class called DefaultHighlighter.LayeredHighlightInfo ... and that is not serializable.

You probably need to declare the 4 JTextField variables as transient. As a general rule, Java GUI classes such as Swing components are not effectively serializable.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216