Richard G Baldwin (512) 223-4758, baldwin@austin.cc.tx.us, http://www2.austin.cc.tx.us/baldwin/

JDK 1.1, Object Serialization, Saving/Restoring a GUI Context with Inner Classes

Java Programming, Lecture Notes # 252, Revised 12/17/98.


Introduction

Students in Prof. Baldwin's Advanced Java Programming classes at ACC are responsible for knowing and understanding all of the material in this lesson.

This lesson was originally written on February 28, 1997 using the software and documentation in JDK 1.1. It was upgraded to JDK 1.2 on December 16, 1998.

One of the features of JDK 1.1 is the ability to create persistent objects using object serialization.

Another feature is the Delegation Event Model which, among other things, uses a source/listener model for handling events.

A third feature is the ability to create inner classes and also to use an abbreviated syntax for the definition of anonymous classes and the instantiation of anonymous objects from those classes.

The definition of anonymous classes and the instantiation of anonymous objects of those classes forms a good match for the need to define Listener classes and instantiate Listener objects in the Delegation Event Model.

The purpose of this lesson is to illustrate the use of the Delegation Event Model along with the use of inner classes and object serialization to achieve persistence of a GUI context.

Sample Program

This program uses object serialization to create a persistent GUI object which includes the use of inner classes and the abbreviated syntax for defining new Listener classes and instantiating objects of those classes. The GUI context makes use of the Source/Listener model for event handling that became available with JDK 1.1.

Discussion, Overview

This application combines

to create, save, and later restore an operational GUI object.

The application creates a serializable GUI object having two buttons and a close box. It uses object serialization to write the object to an output file. It closes the file and flushes the output buffer. Then it disposes of the original object.

Then it reopens the file, uses object serialization to read the object from the file and reconstruct it. It assigns the reconstructed object to a new reference variable. The reconstructed object contains a method named activate(), which, when invoked, causes it to become active.

The instance method named activate() is invoked on the reconstructed object to instantiate listener objects and register them to listen for actionPerformed() events on the two buttons and to listen for windowClosing() events on the Frame.

This activation causes the object which was read from the disk file to become operational as a user interface.

The code used to handle the events is based on the source/listener concept of the Delegation Event Model. The code also makes heavy use of inner classes and the abbreviated instantiation syntax.

Interesting Code Fragments

The first fragment shows the import directives and the definition of the controlling class for the program. The main() method in the controlling class does nothing more than to kick off the process.

import java.io.*;
import java.util.*;
import java.awt.*;
import java.awt.event.*;
//=======================================================//
class ObjSer02{
  static public void main(String[] args){
    MyProcess myProcess = new MyProcess();  
  }//end main()
}//end class ObjSer02

The next fragment begins the discussion of the class named MyProcess that controls the overall process of creating the GUI, writing it to the disk, reading it back from the disk, and activating it.

The constructor for this class instantiates a new object of type GUI and writes it to the disk in the manner described in an earlier introductory lesson on object serialization. Then it disposes of the GUI object completely, flushes the output stream, and closes the output stream.

class MyProcess{
  MyProcess(){//constructor
    try{
      FileOutputStream fout = new FileOutputStream("tmp");
      ObjectOutputStream  outStream  =  
                              new ObjectOutputStream(fout);

      GUI myOldObject = new GUI();      
      //write a custom GUI object to disk
      outStream.writeObject(myOldObject); 
      myOldObject.dispose();
      outStream.flush();
      outStream.close();

The next fragment reads the GUI object from the disk in the manner described in an earlier lesson on object serialization. The readObject() method reconstructs the GUI object from the stream of bytes obtained from the disk. A reference to the reconstructed object is assigned to a new reference variable named myNewObject.

Then the activate() method of the reconstructed object is invoked which causes it to become active. We will see later that this method instantiates listener objects and registers them on various components on the GUI object. It also sets the size of the GUI object and makes it visible.

This is followed by typical exception handling catch blocks, ending the definition of the constructor and the definition of the MyProcess class.

      FileInputStream fin = new FileInputStream("tmp");
      ObjectInputStream inStream = 
                                new ObjectInputStream(fin);
      try{
        //read the custom GUI object from the file
        GUI myNewObject = (GUI)inStream.readObject();
        
        //Invoke one of the objects methods to instantiate 
        // and register listener objects on the components
        // in the custom GUI.  This will make it active.
        myNewObject.activate();
        
      }catch(ClassNotFoundException e){
        System.out.println(e);
      }//end catch block
      inStream.close();  
    }catch(IOException e){System.out.println(e);}
  }//end constructor
}//end class MyProcess

