0

I've been working on a program to learn more about JAVA, and I'm almost finished. It's been a while since I've worked with Serialization, and even after brushing up on it, I'm still missing something. I'm trying to save this class into a file, but it's throwing a NotSerialzableException. This class, MonetaryField, contains the non-primitive type SingleField, which is Serialized and can save to disk. It also contains the following:

public class MonetaryField extends JPanel implements Serializable {

    private static final long serialVersionUID = 1L;
    public JLabel label;
    public SingleField gold;
    public SingleField silver;
    public SingleField copper;

    public MonetaryField() {

    }

    public MonetaryField(String s, boolean editable, boolean border) {
            label = new JLabel(s);
            gold = new SingleField("gold.png", border);
            silver = new SingleField("silver.png", border);
            copper = new SingleField("copper.png", border);
            gold.addKeyListener(keys);
            silver.addKeyListener(keys);
            copper.addKeyListener(keys);

            if(!editable) {
                    gold.setEditable(false);
                    silver.setEditable(false);
                    copper.setEditable(false);
            }
            GroupLayout layout = new GroupLayout(this);             
            this.setLayout(layout);
            layout.setHorizontalGroup(
                  layout.createSequentialGroup()
                      .addComponent(label)
                      .addComponent(gold)
                      .addComponent(silver)
                      .addComponent(copper)
        );
            layout.setVerticalGroup(
                  layout.createSequentialGroup()
                      .addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE)
                       .addComponent(label)
                       .addComponent(gold)
                       .addComponent(silver)
                       .addComponent(copper)
                    ));
    }

    public void setAmount(int g, int s, int c) {
            gold.setValue(g);
            silver.setValue(s);
            copper.setValue(c);
    }

    public void setAmount(String g, String s, String c) {
            gold.setValue(Integer.parseInt(g.replaceAll(",", "")));
            silver.setValue(Integer.parseInt(s.replaceAll(",", "")));
            copper.setValue(Integer.parseInt(c.replaceAll(",", "")));
    }

    public void setAmount(int amount) {
            gold.setValue(Math.floor(amount / 10000));
            silver.setValue(((amount % 10000) - (amount % 100)) / 100);
            copper.setValue(amount % 100);
    }

    public boolean sizeFits(Object field) {
            SingleField s_field = (SingleField) field;
            int max_chars = 4;
            boolean g = s_field.getText().length() < max_chars;
            if(!g) {
                    return false;
            }
            return true;
    }

    KeyListener keys = new KeyListener() {
            @Override
            public void keyTyped(KeyEvent e) {
                    if(!Character.isDigit(e.getKeyChar()) || sizeFits(e.getSource()) == false) {
                            e.consume();
                    }
            }

            @Override
            public void keyReleased(KeyEvent e) {    }

            @Override
            public void keyPressed(KeyEvent e) {    }
    };

    public double getAmount() {
            double d = 0.0;
            d += Double.parseDouble(gold.getText());
            d += Double.parseDouble(silver.getText())/100;
            d += Double.parseDouble(copper.getText())/10000;
            return d;
    }
}

In the end, this is what I know: The non-primitive type SingleField is NOT the problem as it is Serialized and saves to disk successfully. When I queried "e.getCause()" in the class saving this as data, it said "null". When I had it return "e.getMessage", it blamed it on the "javax.swing.GroupLayout". After removing the GroupLayout, "e.getMessage()" just returned "omnitool.MonetaryField$1". Omnitool being the main package. Which is where I'm left. And I just now tried to print a stack trace for you guys (e.getStackTrace().toString), but all it told me was "[Ljava.lang.StackTraceElement;@6ab7501b"... If anyone can explain what that means. Also, I've already gone through a few hours' research on Serialization, but if anyone has any good material they could share, that'd be great!

Siva
  • 1,938
  • 1
  • 17
  • 36
Kyle
  • 151
  • 1
  • 2
  • 14

2 Answers2

6

To print out the stack trace I suggest you use

e.printStackTrace();

It appears you have a non transient field called keys which is the anonymous inner class called omnitool.MonetaryField$1 and this is not Serializable and the reason this won't serialize. You can make it transient, but you would need to re-create on de-serialization by defining a readObject method

From http://docs.oracle.com/javase/7/docs/api/java/io/ObjectInputStream.html you can define a

private void readObject(java.io.ObjectInputStream stream)
     throws IOException, ClassNotFoundException {
    stream.defaultReadObject();
    // re-initialise transient fields.
}

I suggest you avoid confusing GUI components with data transfer objects. You will have a much easier life if you have classes which contain pure data and are easily serialized and separate GUI components which visualise that data.


And I just now tried to print a stack trace for you guys (e.getStackTrace().toString), but all it told me was "[Ljava.lang.StackTraceElement;@6ab7501b"... If anyone can explain what that means.

This means that Java's array.toString is broken and when you attempt to print a plain array it uses Object.toString() which prints the class and System.identHashCode() which is rarely very useful. You need a helper function like Throwable.printStackTrace() or Arrays.toString(array) or print each element yourself to see it.

Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
  • BAM! You, sir, nailed it! Luckily, I don't need it at all to recreate my data. On another note, they both print the same for me if that's normal. Thank you very much! – Kyle Aug 03 '13 at 06:41
  • I don't know what you mean by "they both print the same for me" – Peter Lawrey Aug 03 '13 at 06:45
  • Sorry, I typed that when the answer was shorter. I mean "e.getStackTrace" and its "toString" method. I honestly never use the "toString()", but it was a forgotten relic of trying to get something different to show up. – Kyle Aug 03 '13 at 06:47
  • toString() doesn't include the stack trace. Just the class and the message. The stack trace is expensive both to compute and in term of length of output so I imagine they didn't want this included in toString() by default. – Peter Lawrey Aug 03 '13 at 06:49
0

Problem is that some of your fields can't be serialized (as they don't implement java.io.Serializable interface).

From what you have shown us, you should go for:

transient KeyListener keys = new KeyListener() {

rather than

KeyListener keys = new KeyListener() {

Moreover I don't see the source code of your class: SingleField. If it doesn't implement java.io.Serializable interface, you should mark all those fields transient (or make sure you implement the interface).

Peter Butkovic
  • 11,143
  • 10
  • 57
  • 81