14. Files and Streams: Input/Output Techniques

14.5. Object Serialization: Reading and Writing Objects

The examples in the previous sections showed how to perform I/O op- erations on simple binary data or text. The java.io package also pro- vides methods for reading and writing objects, a process known as ob- ject serialization. Objects can be converted into a sequence of bytes, or serialized, by using the ObjectOutputStream class, and they can be de- serialized, or converted from bytes into a structured object, by using the ObjectInputStream class (Fig. 11.26). Despite the complexity of the serialization/deserialization processes, the methods in these classes make the task just as easy as reading and writing primitive data.

To illustrate object serialization, let’s begin by defining a Student class (Fig. 11.27). In order to serialize an object, it must be a member of a class that implements the Serializable interface. The Serializable inter- face is a marker interface, an interface that doesn’t define any methods or constants but just serves to designate whether an object can be serialized or not.

The Student class contains its own I/O methods, readFromFile() and writeToFile(). This is an appropriate object-oriented design. The Student class encapsulates all the relevant information needed to read and write its data.

 

JAVA EFFECTIVE DESIGN

I/O Design. If an object is going to be

input and output to and from files, it should define its own I/O methods. An object contains all the relevant information needed to perform I/O correctly.

 

Note the definition of the writeToFile() method, which performs the output task. This method’s FileOutputStream parameter is used to create an ObjectOutputStream, whose writeObject() method

 

SECTION 6 Object Serialization531

,,

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

J

Figure 11.27: The serializable Student class.

 

 

writes the object into the file. To output a Student object, we merely invoke the writeObject() method. This method writes out the current values of all the object’s public and private fields. In this case, the method would write a String for the object’s name, an int for the object’s year, and a double for the object’s gpa.

Although our example doesn’t require it, the writeObject() method can also handle fields that refer to other objects. For example, suppose our Student object provided a field for courses that contained a reference to an array of objects, each of which described a course the student has taken. In that case, the writeObject() method would serialize the array and all its objects (assuming they are serializable). Thus, when a complex object is serialized, the result would be a complex structure that contains all the data linked to that root object.

 

Object deserialization


Object deserialization, as shown in the readFromFile() method, is simply the reverse of the serialization process. The readObject() method reads one serialized object from the ObjectInputStream. Its result type is Object, so it is necessary to cast the result into the proper type. In our example we use a local Student variable to store the object as it is input. We then copy each field of the local object to this object.

Note that the readFromFile() method throws both the IOException and ClassNotFoundException. An IOException will be generated if the file you are attempting to read does not contain serialized objects of the

correct type. Objects that can be input by readObject() are those that were output by writeObject(). Thus, just as in the case of binary I/O, it is best to design an object’s input and output routines together so that they are compatible. The ClassNotFoundException will be thrown if the Student class cannot be found. This is needed to determine how to deserialize the object.

 

 

 

 

 

 

 

 

 

 

 

The ObjectIO Class

Given the Student class, let’s now write a user interface that can read and write Student objects. We can use the same interface we used in the BinaryIO program. The only things we need to change are the write- Records() and readRecords() methods. Everything else about this program will be exactly the same as in BinaryIO.

Figure 11.28 provides the full implementation of the ObjectIO class. Note that the writeRecords() method will still write five random records to the data file. The difference in this case is that we will call the Student.writeToFile() method to take care of the actual output op- erations. The revised algorithm will create a new Student object, using randomly generated data for its name, year, and GPA and then invoke its writeToFile() to output its data. Note how a FileOutputStream is created and passed to the Student.writeToFile() method.

The readRecords() method (Fig. 11.28, Part II) will read data from a file containing serialized Student objects. To do so, it first cre- ates a Student object and then invokes its readFromFile() method, passing it a FileInputStream. Note how the FileInputStream is created and, unlike in BinaryIO, the inner try block is exited by an IOException rather than an EOFException.

 

SECTION 6 Java Library : JFileChooser533

,,

import j avax . swing . ;// S w i n g c o m p o n e n t s

import j ava . awt . ;

import j ava . io . ;

import j ava . awt . event . ;

public c l a s s Object IO extends JFrame implements Action Listener

private JText Area display = new JText Area ( ) ;

private JButton read = new JButton ( ”Read From F i l e ) ,

write = new JButton ( Write to F i l e ) ;

private J T e x t F i e l d nameField = new J T e x t F i e l d ( 1 0 ) ;

private JLabel prompt = new JLabel ( Filename : , JLabel . RIGHT ) ;