The next fragment begins the definition of the class from which the GUI object is instantiated.

There is nothing really special about the constructor for this class. You have seen code like this many times in the past. Note however that the constructor does not register listener objects on the components on the GUI. That task is reserved for the method named activate() that we will discuss later.

However, the constructor does make the GUI visible so that you will see it on your screen before it is serialized to disk and disposed. You will see it reappear on the screen when it is read from the disk, reconstructed, and activated by the code in the MyProcess class descussed above.

class GUI extends Frame implements Serializable{
  Button singButton;
  Button whistleButton;
  public GUI(){//constructor
    setLayout(new FlowLayout());
    setTitle("Copyright 1997, R.G.Baldwin");
    setBackground(Color.green);
    add(singButton = new Button("Sing"));
    add(whistleButton = new Button("Whistle"));
    this.setSize(300,75); 
    this.setVisible(true);//display temporarily
  }//end constructor

The next fragment is the entire activate() method. This is the method by which the object knows how to make itself active after it is read from the disk and reconstructed.

Recall that this method is invoked on the reconstructed object by the constructor of the MyProcess class.

There is nothing special about this code. It is typical of code used to anonymously instantiate anonymous listener objects and register those listener objects on components.

Two action listeners and one window listener are instantiated and registered by this method when it is invoked. The event handlers in these objects control the behavior of the GUI object after it has been reconstructed.

  void activate(){
    this.singButton.addActionListener(
             new ActionListener(){
               public void actionPerformed(ActionEvent e){
                 System.out.println(
                                "I am singing, Tra la la");
               }//end actionPerformed()
             }//end ActionListener
           );//end addActionListener()

    this.whistleButton.addActionListener(
             new ActionListener(){
               public void actionPerformed(ActionEvent e){
                 System.out.println(
                      "I am whistling, Tweet Tweet Tweet");
               }//end actionPerformed()
             }//end ActionListener
           );//end addActionListener()

    this.addWindowListener(
             new WindowAdapter(){
               public void windowClosing(WindowEvent e){
                 System.exit(0);//terminate the program
               }//end windowClosing()
             }//end WindowAdapter
           );//end addWindowListener
    this.setSize(300,75); 
    this.setVisible(true);
    this.repaint();
  }//end activate()
}//end class GUI

When the user clicks on the button labeled Sing, the event handler registered to listen for Action events on that button displays the following text on the screen.

"I am singing, Tra la la"

When the user clicks on the button labeled Whistle, the event handler registered to listen for Action events on that button displays the following text on the screen.

"I am whistling, Tweet, Tweet, Tweet"

When the user clicks the "close" box on the Frame, the event handler registered to listen for windowClosing() events on the Frame terminates the program.

This program was tested using JDK 1.1 under Win95. It was also tested using JDK 1.2 under Win95.

Program Listing

A listing of the program with additional comments follows:

