Home > Articles

  • Print
  • + Share This
This chapter is from the book

2.3 Object Input/Output Streams and Serialization

Using a fixed-length record format is a good choice if you need to store data of the same type. However, objects that you create in an object-oriented program are rarely all of the same type. For example, you might have an array called staff that is nominally an array of Employee records but contains objects that are actually instances of a subclass such as Manager.

It is certainly possible to come up with a data format that allows you to store such polymorphic collections—but, fortunately, we don’t have to. The Java language supports a very general mechanism, called object serialization, that makes it possible to write any object to an output stream and read it again later. (You will see in this chapter where the term “serialization” comes from.)

2.3.1 Saving and Loading Serializable Objects

To save object data, you first need to open an ObjectOutputStream object:

var out = new ObjectOutputStream(new FileOutputStream("employee.dat"));

Now, to save an object, simply use the writeObject method of the ObjectOutputStream class as in the following fragment:

var harry = new Employee("Harry Hacker", 50000, 1989, 10, 1);
var boss = new Manager("Carl Cracker", 80000, 1987, 12, 15);
out.writeObject(harry);
out.writeObject(boss);

To read the objects back in, first get an ObjectInputStream object:

var in = new ObjectInputStream(new FileInputStream("employee.dat"));

Then, retrieve the objects in the same order in which they were written, using the readObject method:

var e1 = (Employee) in.readObject();
var e2 = (Employee) in.readObject();

There is, however, one change you need to make to any class that you want to save to an output stream and restore from an object input stream. The class must implement the Serializable interface:

class Employee implements Serializable { . . . }

The Serializable interface has no methods, so you don’t need to change your classes in any way. In this regard, it is similar to the Cloneable interface that we discussed in Volume I, Chapter 6. However, to make a class cloneable, you still had to override the clone method of the Object class. To make a class serializable, you do not need to do anything else.

Behind the scenes, an ObjectOutputStream looks at all the fields of the objects and saves their contents. For example, when writing an Employee object, the name, date, and salary fields are written to the output stream.

However, there is one important situation to consider: What happens when one object is shared by several objects as part of their state?

To illustrate the problem, let us make a slight modification to the Manager class. Let’s assume that each manager has a secretary:

class Manager extends Employee
{
   private Employee secretary;
   . . .
}

Each Manager object now contains a reference to an Employee object that describes the secretary. Of course, two managers can share the same secretary, as is the case in Figure 2.5 and the following code:

FIGURE 2.5

FIGURE 2.5 Two managers can share a mutual employee.

var harry = new Employee("Harry Hacker", . . .);
var carl = new Manager("Carl Cracker", . . .);
carl.setSecretary(harry);
var tony = new Manager("Tony Tester", . . .);
tony.setSecretary(harry);

Saving such a network of objects is a challenge. Of course, we cannot save and restore the memory addresses for the secretary objects. When an object is reloaded, it will likely occupy a completely different memory address than it originally did.

Instead, each object is saved with the serial number—hence the name object serialization for this mechanism. Here is the algorithm:

  1. Associate a serial number with each object reference that you encounter (as shown in Figure 2.6).

    FIGURE 2.6

    FIGURE 2.6 An example of object serialization

  2. When encountering an object reference for the first time, save the object data to the output stream.

  3. If it has been saved previously, just write “same as the previously saved object with serial number x.”

When reading the objects back, the procedure is reversed.

  1. When an object is specified in an object input stream for the first time, construct it, initialize it with the stream data, and remember the association between the serial number and the object reference.

  2. When the tag “same as the previously saved object with serial number x” is encountered, retrieve the object reference for the sequence number.

Listing 2.3 is a program that saves and reloads a network of Employee and Manager objects (some of which share the same employee as a secretary). Note that the secretary object is unique after reloading—when newStaff[1] gets a raise, that is reflected in the secretary fields of the managers.

Listing 2.3 objectStream/ObjectStreamTest.java


 1  package objectStream;
 2  
 3  import java.io.*;
 4  
 5  /**
 6  * @version 1.11 2018-05-01
 7  * @author Cay Horstmann
 8  */
 9  class ObjectStreamTest
