Object Serialization

In Section 14.5, we demonstrated how to write the individual fields of an AccountRecord object into a file as text, and how to read those fields from a file and place their values into an AccountRecord object in memory. In the examples, AccountRecord was used to aggregate the information for one record. When the instance variables for an AccountRecord were output to a disk file, certain information was lost, such as the type of each value. For instance, if the value "3" were read from a file, there is no way to tell if the value came from an int, a String or a double. We have only data, not type information, on a disk. If the program that is going to read this data "knows" what object type the data corresponds to, then the data is simply read into objects of that type. For example, in Section 14.5.2, we know that we are inputting an int (the account number), followed by two Strings (the first and last name) and a double (the balance). We also know that these values are separated by spaces, with only one record on each line. Sometimes we will not know exactly how the data is stored in a file. In such cases, we would like to read or write an entire object from a file. Java provides such a mechanism, called object serialization. A so-called serialized object is an object represented as a sequence of bytes that includes the object's data as well as information about the object's type and the types of data stored in the object. After a serialized object has been written into a file, it can be read from the file and deserializedthat is, the type information and bytes that represent the object and its data can be used to recreate the object in memory.

Classes ObjectInputStream and ObjectOutputStream, which respectively implement the ObjectInput and ObjectOutput interfaces, enable entire objects to be read from or written to a stream (possibly a file). To use serialization with files, we initialize ObjectInputStream and ObjectOutputStream objects with stream objects that read from and write to filesobjects of classes FileInputStream and FileOutputStream, respectively. Initializing stream objects with other stream objects in this manner is sometimes called wrappingthe new stream object being created wraps the stream object specified as a constructor argument. To wrap a FileInputStream in an ObjectInputStream, for instance, we pass the FileInputStream object to the ObjectInputStream's constructor.

The ObjectOutput interface contains method writeObject, which takes an Object that implements interface Serializable (discussed shortly) as an argument and writes its information to an OutputStream. Correspondingly, the ObjectInput interface contains method readObject, which reads and returns a reference to an Object from an InputStream. After an object has been read, its reference can be cast to the object's actual type. As you will see in Chapter 24, Networking, applications that communicate via a network, such as the Internet, can also transmit entire objects across the network.

In this section, we create and manipulate sequential-access files using object serialization. Object serialization is performed with byte-based streams, so the sequential files created and manipulated will be binary files. Recall that binary files cannot be viewed in standard text editors. For this reason, we write a separate application that knows how to read and display serialized objects.

14.6.1. Creating a Sequential-Access File Using Object Serialization

We begin by creating and writing serialized objects to a sequential-access file. In this section, we reuse much of the code from Section 14.5, so we focus only on the new features.

Defining the AccountRecordSerializable Class

Let us begin by modifying our AccountRecord class so that objects of this class can be serialized. Class AccountRecordSerializable (Fig. 14.17) implements interface Serializable (line 7), which allows objects of AccountRecordSerializable to be serialized and deserialized with ObjectOutputStreams and ObjectInputStreams. Interface Serializable is a tagging interface. Such an interface contains no methods. A class that implements Serializable is tagged as being a Serializable object. This is important because an ObjectOutputStream will not output an object unless it is a Serializable object, which is the case for any object of a class that implements Serializable.

Figure 14.17. AccountRecordSerializable class for serializable objects.

(This item is displayed on pages 699 - 700 in the print version)

 1 // Fig. 14.17: AccountRecordSerializable.java
 2 // A class that represents one record of information.
 3 package com.deitel.jhtp6.ch14; // packaged for reuse
 4
 5 import java.io.Serializable;
 6
 7 public class AccountRecordSerializable implements Serializable
 8 {
 9 private int account;
10 private String firstName;
11 private String lastName;
12 private double balance;
13
14 // no-argument constructor calls other constructor with default values
15 public AccountRecordSerializable()
16 {
17 this ( 0, "", "", 0.0 );
18 } // end no-argument AccountRecordSerializable constructor
19
20 // four-argument constructor initializes a record
21 public AccountRecordSerializable(
22 int acct, String first, String last, double bal )
23 {
24 setAccount( acct );
25 setFirstName( first );
26 setLastName( last );
27 setBalance( bal );
28 } // end four-argument AccountRecordSerializable constructor
29
30 // set account number
31 public void setAccount( int acct )
32 {
33 account = acct;
34 } // end method setAccount
35
36 // get account number
37 public int getAccount()
38 {
39 return account;
40 } // end method getAccount
41
42 // set first name
43 public void setFirstName( String first )
44 {
45 firstName = first;
46 } // end method setFirstName
47
48 // get first name
49 public String getFirstName()
50 {
51 return firstName;
52 } // end method getFirstName
53
54 // set last name
55 public void setLastName( String last )
56 {
57 lastName = last;
58 } // end method setLastName
59
60 // get last name
61 public String getLastName()
62 {
63 return lastName;
64 } // end method getLastName
65
66 // set balance
67 public void setBalance( double bal )
68 {
69 balance = bal;
70 } // end method setBalance
71
72 // get balance
73 public double getBalance()
74 {
75 return balance;
76 } // end method getBalance
77 } // end class AccountRecordSerializable

