OK, I haven't done much with Serialization. I have a simple program that creates four JPanels with different background colors and adds them to a larger JPanel using a 2 x 2 GridLayout. That larger JPanel is added to a JFrame. I've written my own serialization code to override the default. All I am storing is one integer for each of the four JPanels, representing the background colors of the smaller JPanels. I'm doing this because I have heard that it was a bad idea to actually serialize the JPanels themselves due to portability issues? Is that true? Anyway, it seems to work, but looking at the files that are produced, they are storing a lot of stuff, not just integers. For example:

¬í sr BPanelwv#hçµ [ panelst 
[[LAPanel;xr javax.swing.JPanel]D
„™B$  xr javax.swing.JComponentñ³ã%Àe F 
alignmentXF 
alignmentYZ autoscrollsI flagsZ isAlignmentXSetZ isAlignmentYSetZ verifyInputWhenFocusTargetL accessibleContextt 'Ljavax/accessibility/AccessibleContext;L 	actionMapt Ljavax/swing/ActionMap;L ancestorInputMapt Ljavax/swing/InputMap;L bordert Ljavax/swing/border/Border;L 
focusInputMapq ~ L 
inputVerifiert Ljavax/swing/InputVerifier;L listenerListt %Ljavax/swing/event/EventListenerList;L 	popupMenut Ljavax/swing/JPopupMenu;L vetoableChangeSupportt "Ljava/beans/VetoableChangeSupport;L windowInputMapt Ljavax/swing/ComponentInputMap;xr java.awt.Container@€sý' I containerSerializedDataVersionZ focusCycleRootZ focusTraversalPolicyProviderI ncomponents[ 	componentt [Ljava/awt/Component;L 
dispatchert  Ljava/awt/LightweightDispatcher;L 	layoutMgrt Ljava/awt/LayoutManager;L maxSizet Ljava/awt/Dimension;xr java.awt.Component•ê¦Y×<¤š #I boundsOpI componentSerializedDataVersionZ enabledJ 	eventMaskZ focusTraversalKeysEnabledZ 	focusableI heightZ 
ignoreRepaintI isFocusTraversableOverriddenZ isPackedZ 
maxSizeSetZ 
minSizeSetZ nameExplicitlySetZ 
newEventsOnlyZ prefSizeSetZ validZ visibleI widthI xI yL accessibleContextq ~ L 
backgroundt Ljava/awt/Color;L 
changeSupportt "Ljava/beans/PropertyChangeSupport;L cursort Ljava/awt/Cursor;L 
dropTargett Ljava/awt/dnd/DropTarget;[ focusTraversalKeyst [Ljava/util/Set;L fontt Ljava/awt/Font;L 
foregroundq ~ L localet Ljava/util/Locale;L maxSizeq ~ L minSizeq ~ L namet Ljava/lang/String;L peerFontq ~ L popupst Ljava/util/Vector;L prefSizeq ~ xp                                       psr  javax.swing.plaf.ColorUIResourcekSùŸòêæ’  xr java.awt.Color¥ƒ3u F falphaI valueL cst Ljava/awt/color/ColorSpace;[ 	frgbvaluet [F[ fvalueq ~  xp    ÿîîîpppppppsr javax.swing.plaf.FontUIResourceBćÁ"‹G  xr 
java.awt.FontÅ¡5æÌÞVs I fontSerializedDataVersionF 	pointSizeI sizeI styleL fRequestedAttributest Ljava/util/Hashtable;L nameq ~ xp   A@         pt Dialogxsq ~     ÿ333pppsr java.util.Locale~ø`œ0ùì I hashcodeL countryq ~ L languageq ~ L variantq ~ xpÿÿÿÿt USt ent  pppppppsr java.awt.ComponentOrientationÆê§E¡œcÌ I orientationxp   ppx        ur [Ljava.awt.Component;‰
æªu  xp   sr APaneld5™\ÒÀZ I 
colorIndexxq ~                                        psq ~     ÿ ÿÿpppppppq ~ %q ~ 'q ~ )pppppppq ~ .ppx         uq ~ /    psr java.awt.FlowLayout›6K[ù9 I alignZ alignOnBaselineI hgapI newAlignI serialVersionOnStreamI vgapxp                pppx           @	  ppppppsr #javax.swing.event.EventListenerList±6Æ}„êÖD  xppxpppw    xxw   xsq ~ 1                                       psq ~     ÿ   pppppppq ~ %q ~ 'q ~ )pppppppq ~ .ppx         q ~ 4psq ~ 5                pppx           @	  ppppppsq ~ 7pxpppw    xxw   xsq ~ 1                                       pq ~ :ppppq ~ %q ~ 'q ~ )pppppppq ~ .ppx         q ~ 4psq ~ 5                pppx           @	  ppppppsq ~ 7pxpppw    xxw   xsq ~ 1                                       pq ~ :ppppq ~ %q ~ 'q ~ )pppppppq ~ .ppx         q ~ 4psq ~ 5                pppx           @	  ppppppsq ~ 7pxpppw    xxw   xpsr java.awt.GridLayout™#úëKÜë I colsI hgapI rowsI vgapxp              pppx           @	  ppppppsq ~ 7pxpppw    xxq ~ 2q ~ 9q ~ =q ~ @x

It's clearly storing a lot of the stuff that I'm not asking it to, like the fact that it's a GridLayout. I don't need or want it to store all the dimensions and stuff like that, but it is anyway. Why? And am I correct that you're not supposed to serialize JPanels due to portability? That's why I just wanted to store an integer and recreate everything from that.


Here's the code:

import java.awt.*;
import java.io.*;
import javax.swing.*;


public class BPanel extends JPanel implements Serializable
{
     APanel panels[][];

     public BPanel (APanel thepanels[][])
     {
         panels = thepanels;
         this.setLayout (new GridLayout (2, 2));
         for (int i = 0; i < 2; i++)
         {
             for (int j = 0; j < 2; j++)
             {
                 this.add (panels[i][j]);
             }
         }
     }



    private void readObject(ObjectInputStream ois)
    {
        try
        {
             panels = new APanel[2][2];
             this.setLayout (new GridLayout (2, 2));
             for (int i = 0; i < 2; i++)
             {
                 for (int j = 0; j < 2; j++)
                 {
                     panels[i][j] = (APanel) ois.readObject();
                     this.add (panels[i][j]);
                 }
             }
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }


    private void writeObject(ObjectOutputStream os)
    {
        try
        {
             for (int i = 0; i < 2; i++)
             {
                 for (int j = 0; j < 2; j++)
                 {
                     os.writeObject (panels[i][j]);
                 }
             }
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }
}
import java.awt.*;
import java.io.*;
import javax.swing.*;


public class APanel extends JPanel implements Serializable
{
     static Color colors[] = {Color.RED, Color.BLACK, Color.MAGENTA, Color.BLUE, Color.CYAN};
     int colorIndex;



     public APanel (int index)
     {
         this.setBackground (colors[index]);
         colorIndex = index;
     }




    private void readObject(ObjectInputStream ois)
    {
        try
        {
            colorIndex = ois.readInt();
            this.setBackground(colors[colorIndex]);
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }

    
    private void writeObject(ObjectOutputStream os)
    {
        try
        {
            os.writeInt (colorIndex);
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }

}
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.*;
import javax.swing.*;


public class Main
{
    public static void main (String args[])
    {
        if (args.length != 2)
        {
            System.out.println ("Usage: 'java Main R filename' or 'java Main W filename'");
            System.exit (1);
        }

        boolean read = false;
        if (args[0].equals("R"))
            read = true;

        String filename = args[1];


        BPanel bpanel = null;
        APanel apanels[][] = new APanel[2][2];

        if (!read)
        {
            Random random = new Random ();
            int colorIndexes[][] = new int[2][2];

            for (int i = 0; i < 2; i++)
            {
                for (int j = 0; j < 2; j++)
                {
                    colorIndexes[i][j] = random.nextInt(APanel.colors.length);
                    apanels[i][j] = new APanel (colorIndexes[i][j]);
                }
            }

            bpanel = new BPanel (apanels);

            try
            {
                FileOutputStream fs = new FileOutputStream(filename);
                ObjectOutputStream os = new ObjectOutputStream(fs);

/*                for (int i = 0; i < 2; i++)
                {
                    for (int j = 0; j < 2; j++)
                    {
                        os.writeObject (apanels[i][j]);
                    }
                }*/

                os.writeObject(bpanel);
                os.flush();
                os.close();
            }
            catch (Exception e)
            {
                e.printStackTrace();
                System.exit (1);
            }
        }
        else
        {
            try
            {
                FileInputStream fis = new FileInputStream(filename);
                ObjectInputStream ois = new ObjectInputStream(fis);

/*                for (int i = 0; i < 2; i++)
                {
                    for (int j = 0; j < 2; j++)
                    {
                        apanels[i][j] = (APanel) ois.readObject();
                    }
                }*/

               // bpanel = new BPanel (apanels);
                bpanel = (BPanel) ois.readObject ();

                ois.close();
            }
            catch (Exception e)
            {
                e.printStackTrace();
                System.exit (1);
            }

        }


        JFrame frame = new JFrame ();
        frame.setSize (500, 500);
        frame.setVisible (true);
        frame.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
        frame.getContentPane ().add (bpanel);
    }
}

Binary serialization not only store the state but it also preserve type fidelity. Classes - XMLEncoder, and XMLDecoder of package java.beans support xml serialization.

Serialization stores everything about your class instances, so if you don't want all that you have the override the appropriate methods.
The portability issue is that Sun warn you that the details of how these things are serialised may change in future releases, so you cannot assume that an object serialised under 1.6.14 will de-serialise under 1.7. Depending on why you are serialising, and how long the data will be stored, this may or may not be as issue for you.

I thought I WAS overriding the appropriate methods when I wrote these functions?

private void readObject(ObjectInputStream ois)
private void writeObject(ObjectOutputStream os)

Yes, sorry, I didn't mean to imply that you weren't!

OK, so if I am successfully overriding the writeObject function, here it is:

private void writeObject(ObjectOutputStream os)
    {
        try
        {
            os.writeInt (colorIndex);
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }

I'm writing a single integer to the file. That's it. I have four instances of APanel, so that'd be four integers total written to the file, right? But if you look at the file from post 1, there's clearly not just four integers there. What is going on?

I found this in Sun's serialisation spec:
"Each subclass of a serializable object may define its own writeObject method. ... When implemented, the class is only responsible for having its own fields, not those of its supertypes or subtypes."
Implies to me that the superclass's (JPanel's) fields will be serialised automatically?

Implies that to me too. I found this option

writeObjectOverride: Method used by subclasses to override the default writeObject method. This method is called by trusted subclasses of ObjectInputStream that constructed ObjectInputStream using the protected no-arg constructor. The subclass is expected to provide an override method with the modifier "final".

It's a protected member of the ObjectOutputStream class though, so when I replace writeObject with writeObjectOverride in my code, I get errors. I think I have some more research and thinking to do. Serialization is pretty new to me.

writeObjectOverride: being protected shouldn't stop you overriding it. What errors do you get?

Have you looked at Externalizable? http://java.sun.com/j2se/1.3/docs/api/java/io/Externalizable.html looks like it may be what you are looking for?

Finally - so the default serialization writes a k or two of extra data. Is this really a problem, or just an itch you can't scratch? (ps: even a single int gets padded into a 1024 bye block)

Ha Ha. I guess I'm posting like a noob, talking about errors, but then not posting them. I'll run it again and report back in more detail. But I AM a noob as far as serialization goes and I don't know what's important and what isn't. Basically I want to be able to "save" a JPanel and then recreate it later. The JPanel will be more complicated than what I'm displaying here. No assumptions can be made that the the width/length/Layout Manager, etc. of the JPanel before it's saved will be the same as when it's later retrieved. So I'm getting paranoid that if I save a JPanel that resides in a 450 x 450 container, then later try to add it to a 600 x 600 container, somehow the fact that it was 450 x 450 before will screw it up. Basically in the example, 4 integers create the four JPanels. The constructors need only one integer to create what they need, so I'm thinking that's all I want/need to save.

It's a cross between an itch that I can't scratch and the fact that I don't know what's important and what isn't. I can afford a few extra Kilobytes of storage, but it's a personal knowledge thing, plus I'm worried about portability across operating systems and JRE versions.

I saw Externalizable too and played with it. It gave me big files too. I'll post back again with more details.

Okay, code is here (almost exactly same as in post 1):

import java.awt.*;
import java.io.*;
import javax.swing.*;


public class APanel extends JPanel implements Serializable
{
     static Color colors[] = {Color.RED, Color.BLACK, Color.MAGENTA, Color.BLUE, Color.CYAN};
     int colorIndex;



     public APanel (int index)
     {
         this.setBackground (colors[index]);
         colorIndex = index;
     }



/*
    private void readObject(ObjectInputStream ois)
    {
    }

    
    private void writeObject(ObjectOutputStream os)
    {
    }
*/
    public void writeObjectOverride(ObjectOutput out)
    {
        try
        {
            out.writeInt (colorIndex);
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }

    public void readObjectOverride(ObjectInput in)
    {
        try
        {
            colorIndex = in.readInt();
            this.setBackground(colors[colorIndex]);
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }
}
import java.awt.*;
import java.io.*;
import javax.swing.*;


public class BPanel extends JPanel implements Serializable
{
     APanel panels[][];

     public BPanel (APanel thepanels[][])
     {
         panels = thepanels;
         this.setLayout (new GridLayout (2, 2));
         for (int i = 0; i < 2; i++)
         {
             for (int j = 0; j < 2; j++)
             {
                 this.add (panels[i][j]);
             }
         }
     }



    private void readObjectOveride(ObjectInputStream ois)
    {
        try
        {
             panels = new APanel[2][2];
             this.setLayout (new GridLayout (2, 2));
             for (int i = 0; i < 2; i++)
             {
                 for (int j = 0; j < 2; j++)
                 {
                     panels[i][j] = (APanel) ois.readObjectOverride();
                     this.add (panels[i][j]);
                 }
             }
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }


    private void writeObjectOverride(ObjectOutputStream os)
    {
        try
        {
             for (int i = 0; i < 2; i++)
             {
                 for (int j = 0; j < 2; j++)
                 {
                     os.writeObjectOverride (panels[i][j]);
                 }
             }
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }
}
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.*;
import javax.swing.*;


public class Main
{
    public static void main (String args[])
    {
        if (args.length != 2)
        {
            System.out.println ("Usage: 'java Main R filename' or 'java Main W filename'");
            System.exit (1);
        }

        boolean read = false;
        if (args[0].equals("R"))
            read = true;

        String filename = args[1];


        BPanel bpanel = null;
        APanel apanels[][] = new APanel[2][2];

        if (!read)
        {
            Random random = new Random ();
            int colorIndexes[][] = new int[2][2];

            for (int i = 0; i < 2; i++)
            {
                for (int j = 0; j < 2; j++)
                {
                    colorIndexes[i][j] = random.nextInt(APanel.colors.length);
                    apanels[i][j] = new APanel (colorIndexes[i][j]);
                }
            }

            bpanel = new BPanel (apanels);

            try
            {
                FileOutputStream fs = new FileOutputStream(filename);
                ObjectOutputStream os = new ObjectOutputStream(fs);

/*                for (int i = 0; i < 2; i++)
                {
                    for (int j = 0; j < 2; j++)
                    {
                        os.writeObject (apanels[i][j]);
                    }
                }*/

                os.writeObjectOverride (bpanel);
                os.flush();
                os.close();
            }
            catch (Exception e)
            {
                e.printStackTrace();
                System.exit (1);
            }
        }
        else
        {
            try
            {
                FileInputStream fis = new FileInputStream(filename);
                ObjectInputStream ois = new ObjectInputStream(fis);

/*                for (int i = 0; i < 2; i++)
                {
                    for (int j = 0; j < 2; j++)
                    {
                        apanels[i][j] = (APanel) ois.readObject();
                    }
                }*/

               // bpanel = new BPanel (apanels);
                bpanel = (BPanel) ois.readObjectOverride ();

                ois.close();
            }
            catch (Exception e)
            {
                e.printStackTrace();
                System.exit (1);
            }
        }


        JFrame frame = new JFrame ();
        frame.setSize (500, 500);
        frame.setVisible (true);
        frame.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
        frame.getContentPane ().add (bpanel);
    }
}

Errors (line numbers are same as above - lines 35, 55 in class BPanel.java, lines 58 and 84 in Main.java). All deal with the fact that readObjectOverride and writeObjectOverride are protected, so I don't have access to them, at least not the way I am calling them. Am I calling them wrong? Obviously I am, but what's the right way?

C:\Users\Rick\Documents\NetBeansProjects\SerialTrial3\src\BPanel.java:35: readObjectOverride() has protected access in java.io.ObjectInputStream
panels[j] = (APanel) ois.readObjectOverride();
C:\Users\Rick\Documents\NetBeansProjects\SerialTrial3\src\BPanel.java:55: writeObjectOverride(java.lang.Object) has protected access in java.io.ObjectOutputStream
os.writeObjectOverride (panels[j]);
C:\Users\Rick\Documents\NetBeansProjects\SerialTrial3\src\Main.java:58: writeObjectOverride(java.lang.Object) has protected access in java.io.ObjectOutputStream
os.writeObjectOverride (bpanel);
C:\Users\Rick\Documents\NetBeansProjects\SerialTrial3\src\Main.java:84: readObjectOverride() has protected access in java.io.ObjectInputStream
bpanel = (BPanel) ois.readObjectOverride ();

You are overriding a protected method with a private one, try making it protected?

Hi JC,
I am bit confused while reading a following text.

This text is taken from Java documentation:

Method used by subclasses to override the default writeObject method. This method is called by trusted subclasses of ObjectInputStream that constructed ObjectInputStream using the protected no-arg constructor. The subclass is expected to provide an override method with the modifier "final".

Another text from the java doc in my own words - for writeObject and readObject:
For custom serialization, Java platform provides three additional methods - readObject(), validateObject(), and writeObject(). The readObject and writeObject methods are not part of any interface to be implemented or class to be extended. Even through the serialization support calls readObject and writeObject, they must be private methods.

I modified the example posted by sir vernon. I wrote it with both - readObjectOverride/writeObjectOverride and readObject/writeObject, and it works. I also wrote it with XMLEncoder/Decoder.

Yes, that really is confusing! But if you now have it working....

You are overriding a protected method with a private one, try making it protected?

If you mean change the word "public" to "protected" on lines 31 and 43 in APanel, and "private" to "protected" in lines 25 and 47 in BPanel, that appears to have no effect at all. Same errors. If that's not what you mean, can you elaborate please?

Hi JC,
I am bit confused while reading a following text.

This text is taken from Java documentation:


Another text from the java doc in my own words - for writeObject and readObject:
For custom serialization, Java platform provides three additional methods - readObject(), validateObject(), and writeObject(). The readObject and writeObject methods are not part of any interface to be implemented or class to be extended. Even through the serialization support calls readObject and writeObject, they must be private methods.

I modified the example posted by sir vernon. I wrote it with both - readObjectOverride/writeObjectOverride and readObject/writeObject, and it works. I also wrote it with XMLEncoder/Decoder.

Yes, that really is confusing! But if you now have it working....

adatapost may have have it working, but I don't! adatapost, would you mind posting your revised code so I can try it out?

It's my pleasure.

I am attaching zip with this post.
org.zip - original code - as per your post.
override.zip - some modification.
writeobject.zip - override writeObject method
xmlser.zip - XML ser.

commented: Thanks. +18

Okay, thanks. That's some good food for thought. I was hoping to get a nice text file with four integers. I'm going to study up and take a few tutorials and see if I can understand serialization a little better. These examples will help in that process. I think I like the XML one best since it's in plain text. Thanks a lot to both of you.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.