10  {
11    public static void main(String[] args) throws IOException, ClassNotFoundException
12    {
13        var harry = new Employee("Harry Hacker", 50000, 1989, 10, 1);
14        var carl = new Manager("Carl Cracker", 80000, 1987, 12, 15);
15        carl.setSecretary(harry);
16        var tony = new Manager("Tony Tester", 40000, 1990, 3, 15);
17        tony.setSecretary(harry);
18  
19        var staff = new Employee[3];
20  
21        staff[0] = carl;
22        staff[1] = harry;
23        staff[2] = tony;
24  
25        // save all employee records to the file employee.dat
26        try (var out = new ObjectOutputStream(new FileOutputStream("employee.dat")))
27        {
28          out.writeObject(staff);
29        }
30  
31          try (var in = new ObjectInputStream(new FileInputStream("employee.dat")))
32          {
33            // retrieve all records into a new array
34            
35            var newStaff = (Employee[]) in.readObject();
36            
37            // raise secretary’s salary
38            newStaff[1].raiseSalary(10);
39            
40            // print the newly read employee records
41            for (Employee e : newStaff)
42                System.out.println(e);
43          }
44      }
45  }

2.3.2 Understanding the Object Serialization File Format

Object serialization saves object data in a particular file format. Of course, you can use the writeObject/readObject methods without having to know the exact sequence of bytes that represents objects in a file. Nonetheless, we found studying the data format extremely helpful for gaining insight into the object serialization process. As the details are somewhat technical, feel free to skip this section if you are not interested in the implementation.

Every file begins with the two-byte “magic number”

AC ED

followed by the version number of the object serialization format, which is currently

00 05

(We use hexadecimal numbers throughout this section to denote bytes.) Then, it contains a sequence of objects, in the order in which they were saved.

String objects are saved as

74

two-byte length

characters

For example, the string “Harry” is saved as

74 00 05 Harry

The Unicode characters of the string are saved in the “modified UTF-8” format.

When an object is saved, the class of that object must be saved as well. The class description contains

  • The name of the class

  • The serial version unique ID, which is a fingerprint of the data field types and method signatures

  • A set of flags describing the serialization method

  • A description of the data fields

The fingerprint is obtained by ordering the descriptions of the class, superclass, interfaces, field types, and method signatures in a canonical way, and then applying the so-called Secure Hash Algorithm (SHA) to that data.

SHA is a fast algorithm that gives a “fingerprint” of a larger block of information. This fingerprint is always a 20-byte data packet, regardless of the size of the original data. It is created by a clever sequence of bit operations on the data that makes it essentially 100 percent certain that the fingerprint will change if the information is altered in any way. (For more details on SHA, see, for example, Cryptography and Network Security, Seventh Edition by William Stallings, Prentice Hall, 2016.) However, the serialization mechanism uses only the first eight bytes of the SHA code as a class fingerprint. It is still very likely that the class fingerprint will change if the data fields or methods change.

When reading an object, its fingerprint is compared against the current fingerprint of the class. If they don’t match, it means the class definition has changed after the object was written, and an exception is generated. Of course, in practice, classes do evolve, and it might be necessary for a program to read in older versions of objects. We will discuss this in Section 2.3.5, “Versioning,” on p. 103.

Here is how a class identifier is stored:

  • 72

  • 2-byte length of class name

  • Class name

  • 8-byte fingerprint

  • 1-byte flag

  • 2-byte count of data field descriptors

  • Data field descriptors

  • 78 (end marker)

  • Superclass type (70 if none)

The flag byte is composed of three bit masks, defined in java.io.ObjectStreamConstants:

static final byte SC_WRITE_METHOD = 1;
   // class has a writeObject method that writes additional data
static final byte SC_SERIALIZABLE = 2;
   // class implements the Serializable interface
static final byte SC_EXTERNALIZABLE = 4;
   // class implements the Externalizable interface

We discuss the Externalizable interface later in this chapter. Externalizable classes supply custom read and write methods that take over the output of their instance fields. The classes that we write implement the Serializable interface and will have a flag value of 02. The serializable java.util.Date class defines its own readObject/writeObject methods and has a flag of 03.