In a class that implements Serializable, the programmer must ensure that every instance variable of the class is a Serializable type. Any instance variable that is not serializable must be declared transient to indicate that it is not Serializable and should be ignored during the serialization process. By default, all primitive-type variables are serializable. For variables of reference types, you must check the definition of the class (and possibly its superclasses) to ensure that the type is Serializable. By default, array objects are serializable. However, if the array contains references to other objects, those objects may or may not be serializable.

Class AccountRecordSerializable contains private data members account, firstName, lastName and balance. This class also provides public get and set methods for accessing the private fields.

Now let us discuss the code that creates the sequential-access file (Fig. 14.18Fig. 14.19). We concentrate only on new concepts here. As stated in Section 14.3, a program can open a file by creating an object of stream class FileInputStream or FileOutputStream. In this example, the file is to be opened for output, so the program creates a FileOutputStream (line 21 of Fig. 14.18). The string argument that is passed to the FileOutputStream's constructor represents the name and path of the file to be opened. Existing files that are opened for output in this manner are truncated. Note that the .ser file extension is usedwe use this file extension for binary files that contain serialized objects.

Figure 14.18. Sequential file created using ObjectOutputStream.

(This item is displayed on pages 701 - 702 in the print version)

 1 // Fig. 14.18: CreateSequentialFile.java
 2 // Writing objects sequentially to a file with class ObjectOutputStream. 
 3 import java.io.FileOutputStream; 
 4 import java.io.IOException; 
 5 import java.io.ObjectOutputStream; 
 6 import java.util.NoSuchElementException;
 7 import java.util.Scanner; 
 8
 9 import com.deitel.jhtp6.ch14.AccountRecordSerializable;
10
11 public class CreateSequentialFile
12 {
13 private ObjectOutputStream output; // outputs data to file
14
15 // allow user to specify file name
16 public void openFile()
17 {
18 try // open file
19 {
20 output = new ObjectOutputStream( 
21  new FileOutputStream( "clients.ser" ) );
22 } // end try
23 catch ( IOException ioException )
24 {
25 System.err.println( "Error opening file." );
26 } // end catch
27 } // end method openFile
28
29 // add records to file
30 public void addRecords()
31 {
32 AccountRecordSerializable record; // object to be written to file
33 int accountNumber = 0; // account number for record object
34 String firstName; // first name for record object
35 String lastName; // last name for record object
36 double balance; // balance for record object
37
38 Scanner input = new Scanner( System.in );
39
40 System.out.printf( "%s
%s
%s
%s

",
41 "To terminate input, type the end-of-file indicator ",
42 "when you are prompted to enter input.",
43 "On UNIX/Linux/Mac OS X type  d then press Enter",
44 "On Windows type  z then press Enter" );
45
46 System.out.printf( "%s
%s",
47 "Enter account number (> 0), first name, last name and balance.",
48 "? " );
49
50 while ( input.hasNext() ) // loop until end-of-file indicator
51 {
52 try // output values to file
53 {
54 accountNumber = input.nextInt(); // read account number
55 firstName = input.next(); // read first name
56 lastName = input.next(); // read last name
57 balance = input.nextDouble(); // read balance
58
59 if ( accountNumber > 0 )
60 {
61 // create new record
62 record = new AccountRecordSerializable( accountNumber,
63  firstName, lastName, balance ); 
64 output.writeObject( record ); // output record 
65 } // end if
66 else
67 {
68 System.out.println(
69 "Account number must be greater than 0." );
70 } // end else
71 } // end try
72 catch ( IOException ioException )
73 {
74 System.err.println( "Error writing to file." );
75 return;
76 } // end catch
77 catch ( NoSuchElementException elementException )
78 {
79 System.err.println( "Invalid input. Please try again." );
80 input.nextLine(); // discard input so user can try again
81 } // end catch
82
83 System.out.printf( "%s %s
%s", "Enter account number (>0),",
84 "first name, last name and balance.", "? " );
85 } // end while
86 } // end method addRecords
87
88 // close file and terminate application
89 public void closeFile()
90 {
91 try // close file
92 {
93 if ( output != null )
94 output.close();
95 } // end try
96 catch ( IOException ioException )
97 {
98 System.err.println( "Error closing file.");
99 System.exit( 1 );
100 } // end catch
101 } // end method closeFile
102 } // end class CreateSequentialFile

