Selective Serialization – Java I/O: Part I

Selective Serialization

As noted earlier, static fields are not serialized, as these are not part of the state of an object. An instance field of an object can be omitted from being serialized by specifying the transient modifier in the declaration of the field—typically used for sensitive data in a field. Selective serialization discussed here is not applicable to record classes.

Example 20.7 illustrates some salient aspects of serialization. The setup comprises the classes Wheel and Unicycle, and their client class SerialClient. The class Unicycle has a field of type Wheel, and the class Wheel has a field of type int. The class Unicycle is a compound object with a Wheel object as a constituent object. The class Serial-Client serializes and deserializes a unicycle in the try-with-resources statements at (4) and (5), respectively. The state of the objects is printed to the standard output stream before serialization, and so is the state of the object created by deserialization.

Both the Compound Object and Its Constituents Are Serializable

If we run the program with the following declarations for the Wheel and the Unicycle classes, where a compound object of the serializable class Unicycle uses an object of the serializable class Wheel as a constituent object:

Click here to view code image

class Wheel implements Serializable {                               // (1a)
  private int wheelSize;
  …
}
class Unicycle implements Serializable {                            // (2)
  private Wheel wheel;                                              // (3a)
  …
}

we get the following output, showing that both serialization and deserialization were successful:

Click here to view code image

Before writing: Unicycle with wheel size: 65
After reading: Unicycle with wheel size: 65

A compound object with its constituent objects is often referred to as an object graph. Serializing a compound object serializes its complete object graph—that is, the compound object and its constituent objects are recursively serialized.

Example 20.7 Non-Serializable Objects

Click here to view code image

import java.io.Serializable;
// public class Wheel implements Serializable {                   // (1a)
public class Wheel {                                              // (1b)
  private int wheelSize;
  public Wheel(int ws) { wheelSize = ws; }
  @Override
  public String toString() { return “wheel size: ” + wheelSize; }
}

Click here to view code image

import java.io.Serializable;
public class Unicycle implements Serializable {                     // (2)
  private Wheel wheel;                                              // (3a)
//transient private Wheel wheel;                                    // (3b)
  public Unicycle (Wheel wheel) { this.wheel = wheel; }
  @Override
  public String toString() { return “Unicycle with ” + wheel; }
}

Click here to view code image

import java.io.*;
public class SerialClient {
  public static void main(String args[])
      throws IOException, ClassNotFoundException {
    try (// Set up the output stream:                              // (4)
        FileOutputStream outputFile = new FileOutputStream(“storage.dat”);
        ObjectOutputStream outputStream = new ObjectOutputStream(outputFile)) {
      // Write the data:
      Wheel wheel = new Wheel(65);
      Unicycle uc = new Unicycle(wheel);
      System.out.println(“Before writing: ” + uc);
      outputStream.writeObject(uc);
    }
    try (// Set up the input streams:                              // (5)
        FileInputStream inputFile = new FileInputStream(“storage.dat”);
        ObjectInputStream inputStream = new ObjectInputStream(inputFile)) {
      // Read data.
      Unicycle uc = (Unicycle) inputStream.readObject();
      // Write data on standard output stream.
      System.out.println(“After reading: ” + uc);
    }
  }
}

Writing Formatted Values – Java I/O: Part I

Writing Formatted Values

Although formatting of values is covered extensively in Chapter 18, p. 1095, here we mention the support for formatting values provided by I/O streams. The PrintWriter class provides the format() methods and the printf() convenient methods to write formatted values. The printf() methods are functionally equivalent to the format() methods. As the methods return a PrintWriter, calls to these methods can be chained.

The printf() and the format() methods for printing formatted values are also provided by the PrintStream and the Console classes (p. 1256). The format() method is also provided by the String class (§8.4, p. 457). We assume familiarity with printing formatted values on the standard output stream by calling the printf() method on the System.out field which is an object of the PrintStream class (§1.9, p. 24).

Click here to view code image

PrintWriter format(String format, Object… args)
PrintWriter format(Locale loc, String format, Object… args)
PrintWriter printf(String format, Object… args)
PrintWriter printf(Locale loc, String format, Object… args)

The String parameter format specifies how formatting will be done. It contains format specifiers that determine how each subsequent value in the variable arity parameter args will be formatted and printed. The resulting string from the formatting will be written to the current writer.

If the locale is specified, it is taken into consideration to format the args.

Any error in the format string will result in a runtime exception.

Writing Text Files

When writing text representation of values to a file using the default character encoding, any one of the following four procedures for setting up a PrintWriter can be used.

Setting up a PrintWriter based on an OutputStreamWriter which is chained to a FileOutputStream (Figure 20.4(a)):

Figure 20.4 Setting Up a PrintWriter to Write to a File

Create a FileOutputStream:

Click here to view code image

FileOutputStream outputFile = new FileOutputStream(“info.txt”);

Create an OutputStreamWriter which is chained to the FileOutputStream:

Click here to view code image

OutputStreamWriter outputStream = new OutputStreamWriter(outputFile);

The OutputStreamWriter uses the default character encoding for writing the characters to the file.

Create a PrintWriter which is chained to the OutputStreamWriter:

Click here to view code image

PrintWriter printWriter1 = new PrintWriter(outputStream, true);

The value true for the second parameter in the constructor will result in the output buffer being flushed by the println() and printf() methods.

Setting up a PrintWriter based on a FileOutputStream (Figure 20.4(b)):

Create a FileOutputStream:

Click here to view code image

FileOutputStream outputFile = new FileOutputStream(“info.txt”);

Create a PrintWriter which is chained to the FileOutputStream:

Click here to view code image

PrintWriter printWriter2 = new PrintWriter(outputFile, true);

The intermediate OutputStreamWriter to convert the characters using the default encoding is automatically supplied. The output buffer will also perform automatic line flushing.

Setting up a PrintWriter based on a FileWriter (Figure 20.4(c)):

Create a FileWriter which is a subclass of OutputStreamWriter:

Click here to view code image

FileWriter fileWriter = new FileWriter(“info.txt”);

This is equivalent to having an OutputStreamWriter chained to a FileOutputStream for writing the characters to the file, as shown in Figure 20.4(a).

Create a PrintWriter which is chained to the FileWriter:

Click here to view code image

PrintWriter printWriter3 = new PrintWriter(fileWriter, true);

The output buffer will be flushed by the println() and printf() methods.

Setting up a PrintWriter, given the file name (Figure 20.4(d)):

Create a PrintWriter, supplying the file name:

Click here to view code image

PrintWriter printWriter4 = new PrintWriter(“info.txt”);

The underlying OutputStreamWriter is created to write the characters to the file in the default encoding, as shown in Figure 20.4(d). In this case, there is no automatic flushing by the println() and printf() methods.

If a specific character encoding is desired for the writer, the third procedure (Figure 20.4(c)) can be used, with the encoding being specified for the FileWriter:

Click here to view code image

Charset utf8 = Charset.forName(“UTF-8”);
FileWriter fileWriter = new FileWriter(“info.txt”, utf8);
PrintWriter printWriter5 = new PrintWriter(fileWriter, true);

This writer will use the UTF-8 character encoding to write the characters to the file. Alternatively, we can use a PrintWriter constructor that accepts a character encoding:

Click here to view code image

Charset utf8 = Charset.forName(“UTF-8”);
PrintWriter printWriter6 = new PrintWriter(“info.txt”, utf8);

A BufferedWriter can also be used to improve the efficiency of writing characters to the underlying stream, as explained later in this section.