Each data field descriptor has the format:

  • 1-byte type code

  • 2-byte length of field name

  • Field name

  • Class name (if the field is an object)

The type code is one of the following:

B

byte

C

char

D

double

F

float

I

int

J

long

L

object

S

short

Z

boolean

[

array

When the type code is L, the field name is followed by the field type. Class and field name strings do not start with the string code 74, but field types do. Field types use a slightly different encoding of their names—namely, the format used by native methods.

For example, the salary field of the Employee class is encoded as

D 00 06 salary

Here is the complete class descriptor of the Employee class:

72 00 08 Employee

E6 D2 86 7D AE AC 18 1B 02

Fingerprint and flags

00 03

Number of instance fields

D 00 06 salary

Instance field type and name

L 00 07 hireDay

Instance field type and name

74 00 10 Ljava/util/Date;

Instance field class name: Date

L 00 04 name

Instance field type and name

74 00 12 Ljava/lang/String;

Instance field class name: String

78

End marker

70

No superclass

These descriptors are fairly long. If the same class descriptor is needed again in the file, an abbreviated form is used:

71

4-byte serial number

The serial number refers to the previous explicit class descriptor. We discuss the numbering scheme later.

An object is stored as

73

class descriptor

object data

For example, here is how an Employee object is stored:

40 E8 6A 00 00 00 00 00

salary field value: double

73

hireDay field value: new object

71 00 7E 00 08

Existing class java.util.Date

77 08 00 00 00 91 1B 4E B1 80 78

External storage (details later)

74 00 0C Harry Hacker

name field value: String

As you can see, the data file contains enough information to restore the Employee object.

Arrays are saved in the following format:

75

class descriptor

4-byte number of entries

entries

The array class name in the class descriptor is in the same format as that used by native methods (which is slightly different from the format used by class names in other class descriptors). In this format, class names start with an L and end with a semicolon.

For example, an array of three Employee objects starts out like this:

75

Array

72 00 0B [LEmployee;

New class, string length, class name Employee[]

FC BF 36 11 C5 91 11 C7 02

Fingerprint and flags

00 00

Number of instance fields

78

End marker

70

No superclass

00 00 00 03

Number of array entries

Note that the fingerprint for an array of Employee objects is different from a fingerprint of the Employee class itself.

All objects (including arrays and strings) and all class descriptors are given serial numbers as they are saved in the output file. The numbers start at 00 7E 00 00.

We already saw that a full class descriptor for any given class occurs only once. Subsequent descriptors refer to it. For example, in our previous example, a repeated reference to the Date class was coded as

71 00 7E 00 08

The same mechanism is used for objects. If a reference to a previously saved object is written, it is saved in exactly the same way—that is, 71 followed by the serial number. It is always clear from the context whether a particular serial reference denotes a class descriptor or an object.

Finally, a null reference is stored as

70

Here is the commented output of the ObjectRefTest program of the preceding section. Run the program, look at a hex dump of its data file employee.dat, and compare it with the commented listing. The important lines toward the end of the output show a reference to a previously saved object.

AC ED 00 05

File header

75

Array staff (serial #1)

72 00 0B [LEmployee;

New class, string length, class name Employee[] (serial #0)

FC BF 36 11 C5 91 11 C7 02

Fingerprint and flags

00 00

Number of instance fields

78

End marker

70

No superclass

00 00 00 03

Number of array entries

73

staff[0]—new object (serial #7)

72 00 07 Manager

New class, string length, class name (serial #2)

36 06 AE 13 63 8F 59 B7 02

Fingerprint and flags

00 01

Number of data fields

L 00 09 secretary

Instance field type and name

74 00 0A LEmployee;

Instance field class name: String (serial #3)

78

End marker

72 00 08 Employee

Superclass: new class, string length, class name (serial #4)

E6 D2 86 7D AE AC 18 1B 02

Fingerprint and flags

00 03

Number of instance fields

D 00 06 salary

Instance field type and name

L 00 07 hireDay

Instance field type and name

74 00 10 Ljava/util/Date;

Instance field class name: String (serial #5)

L 00 04 name

Instance field type and name

74 00 12 Ljava/lang/String;

Instance field class name: String (serial #6)

78

End marker

70

No superclass

40 F3 88 00 00 00 00 00

salary field value: double

73

hireDay field value: new object (serial #9)

72 00 0E java.util.Date

New class, string length, class name (serial #8)

68 6A 81 01 4B 59 74 19 03

Fingerprint and flags

00 00

No instance variables

78

End marker

70

No superclass

77 08

External storage, number of bytes

00 00 00 83 E9 39 E0 00

Date

78

End marker

74 00 0C Carl Cracker

name field value: String (serial #10)

73

secretary field value: new object (serial #11)

71 00 7E 00 04

existing class (use serial #4)

40 E8 6A 00 00 00 00 00

salary field value: double

73

hireDay field value: new object (serial #12)

71 00 7E 00 08

Existing class (use serial #8)

77 08

External storage, number of bytes

00 00 00 91 1B 4E B1 80

Date

78

End marker

74 00 0C Harry Hacker

name field value: String (serial #13)

71 00 7E 00 0B

staff[1]: existing object (use serial #11)

73

staff[2]: new object (serial #14)

71 00 7E 00 02

Existing class (use serial #2)

40 E3 88 00 00 00 00 00

salary field value: double

73

hireDay field value: new object (serial #15)

71 00 7E 00 08

Existing class (use serial #8)

77 08

External storage, number of bytes

00 00 00 94 6D 3E EC 00 00

Date

78

End marker

74 00 0B Tony Tester

name field value: String (serial #16)

71 00 7E 00 0B

secretary field value: existing object (use serial #11)

Of course, studying these codes can be about as exciting as reading a phone book. It is not important to know the exact file format (unless you are trying to create an evil effect by modifying the data), but it is still instructive to know that the serialized format has a detailed description of all the objects it contains, with sufficient detail to allow reconstruction of both objects and arrays of objects.

What you should remember is this:

  • The serialized format contains the types and data fields of all objects.

  • Each object is assigned a serial number.

  • Repeated occurrences of the same object are stored as references to that serial number.

2.3.3 Modifying the Default Serialization Mechanism

Certain data fields should never be serialized—for example, integer values that store file handles or handles of windows that are only meaningful to native methods. Such information is guaranteed to be useless when you reload an object at a later time or transport it to a different machine. In fact, improper values for such fields can actually cause native methods to crash. Java has an easy mechanism to prevent such fields from ever being serialized: Mark them with the keyword transient. You also need to tag fields as transient if they belong to nonserializable classes. Transient fields are always skipped when objects are serialized.

The serialization mechanism provides a way for individual classes to add validation or any other desired action to the default read and write behavior. A serializable class can define methods with the signature

private void readObject(ObjectInputStream in)
      throws IOException, ClassNotFoundException;
private void writeObject(ObjectOutputStream out)
      throws IOException;

Then, the data fields are no longer automatically serialized—these methods are called instead.

Here is a typical example. A number of classes in the java.awt.geom package, such as Point2D.Double, are not serializable. Now, suppose you want to serialize a class LabeledPoint that stores a String and a Point2D.Double. First, you need to mark the Point2D.Double field as transient to avoid a NotSerializableException.

public class LabeledPoint implements Serializable
{
   private String label;
   private transient Point2D.Double point;
   . . .
}

In the writeObject method, we first write the object descriptor and the String field, label, by calling the defaultWriteObject method. This is a special method of the ObjectOutputStream class that can only be called from within a writeObject method of a serializable class. Then we write the point coordinates, using the standard DataOutput calls.

private void writeObject(ObjectOutputStream out)
      throws IOException
{
   out.defaultWriteObject();
   out.writeDouble(point.getX());
   out.writeDouble(point.getY());
}

In the readObject method, we reverse the process:

private void readObject(ObjectInputStream in)
      throws IOException
{
   in.defaultReadObject();
   double x = in.readDouble();
   double y = in.readDouble();
   point = new Point2D.Double(x, y);
}

Another example is the java.util.Date class that supplies its own readObject and writeObject methods. These methods write the date as a number of milliseconds from the epoch (January 1, 1970, midnight UTC). The Date class has a complex internal representation that stores both a Calendar object and a millisecond count to optimize lookups. The state of the Calendar is redundant and does not have to be saved.

The readObject and writeObject methods only need to save and load their data fields. They should not concern themselves with superclass data or any other class information.

Instead of letting the serialization mechanism save and restore object data, a class can define its own mechanism. To do this, a class must implement the Externalizable interface. This, in turn, requires it to define two methods:

public void readExternal(ObjectInputStream in)
      throws IOException, ClassNotFoundException;
public void writeExternal(ObjectOutputStream out)
      throws IOException;

Unlike the readObject and writeObject methods that were described in the previous section, these methods are fully responsible for saving and restoring the entire object, including the superclass data. When writing an object, the serialization mechanism merely records the class of the object in the output stream. When reading an externalizable object, the object input stream creates an object with the no-argument constructor and then calls the readExternal method. Here is how you can implement these methods for the Employee class:

public void readExternal(ObjectInput s)
      throws IOException
{
   name = s.readUTF();
   salary = s.readDouble();
   hireDay = LocalDate.ofEpochDay(s.readLong());
}
public void writeExternal(ObjectOutput s)
      throws IOException
{
   s.writeUTF(name);
   s.writeDouble(salary);
   s.writeLong(hireDay.toEpochDay());
}

2.3.4 Serializing Singletons and Typesafe Enumerations

You have to pay particular attention to serializing and deserializing objects that are assumed to be unique. This commonly happens when you are implementing singletons and typesafe enumerations.

If you use the enum construct of the Java language, you need not worry about serialization—it just works. However, suppose you maintain legacy code that contains an enumerated type such as

public class Orientation
{
   public static final Orientation HORIZONTAL = new Orientation(1);
   public static final Orientation VERTICAL  = new Orientation(2);
   private int value;
   private Orientation(int v) { value = v; }
}

This idiom was common before enumerations were added to the Java language. Note that the constructor is private. Thus, no objects can be created beyond Orientation.HORIZONTAL and Orientation.VERTICAL. In particular, you can use the == operator to test for object equality:

if (orientation == Orientation.HORIZONTAL) . . .

There is an important twist that you need to remember when a typesafe enumeration implements the Serializable interface. The default serialization mechanism is not appropriate. Suppose we write a value of type Orientation and read it in again:

Orientation original = Orientation.HORIZONTAL;
ObjectOutputStream out = . . .;
out.write(original);
out.close();
ObjectInputStream in = . . .;
var saved = (Orientation) in.read();

Now the test

if (saved == Orientation.HORIZONTAL) . . .

will fail. In fact, the saved value is a completely new object of the Orientation type that is not equal to any of the predefined constants. Even though the constructor is private, the serialization mechanism can create new objects!

To solve this problem, you need to define another special serialization method, called readResolve. If the readResolve method is defined, it is called after the object is deserialized. It must return an object which then becomes the return value of the readObject method. In our case, the readResolve method will inspect the value field and return the appropriate enumerated constant:

protected Object readResolve() throws ObjectStreamException
{
   if (value == 1) return Orientation.HORIZONTAL;
   if (value == 2) return Orientation.VERTICAL;
   throw new ObjectStreamException(); // this shouldn’t happen
}

Remember to add a readResolve method to all typesafe enumerations in your legacy code and to all classes that follow the singleton design pattern.

2.3.5 Versioning

If you use serialization to save objects, you need to consider what happens when your program evolves. Can version 1.1 read the old files? Can the users who still use 1.0 read the files that the new version is producing? Clearly, it would be desirable if object files could cope with the evolution of classes.

At first glance, it seems that this would not be possible. When a class definition changes in any way, its SHA fingerprint also changes, and you know that object input streams will refuse to read in objects with different fingerprints. However, a class can indicate that it is compatible with an earlier version of itself. To do this, you must first obtain the fingerprint of the earlier version of the class. Use the standalone serialver program that is part of the JDK to obtain this number. For example, running

serialver Employee

prints

Employee: static final long serialVersionUID = -1814239825517340645L;

All later versions of the class must define the serialVersionUID constant to the same fingerprint as the original.

class Employee implements Serializable // version 1.1
{
   . . .
   public static final long serialVersionUID = -1814239825517340645L;
}

When a class has a static data member named serialVersionUID, it will not compute the fingerprint manually but will use that value instead.

Once that static data member has been placed inside a class, the serialization system is now willing to read in different versions of objects of that class.

If only the methods of the class change, there is no problem with reading the new object data. However, if the data fields change, you may have problems. For example, the old file object may have more or fewer data fields than the one in the program, or the types of the data fields may be different. In that case, the object input stream makes an effort to convert the serialized object to the current version of the class.

The object input stream compares the data fields of the current version of the class with those of the version in the serialized object. Of course, the object input stream considers only the nontransient and nonstatic data fields. If two fields have matching names but different types, the object input stream makes no effort to convert one type to the other—the objects are incompatible. If the serialized object has data fields that are not present in the current version, the object input stream ignores the additional data. If the current version has data fields that are not present in the serialized object, the added fields are set to their default (null for objects, zero for numbers, and false for boolean values).

Here is an example. Suppose we have saved a number of employee records on disk, using the original version (1.0) of the class. Now we change the Employee class to version 2.0 by adding a data field called department. Figure 2.7 shows what happens when a 1.0 object is read into a program that uses 2.0 objects. The department field is set to null. Figure 2.8 shows the opposite scenario: A program using 1.0 objects reads a 2.0 object. The additional department field is ignored.

FIGURE 2.7

FIGURE 2.7 Reading an object with fewer data fields

FIGURE 2.8

FIGURE 2.8 Reading an object with more data fields

Is this process safe? It depends. Dropping a data field seems harmless—the recipient still has all the data that it knows how to manipulate. Setting a data field to null might not be so safe. Many classes work hard to initialize all data fields in all constructors to non-null values, so that the methods don’t have to be prepared to handle null data. It is up to the class designer to implement additional code in the readObject method to fix version incompatibilities or to make sure the methods are robust enough to handle null data.

2.3.6 Using Serialization for Cloning

There is an amusing use for the serialization mechanism: It gives you an easy way to clone an object, provided the class is serializable. Simply serialize it to an output stream and then read it back in. The result is a new object that is a deep copy of the existing object. You don’t have to write the object to a file—you can use a ByteArrayOutputStream to save the data into a byte array.

As Listing 2.4 shows, to get clone for free, simply extend the SerialCloneable class, and you are done.

You should be aware that this method, although clever, will usually be much slower than a clone method that explicitly constructs a new object and copies or clones the data fields.

Listing 2.4 serialClone/SerialCloneTest.java


 1  package serialClone;
 2  
 3  /**
 4  * @version 1.22 2018-05-01
 5  * @author Cay Horstmann
 6  */
 7  
 8  import java.io.*;
 9  import java.time.*;
10  
11  public class SerialCloneTest
12  {
13    public static void main(String[] args) throws CloneNotSupportedException
14    {
15        var harry = new Employee("Harry Hacker", 35000, 1989, 10, 1);
16        // clone harry
17        var harry2 = (Employee) harry.clone();
18  
19        // mutate harry
20        harry.raiseSalary(10);
21  
22        // now harry and the clone are different
23        System.out.println(harry);
24        System.out.println(harry2);
25      }
26  }
27  
28  /**
29  * A class whose clone method uses serialization.
30  */
31  class SerialCloneable implements Cloneable, Serializable
32  {
33    public Object clone() throws CloneNotSupportedException
34    {
35        try {
36          // save the object to a byte array
37          var bout = new ByteArrayOutputStream();
38          try (var out = new ObjectOutputStream(bout))
39          {
40              out.writeObject(this);
41          }
42    
43          // read a clone of the object from the byte array
44          try (var bin = new ByteArrayInputStream(bout.toByteArray()))
45          {
46              var in = new ObjectInputStream(bin);
47              return in.readObject();
48          }
49        }
50        catch (IOException | ClassNotFoundException e)
51        {
52          var e2 = new CloneNotSupportedException();
53          e2.initCause(e);
54          throw e2;
55        }
56      }
57  }
58  
59  /**
60  * The familiar Employee class, redefined to extend the
61  * SerialCloneable class.
62  */
63  class Employee extends SerialCloneable
64  {
65      private String name;
66      private double salary;
67      private LocalDate hireDay;
68  
69      public Employee(String n, double s, int year, int month, int day)
70      {
71        name = n;
72        salary = s;
73        hireDay = LocalDate.of(year, month, day);
74      }
75  
76      public String getName()
77      {
78          return name;
79      }
80  
81      public double getSalary()
82      {
83          return salary;
84      }
85  
86      public LocalDate getHireDay()
87      {
88          return hireDay;
89      }
90  
91      /**
92          Raises the salary of this employee.
93          @byPercent the percentage of the raise
94      */
95      public void raiseSalary(double byPercent)
96      {
97          double raise = salary * byPercent / 100;
98          salary += raise;
99      }
100 
101      public String toString()
102      {
103        return getClass().getName()
104            + "[name=" + name
105            + ",salary=" + salary
106            + ",hireDay=" + hireDay
107            + "]";
108      }
109  }
  • + Share This
  • 🔖 Save To Your Account

InformIT Promotional Mailings & Special Offers

I would like to receive exclusive offers and hear about products from InformIT and its family of brands. I can unsubscribe at any time.

Overview


Pearson Education, Inc., 221 River Street, Hoboken, New Jersey 07030, (Pearson) presents this site to provide information about products and services that can be purchased through this site.

This privacy notice provides an overview of our commitment to privacy and describes how we collect, protect, use and share personal information collected through this site. Please note that other Pearson websites and online products and services have their own separate privacy policies.

Collection and Use of Information


To conduct business and deliver products and services, Pearson collects and uses personal information in several ways in connection with this site, including:

Questions and Inquiries

For inquiries and questions, we collect the inquiry or question, together with name, contact details (email address, phone number and mailing address) and any other additional information voluntarily submitted to us through a Contact Us form or an email. We use this information to address the inquiry and respond to the question.

Online Store

For orders and purchases placed through our online store on this site, we collect order details, name, institution name and address (if applicable), email address, phone number, shipping and billing addresses, credit/debit card information, shipping options and any instructions. We use this information to complete transactions, fulfill orders, communicate with individuals placing orders or visiting the online store, and for related purposes.

Surveys

Pearson may offer opportunities to provide feedback or participate in surveys, including surveys evaluating Pearson products, services or sites. Participation is voluntary. Pearson collects information requested in the survey questions and uses the information to evaluate, support, maintain and improve products, services or sites, develop new products and services, conduct educational research and for other purposes specified in the survey.

Contests and Drawings

Occasionally, we may sponsor a contest or drawing. Participation is optional. Pearson collects name, contact information and other information specified on the entry form for the contest or drawing to conduct the contest or drawing. Pearson may collect additional personal information from the winners of a contest or drawing in order to award the prize and for tax reporting purposes, as required by law.

Newsletters

If you have elected to receive email newsletters or promotional mailings and special offers but want to unsubscribe, simply email information@informit.com.

Service Announcements

On rare occasions it is necessary to send out a strictly service related announcement. For instance, if our service is temporarily suspended for maintenance we might send users an email. Generally, users may not opt-out of these communications, though they can deactivate their account information. However, these communications are not promotional in nature.

Customer Service

We communicate with users on a regular basis to provide requested services and in regard to issues relating to their account we reply via email or phone in accordance with the users' wishes when a user submits their information through our Contact Us form.

Other Collection and Use of Information


Application and System Logs

Pearson automatically collects log data to help ensure the delivery, availability and security of this site. Log data may include technical information about how a user or visitor connected to this site, such as browser type, type of computer/device, operating system, internet service provider and IP address. We use this information for support purposes and to monitor the health of the site, identify problems, improve service, detect unauthorized access and fraudulent activity, prevent and respond to security incidents and appropriately scale computing resources.

Web Analytics

Pearson may use third party web trend analytical services, including Google Analytics, to collect visitor information, such as IP addresses, browser types, referring pages, pages visited and time spent on a particular site. While these analytical services collect and report information on an anonymous basis, they may use cookies to gather web trend information. The information gathered may enable Pearson (but not the third party web trend services) to link information with application and system log data. Pearson uses this information for system administration and to identify problems, improve service, detect unauthorized access and fraudulent activity, prevent and respond to security incidents, appropriately scale computing resources and otherwise support and deliver this site and its services.

Cookies and Related Technologies

This site uses cookies and similar technologies to personalize content, measure traffic patterns, control security, track use and access of information on this site, and provide interest-based messages and advertising. Users can manage and block the use of cookies through their browser. Disabling or blocking certain cookies may limit the functionality of this site.

Do Not Track

This site currently does not respond to Do Not Track signals.

Security


Pearson uses appropriate physical, administrative and technical security measures to protect personal information from unauthorized access, use and disclosure.

Children


This site is not directed to children under the age of 13.

Marketing


Pearson may send or direct marketing communications to users, provided that

  • Pearson will not use personal information collected or processed as a K-12 school service provider for the purpose of directed or targeted advertising.
  • Such marketing is consistent with applicable law and Pearson's legal obligations.
  • Pearson will not knowingly direct or send marketing communications to an individual who has expressed a preference not to receive marketing.
  • Where required by applicable law, express or implied consent to marketing exists and has not been withdrawn.

Pearson may provide personal information to a third party service provider on a restricted basis to provide marketing solely on behalf of Pearson or an affiliate or customer for whom Pearson is a service provider. Marketing preferences may be changed at any time.

Correcting/Updating Personal Information


If a user's personally identifiable information changes (such as your postal address or email address), we provide a way to correct or update that user's personal data provided to us. This can be done on the Account page. If a user no longer desires our service and desires to delete his or her account, please contact us at customer-service@informit.com and we will process the deletion of a user's account.

Choice/Opt-out


Users can always make an informed choice as to whether they should proceed with certain services offered by InformIT. If you choose to remove yourself from our mailing list(s) simply visit the following page and uncheck any communication you no longer want to receive: www.informit.com/u.aspx.

Sale of Personal Information


Pearson does not rent or sell personal information in exchange for any payment of money.

While Pearson does not sell personal information, as defined in Nevada law, Nevada residents may email a request for no sale of their personal information to NevadaDesignatedRequest@pearson.com.

Supplemental Privacy Statement for California Residents


California residents should read our Supplemental privacy statement for California residents in conjunction with this Privacy Notice. The Supplemental privacy statement for California residents explains Pearson's commitment to comply with California law and applies to personal information of California residents collected in connection with this site and the Services.

Sharing and Disclosure


Pearson may disclose personal information, as follows:

  • As required by law.
  • With the consent of the individual (or their parent, if the individual is a minor)
  • In response to a subpoena, court order or legal process, to the extent permitted or required by law
  • To protect the security and safety of individuals, data, assets and systems, consistent with applicable law
  • In connection the sale, joint venture or other transfer of some or all of its company or assets, subject to the provisions of this Privacy Notice
  • To investigate or address actual or suspected fraud or other illegal activities
  • To exercise its legal rights, including enforcement of the Terms of Use for this site or another contract
  • To affiliated Pearson companies and other companies and organizations who perform work for Pearson and are obligated to protect the privacy of personal information consistent with this Privacy Notice
  • To a school, organization, company or government agency, where Pearson collects or processes the personal information in a school setting or on behalf of such organization, company or government agency.

Links


This web site contains links to other sites. Please be aware that we are not responsible for the privacy practices of such other sites. We encourage our users to be aware when they leave our site and to read the privacy statements of each and every web site that collects Personal Information. This privacy statement applies solely to information collected by this web site.

Requests and Contact


Please contact us about this Privacy Notice or if you have any requests or questions relating to the privacy of your personal information.

Changes to this Privacy Notice


We may revise this Privacy Notice through an updated posting. We will identify the effective date of the revision in the posting. Often, updates are made to provide greater clarity or to comply with changes in regulatory requirements. If the updates involve material changes to the collection, protection, use or disclosure of Personal Information, Pearson will provide notice of the change through a conspicuous notice on this site or other appropriate way. Continued use of the site after the effective date of a posted revision evidences acceptance. Please contact us if you have questions or concerns about the Privacy Notice or any objection to any revisions.

Last Update: November 17, 2020