Figure 14.19. Testing class CreateSequentialFile.

(This item is displayed on page 703 in the print version)

 1 // Fig. 14.19: CreateSequentialFileTest.java
 2 // Testing class CreateSequentialFile.
 3
 4 public class CreateSequentialFileTest
 5 {
 6 public static void main( String args[] )
 7 {
 8 CreateSequentialFile application = new CreateSequentialFile();
 9
10 application.openFile();
11 application.addRecords();
12 application.closeFile();
13 } // end main
14 } // end class CreateSequentialFileTest
 
To terminate input, type the end-of-file indicator
when you are prompted to enter input.
On UNIX/Linux/Mac OS X type  d then press Enter
On Windows type  z then press Enter

Enter account number (> 0), first name, last name and balance.
? 100 Bob Jones 24.98
Enter account number (> 0), first name, last name and balance.
? 200 Steve Doe -345.67
Enter account number (> 0), first name, last name and balance.
? 300 Pam White 0.00
Enter account number (> 0), first name, last name and balance.
? 400 Sam Stone -42.16
Enter account number (> 0), first name, last name and balance.
? 500 Sue Rich 224.62
Enter account number (> 0), first name, last name and balance.
? ^Z
 

Common Programming Error 14.2

It is a logic error to open an existing file for output when, in fact, the user wishes to preserve the file.

Class FileOutputStream provides methods for writing byte arrays and individual bytes to a file. In this program we wish to write objects to a filea capability not provided by FileOutputStream. For this reason, we wrap a FileOutputStream in an ObjectOutputStream by passing the new FileOutputStream object to the ObjectOutputStream's constructor (lines 2021). The ObjectOutputStream object uses the FileOutputStream object to write objects into the file. Lines 2021 might throw an IOException if a problem occurs while opening the file (e.g., when a file is opened for writing on a drive with insufficient space or when a read-only file is opened for writing). If so, the program displays an error message (lines 2326). If no exception occurs, the file is open and variable output can be used to write objects to the file.

This program assumes that data is input correctly and in the proper record-number order. Method addRecords (lines 3086) performs the write operation. Lines 6263 create an AccountRecordSerializable object from the data entered by the user. Line 64 calls ObjectOutputStream method writeObject to write the record object to the output file. Note that only one statement is required to write the entire object.

Method closeFile (lines 89101) closes the file. Method closeFile calls ObjectOutputStream method close on output to close both the ObjectOutputStream and its underlying FileOutputStream (line 94). Note that the call to method close is contained in a try block. Method close throws an IOException if the file cannot be closed properly. In this case, it is important to notify the user that the information in the file might be corrupted. When using wrapped streams, closing the outermost stream also closes the underlying file.

In the sample execution for the program in Fig. 14.19, we entered information for five accountsthe same information shown in Fig. 14.10. The program does not show how the data records actually appear in the file. Remember that now we are using binary files, which are not humanly readable. To verify that the file has been created successfully, the next section presents a program to read the file's contents.

14.6.2. Reading and Deserializing Data from a Sequential-Access File

As discussed in Section 14.5.2, data is stored in files so that it may be retrieved for processing when needed. The preceding section showed how to create a file for sequential access using object serialization. In this section, we discuss how to read serialized data sequentially from a file.

The program in Fig. 14.20Fig. 14.21 reads records from a file created by the program in Section 14.6.1 and displays the contents. The program opens the file for input by creating a FileInputStream object (line 21). The name of the file to open is specified as an argument to the FileInputStream constructor. In Fig. 14.18, we wrote objects to the file, using an ObjectOutputStream object. Data must be read from the file in the same format in which it was written. Therefore, we use an ObjectInputStream wrapped around a FileInputStream in this program (lines 2021) If no exceptions occur when opening the file, variable input can be used to read objects from the file.

Figure 14.20. Sequential file read using an ObjectInputStream.

(This item is displayed on pages 704 - 705 in the print version)

 1 // Fig. 14.20: ReadSequentialFile.java
 2 // This program reads a file of objects sequentially
 3 // and displays each record.
 4 import java.io.EOFException; 
 5 import java.io.FileInputStream; 
 6 import java.io.IOException; 
 7 import java.io.ObjectInputStream;
 8
 9 import com.deitel.jhtp6.ch14.AccountRecordSerializable;