private JPanel commands = new JPanel ( ) ;

 

public Object IO ( )

super ( Object IO Demo” ) ;// S e t wi n do w t i t l e

read . add Action Listener ( t h i s ) ; write . add Action Listener ( t h i s ) ;

commands . set Layout ( new GridLayout ( 2 , 2 , 1 , 1 ) ) ;

commands . add ( prompt ) ;// C o n t r o l p a n e l

commands . add ( nameField ) ; commands . add ( read ) ; commands . add ( write ) ;

display . setLineWrap ( t rue ) ;

t h i s . get Content Pane ( ) . set Layout ( new BorderLayout ( ) ) ;

t h i s . get Content Pane ( ) . add ( North” , commands ) ;

t h i s . get Content Pane ( ) . add ( new JS c r o l l P a n e ( display ) ) ;

t h i s . get Content Pane ( ) . add ( Center , display ) ;

} // O b j e c t I O

public void action Performed ( ActionEvent evt ) S t r i n g fileName = nameField . get Text ( ) ;

i f ( evt . get Source ( ) == read ) readRecords ( fileName ) ;

e ls e

write Records ( fileName ) ;

// a c t i o n P e r f o r m e d ( )

\J

Figure 11.28: Part I of the ObjectIO class, which provides an interface to reading and writing files of Students.

 

SELF-STUDY EXERCISE

EXERCISE 11.6 Given the following definition, would a binary file con- sisting of several SomeObjects be readable by either the BinaryIO or the ObjectIO programs? Explain.

,,

 

 

 

 

J

 

,,

private void readRecords ( S t r i n g fileName )

t r y

File Input Stream in Stream = new File Input Stream ( fileName ) ;// Ope n a s t r e a m

display . s e t T e xt ( ”Name t Year tGPA n” ) ;

t r y

while ( t rue )// I n f i n i t e l o o p

Student student = new Student ( ) ;// C r e a t e a s t u d e n t i n s t a n c e

student . readFrom File ( in Stream ) ;//a n d h a v e i t r e a d a n o b j e c t

display . append ( student . t o S t r i n g ( ) +\n” ) ; //a n d d i s p l a y i t

}

} catch ( IOException e ) {// U n t i l I O E x c e p t i o n

}in Stream . c l o s e ( ) ;// C l o s e t h e s t r e a m

} catch ( File Not FoundException e ) {

display . append ( ”IOERROR : F i l e NOT Found : + fileName + \n” ) ;

} catch ( IOException e ) {

display . append ( ”IOERROR : + e . getMessage ( ) + \n” ) ;

} catch ( ClassNotFoundException e ) {

display . append ( ”ERROR: Class NOT found + e . getMessage ( ) + \n” ) ;

}

} // r e a d R e c o r d s ( )

private void write Records ( S t r i n g fileName )

t r y

File Output Stream outStream = new File Output Stream ( fileName ) ; // Ope n s t r e a m

for ( in t k = 0 ; k < 5 ; k++)// G e n e r a t e 5 r a n d o m o b j e c t s

S t r i n g name = ”name” + k ;// Name

in t year = ( in t ) ( 2 0 0 0 + Math . random ( )4 ) ;// C l a s s y e a r

double gpa = Math . random ( )1 2 ;// GPA

Student student = new Student ( name , year , gpa ) ; // C r e a t e t h e o b j e c t

display . append ( ”Output : + student . t o S t r i n g ( ) + n” ) ; // a n d d i s p l a y i t

student . wr i te To File ( outStream ) ;//a n d t e l l i t t o w r i t e d a t a

// f o r

outStream . c l o s e ( ) ;

} catch ( IOException e ) {

display . append ( ”IOERROR : + e . getMessage ( ) + \n” ) ;

}

} // w r i t e R e c o r d s ( )

public s t a t i c void main ( S t r i n g args [ ] ) Object IO io = new Object IO ( ) ;

io . s e t S i z e ( 4 0 0 , 2 0 0 ) ; io . s e t V i s i b l e ( t rue ) ;

io . addWindowListener ( new WindowAdapter ( )

public void windowClosing ( WindowEvent e ) System . e x i t ( 0 ) ;// Q u i t t h e a p p l i c a t i o n

}

} ) ;

} // m a i n ( )

// O b j e c t I O

\J

Figure 11.28: (continued) The ObjectIO class, Part II.

 

SECTION 11.7 From the Java Libraryjavax.swing.JFileChooser 535