/*File ObjSer02.java, Copyright 1997, R.G.Baldwin
Rev 12/16/98
Designed to be compiled and run under JDK 1.1 or a later
version.


This application combines 

object serialization, 
inner classes, and 
the Delegation Event Model 

to create, save in a file, and later restore an 
operational GUI context.

This application creates a serializable GUI object having 
two buttons and a close box.  It uses object serialization
to write the object to an output file. It closes the file 
and flushes.  Then it disposes of the original object.

Then it reopens the file, uses object serialization to 
read the object from the file and assigns it to a new 
reference variable.  

An instance method named activate() is invoked on the 
new object to instantiate listener objects and register 
them to listen for actionPerformed() events on the two 
buttons and to listen for windowClosing() events on the 
Frame. The method also sets the size of the Frame 
containing the GUI, makes it visible, and requests a
repaint.  This causes the object which was read from the 
disk file to become operational as a user interface.

The code used to handle the events is based on the 
source/listener concept of the Delegation Event Model.  
The code also makes heavy use of Inner Classes and the 
abbreviated instantiation syntax.

An instance method named activate() is defined in the 
GUI class.  This method is used to anonymously define the 
classes for and instantiate three anonymous listener 
objects.  The first two implement the ActionListener 
interface while the third extends the WindowListener 
adapter.

The first two override the actionPerformed() method of the
ActionListener interface while the third overrides the 
windowClosing() method of the WindowListener interface.

The class named GUI extends Frame.  Two Button objects 
labeled Sing and Whistle are instantiated in the GUI 
constructor. They are referenced by variables named 
singButton and whistleButton respectively.  Both buttons
are added to the Frame object.

The first two anonymous listener objects mentioned above 
are registered to listen for actionPerformed() events on 
the two buttons.  The third anonymous listener object is 
registered to listen for windowClosing() events on the 
Frame object.

When the program starts, a Frame object containing the two
buttons appears on the screen momentarily, is written into
the file using object serialization, is disposed of and 
disappears.

The file is closed and reopened and the object is read 
from the file, appears on the screen, and is activated.

When the user clicks on the button labeled Sing, the event
handler registered to listen for Action events on that 
button displays the following text on the screen.

"I am singing, Tra la la" 

When the user clicks on the button labeled Whistle, the 
event handler registered to listen for Action events on 
that button displays the following text on the screen.

"I am whistling, Tweet, Tweet, Tweet"

When the user clicks the "close" box on the Frame, the 
event handler registered to listen for windowClosing() 
events on the Frame terminates the program.

As mentioned above, this version of the program uses 
abbreviated notation in defining and instantiating the 
listener objects.  The abbreviated notation is fairly 
cryptic.

This program was tested using JDK 1.1 under Win95. It was
also tested using JDK 1.2 under Win95.
**********************************************************/
import java.io.*;
import java.util.*;
import java.awt.*;
import java.awt.event.*;
//=======================================================//
class ObjSer02{
  static public void main(String[] args){
  MyProcess myProcess = new MyProcess();  
  }//end main()
}//end class ObjSer02
//=======================================================//
class MyProcess{
  MyProcess(){//constructor
    try{
      FileOutputStream fout = new FileOutputStream("tmp");
      ObjectOutputStream  outStream  =  
                              new ObjectOutputStream(fout);

      GUI myOldObject = new GUI();      
      //write a custom GUI object to disk
      outStream.writeObject(myOldObject); 
      myOldObject.dispose();
      outStream.flush();
      outStream.close();

      FileInputStream fin = new FileInputStream("tmp");
      ObjectInputStream inStream = 
                                new ObjectInputStream(fin);
      try{
        //read the custom GUI object from the file
        GUI myNewObject = (GUI)inStream.readObject();
        
        //Invoke one of the objects methods to instantiate 
        // and register listener objects on the components
        // in the custom GUI.  This will make it active.
        myNewObject.activate();
        
      }catch(ClassNotFoundException e){
        System.out.println(e);
      }//end catch block
      inStream.close();  
    }catch(IOException e){System.out.println(e);}
  }//end constructor
}//end class MyProcess
//=======================================================//

class GUI extends Frame implements Serializable{
  Button singButton;
  Button whistleButton;
  public GUI(){//constructor
    setLayout(new FlowLayout());
    setTitle("Copyright 1997, R.G.Baldwin");
    setBackground(Color.green);
    add(singButton = new Button("Sing"));
    add(whistleButton = new Button("Whistle"));
    this.setSize(300,75); 
    this.setVisible(true);//display temporarily
  }//end constructor
  //-----------------------------------------------------//
    
  void activate(){
    //The code which follows instantiates three anonymous 
    // objects of types ActionListener and WindowAdapter, 
    // and registers them for handling events on the two 
    // corresponding Button objects and the Frame object.
    // This code uses the abbreviated syntax for 
    // anonymous classes and objects.  This method is used
    // to activate the GUI after it is read from the disk
    // file.
    this.singButton.addActionListener(
             new ActionListener(){
               public void actionPerformed(ActionEvent e){
                 System.out.println(
                                "I am singing, Tra la la");
               }//end actionPerformed()
             }//end ActionListener
           );//end addActionListener()

    this.whistleButton.addActionListener(
             new ActionListener(){
               public void actionPerformed(ActionEvent e){
                 System.out.println(
                      "I am whistling, Tweet Tweet Tweet");
               }//end actionPerformed()
             }//end ActionListener
           );//end addActionListener()

    this.addWindowListener(
             new WindowAdapter(){
               public void windowClosing(WindowEvent e){
                 System.exit(0);//terminate the program
               }//end windowClosing()
             }//end WindowAdapter
           );//end addWindowListener
    this.setSize(300,75); 
    this.setVisible(true);
    this.repaint();
  }//end activate()
}//end class GUI
//=======================================================//

-end-