10
11 public class ReadSequentialFile
12 {
13 private ObjectInputStream input;
14
15 // enable user to select file to open
16 public void openFile()
17 {
18 try // open file
19 {
20 input = new ObjectInputStream( 
21  new FileInputStream( "clients.ser" ) );
22 } // end try
23 catch ( IOException ioException )
24 {
25 System.err.println( "Error opening file." );
26 } // end catch
27 } // end method openFile
28
29 // read record from file
30 public void readRecords()
31 {
32 AccountRecordSerializable record;
33 System.out.printf( "%-10s%-12s%-12s%10s
", "Account",
34 "First Name", "Last Name", "Balance" );
35
36 try // input the values from the file
37 {
38 while ( true )
39 {
40 record = ( AccountRecordSerializable ) input.readObject();
41
42 // display record contents
43 System.out.printf( "%-10d%-12s%-12s%10.2f
", 
44  record.getAccount(), record.getFirstName(), 
45  record.getLastName(), record.getBalance() );
46 } // end while
47 } // end try
48 catch ( EOFException endOfFileException )
49 {
50 return; // end of file was reached
51 } // end catch
52 catch ( ClassNotFoundException classNotFoundException )
53 {
54 System.err.println( "Unable to create object." );
55 } // end catch
56 catch ( IOException ioException )
57 {
58 System.err.println( "Error during read from file." );
59 } // end catch
60 } // end method readRecords
61
62 // close file and terminate application
63 public void closeFile()
64 {
65 try // close file and exit
66 {
67 if ( input != null )
68 input.close();
69 } // end try
70 catch ( IOException ioException )
71 {
72 System.err.println( "Error closing file." );
73 System.exit( 1 );
74 } // end catch
75 } // end method closeFile
76 } // end class ReadSequentialFile

Figure 14.21. Testing class ReadSequentialFile.

 1 // Fig. 14.21: ReadSequentialFileTest.java
 2 // This program test class ReadSequentialFile.
 3
 4 public class ReadSequentialFileTest
 5 {
 6 public static void main( String args[] )
 7 {
 8 ReadSequentialFile application = new ReadSequentialFile();
 9
10 application.openFile();
11 application.readRecords();
12 application.closeFile();
13 } // end main
14 } // end class ReadSequentialFileTest
 
Account First Name Last Name Balance
100 Bob Jones 24.98
200 Steve Doe -345.67
300 Pam White 0.00
400 Sam Stone -42.16
500 Sue Rich 224.62
 

The program reads records from the file in method readRecords (lines 3060). Line 40 calls ObjectInputStream method readObject to read an Object from the file. To use AccountRecordSerializable-specific methods, we downcast the returned Object to type AccountRecordSerializable. Method readObject tHRows an EOFException (processed at lines 4851) if an attempt is made to read beyond the end of the file. Method readObject throws a ClassNotFoundException if the class for the object being read cannot be located. This might occur if the file is accessed on a computer that does not have the class. Figure 14.21 contains method main (lines 613), which opens the file, calls method readRecords and closes the file.

Introduction to Computers, the Internet and the World Wide Web

Introduction to Java Applications

Introduction to Classes and Objects

Control Statements: Part I

Control Statements: Part 2

Methods: A Deeper Look

Arrays

Classes and Objects: A Deeper Look

Object-Oriented Programming: Inheritance

Object-Oriented Programming: Polymorphism

GUI Components: Part 1

Graphics and Java 2D™

Exception Handling

Files and Streams

Recursion

Searching and Sorting

Data Structures

Generics

Collections

Introduction to Java Applets

Multimedia: Applets and Applications

GUI Components: Part 2

Multithreading

Networking

Accessing Databases with JDBC

Servlets

JavaServer Pages (JSP)

Formatted Output

Strings, Characters and Regular Expressions

Appendix A. Operator Precedence Chart

Appendix B. ASCII Character Set

Appendix C. Keywords and Reserved Words

Appendix D. Primitive Types

Appendix E. (On CD) Number Systems

Appendix F. (On CD) Unicode®

Appendix G. Using the Java API Documentation

Appendix H. (On CD) Creating Documentation with javadoc

Appendix I. (On CD) Bit Manipulation

Appendix J. (On CD) ATM Case Study Code

Appendix K. (On CD) Labeled break and continue Statements

Appendix L. (On CD) UML 2: Additional Diagram Types

Appendix M. (On CD) Design Patterns

Appendix N. Using the Debugger

Inside Back Cover



Java(c) How to Program
Java How to Program (6th Edition) (How to Program (Deitel))
ISBN: 0131483986
EAN: 2147483647
Year: 2003
Pages: 615

Flylib.com © 2008-2020.
If you may any questions please contact us: flylib@qtcs.net