Home > Articles > Programming > Java

Working with Streams in Java

A Java program uses a stream to either read data items from a source or to write data items to a destination. This article by Java expert Jeff Friesen discusses the concept of streams, and shows how to work with some of the more commonly used stream classes.
This article is excerpted from Java 2 By Example, Second Edition (Que, 2001), by Jeff Friesen.
This chapter is from the book

This chapter is from the book

A Java program uses a stream to either read data items from a source or to write data items to a destination. Think of a stream as a conduit by which a sequence of bytes flows from a source to specific program code or from specific program code to a destination. That conduit can be likened to a wire on which an electrical current flows, or to a river of water on which boats and barrels float. Stream sources include files, memory buffers, network sockets, threads, and other streams. Stream destinations include the same entities as stream sources, and other entities (such as printers). When a stream of data items flows from a source, that stream is referred to as an input stream. Similarly, when a stream of data items flows to a destination, that stream is referred to as an output stream. Input and output streams are illustrated in Figure 1.

Figure 1 Data items flow from a source to specific program code over an input stream, and flow from specific program code to a destination over an output stream.

Java divides streams into input and output categories. Java also divides streams into byte-oriented and character-oriented categories. The basic unit of a byte-oriented stream is a byte and the basic unit of a character-oriented stream is a Unicode character.

All byte-oriented input streams are created from objects whose classes derive from the abstract InputStream class, and all character-oriented input streams are created from objects whose classes derive from the abstract Reader class. Those classes share several methods in common, including a close() method and a no-argument read() method. Similarly, all byte-oriented output streams are created from objects whose classes derive from the abstract OutputStream class, and all character-oriented output streams are created from objects whose classes derive from the abstract Writer class. As with the InputStream and Reader classes, OutputStream and Writer share methods in common (such as close() and flush). Each class is located in the java.io package.

NOTE

InputStream's and Reader's read() methods are designed to block (wait) for input if data is not available when either of those methods is called. InputStream declares an available() method that can be called to return an integer identifying the number of bytes that can be read without blocking. Reader has no such method.

An Inventory of Stream Classes

Java's class library includes many stream classes. Rather than attempt to itemize every last stream class, this section focuses on a representative sample: file stream classes, buffered stream classes, data stream classes, piped stream classes, and Zip stream classes.

File Stream Classes

If you need to work with files in either a sequential-access or a random-access manner, you can use the RandomAccessFile class. However, the intent of the RandomAccessFile class is for its objects to manipulate record-oriented flat-file databases. If you are interested in reading an image's bytes, reading the contents of a text file, writing some configuration information to a file, and so forth, you would not use RandomAccessFile. Instead, you would work with various file stream classes: FileInputStream, FileReader, FileOutputStream, and FileWriter. (Those classes are located in the java.io package).

TIP

Use the FileInputStream and FileOutputStream classes to read/write binary data from/to image files, sound files, video files, configuration files and so on. Also, those classes can be used to read/write ASCII-based text files. To read/write modern Unicode-based text files, use FileReader and FileWriter.

The file stream classes include constructors for creating input and output byte-oriented or character-oriented streams that are connected to files opened or created by those constructors. If an input stream constructor cannot find a file to open for input, it will throw a FileNotFoundException object. Similarly, if an output stream constructor cannot create a file (because of bad path information, or for some other reason), it will throw an IOException object.

Because of the various exceptions thrown by their constructors and methods, the file stream classes might seem difficult to use. However, if you follow a pattern similar to the usage pattern that the Copy source code in Listing 1 demonstrates, you should not have trouble.

Listing 1: Copy.java.

// Copy.java

import java.io.*;

class Copy
{
  public static void main (String [] args)
  {
   if (args.length != 2)
   {
     System.out.println ("usage: java Copy srcpath dstpath");
     return;
   }

   FileInputStream fis = null;
   FileOutputStream fos = null;

   try
   {
     fis = new FileInputStream (args [0]);
     fos = new FileOutputStream (args [1]);

     int byte_;
     while ((byte_ = fis.read ()) != -1)
       fos.write (byte_);
   }
   catch (FileNotFoundException e)
   {
     System.out.println ("File not found");
     // Do other stuff related to that exception (if necessary).
   }
   catch (IOException e)
   {
     System.out.println ("I/O Problem: " + e.getMessage ());
     // Do other stuff related to that exception (if necessary).
   }
   finally
   {
     if (fis != null)
       try
       {
         fis.close ();
       }
       catch (IOException e)
       {
       }

     if (fos != null)
       try
       {
         fos.close ();
       }
       catch (IOException e)
       {
       }
   }
  }
}

As its name suggests, Copy is an application that copies data from one file to another. Copy copies bytes from a file identified by a source path to a file identified by a destination path. For example, to copy all bytes contained in Copy.java to Copy.bak, issue the following command line: java Copy Copy.java Copy.bak.

Notice the pattern that Copy's source code uses when working with files. First, because Copy is designed to copy byte-oriented streams instead of character-oriented streams, Copy declares a pair of FileInputStream and FileOutputStream reference variables, and initializes those variables to null. Within a Try statement, Copy attempts to create FileInputStream and FileOutputStream objects. The FileInputStream constructor throws a FileNotFoundException object if it cannot locate the source file and the FileOutputStream constructor throws an IOException object if it is given bad path information to a destination file. Assuming both constructors succeed, a While loop statement repeatedly calls FileInputStream's read() method to read the next byte, and FileOutputStream's write() method to write that byte. The read() method continues to read bytes until end-of-file is encountered. At that time, read() returns -1, and the loop ends. Regardless of whether or not an exception is thrown, the Finally clause executes last. By using If decision statements, it checks that FileInputStream and FileOutputStream objects were created. If one or both of those objects was created, the object's close() method is called to close the underlying file. Because close() throws an IOException object if the underlying file is not open, it is necessary to place close() method calls within their own Try statements. If you follow a pattern similar to what you have just read, you should not experience trouble when working with the file stream classes.

TIP

The FileOutputStream and FileWriter constructors typically erase existing files when creating files. However, it is possible to append bytes or characters to existing files by calling the FileOutputStream(String name, boolean append) and FileWriter(String name, boolean append) constructors, respectively, with true as the value of the append argument.

Buffered Stream Classes

Failing to buffer I/O operations is the leading cause of poor I/O performance. That is not surprising when you consider that disk drives efficiently read and write large aggregates of bytes but are not very efficient when it comes to reading and writing small byte aggregates. Because most of Java's stream classes do not buffer their read and write operations, stream objects are prone to poor I/O performance.

I/O performance can be radically improved by grouping individual bytes (or characters) into aggregates before performing a write operation or reading a large group of bytes (or characters) and returning those bytes (or characters) on an individual basis from a buffer. That is the goal behind Java's BufferedInputStream, BufferedReader, BufferedOutputStream, and BufferedWriter classes. (Those classes are located in the java.io package.)

BufferedInputStream and BufferedReader objects represent buffered input streams that are chained to other input streams so that bytes (or characters) can flow from those other streams into buffered input streams. The following code fragment demonstrates that input stream chaining.

FileInputStream fis = new FileInputStream (pathname);
BufferedInputStream bis = new BufferedInputStream (fis);
System.out.println (bis.read ());

The code fragment creates a FileInputStream object and chains, to that object, a BufferedInputStream object, by passing the FileInputStream object's reference to the BufferedInputStream constructor. The resulting BufferedInputStream object's reference assigns to bis. When bis.read() is called, that read() method checks an internal buffer (associated with the BufferedInputStream object assigned to bis) for at least one byte that can be returned. If a byte exists in that buffer, bis.read() immediately returns. Otherwise, bis.read() internally calls fis.read(byte [] buffer, int offset, int length) to read a large chunk of bytes into the bis object's internal buffer. As long as bis.read() does not have to call fis.read(byte [] buffer, int offset, int length), performance is fast. When bis.read() must call fis.read(byte [] buffer, int offset, int length), performance slows down somewhat, because fis.read(byte [] buffer, int offset, int length) must access the disk drive. However, reading a large chunk of bytes via the fis.read(byte [] buffer, int offset, int length) method call is faster than performing many individual no-argument fis.read() method calls. Therefore, a bis.read() method call is considerably faster than calls to fis.read().

NOTE

To be fair, many platforms buffer data that is to be read from or written to a file. Therefore, the file stream classes do have some sort of buffering at their disposal. However, not all devices that support Java will buffer data at a platform level. Therefore, it is not a good idea to rely on such support. Instead, you should get into the habit of writing code that relies on the buffered stream classes.

BufferedOutputStream and BufferedWriter objects represent buffered output streams that are chained to other output streams so that bytes (or characters) can flow from buffered output streams to those other streams. The following code fragment demonstrates that output stream chaining.

FileOutputStream fos = new FileOutputStream (pathname);
BufferedOutputStream bos = new BufferedOutputStream (fos);
bos.write ('A');

The code fragment creates a FileOutputStream object and chains, to that object, a BufferedOutputStream object, by passing the FileOutputStream object's reference to the BufferedOutputStream constructor. The resulting BufferedOutputStream object's reference assigns to bos. When bos.write ('A'); executes, that method call appends 'A' to the contents of an internal buffer (associated with the BufferedOutputStream object assigned to bos). After that buffer fills, bos.write() calls fos.write() to write the entire buffer to the disk. Because fewer (but larger) writes are made to a disk, performance improves.

The Copy application in Listing 1 was not as efficient as it could have been. By adding support for buffering, Copy can become faster. Listing 2 introduces a BufferedCopy application that uses the BufferedInputStream and BufferedOutputStream classes to support buffering.

Listing 2: BufferedCopy.java.

// BufferedCopy.java

import java.io.*;

class BufferedCopy
{
  public static void main (String [] args)
  {
   if (args.length != 2)
   {
     System.out.println ("usage: java BufferedCopy srcpath dstpath");
     return;
   }

   BufferedInputStream bis = null;
   BufferedOutputStream bos = null;

   try
   {
     FileInputStream fis = new FileInputStream (args [0]);
     bis = new BufferedInputStream (fis);

     FileOutputStream fos = new FileOutputStream (args [1]);
     bos = new BufferedOutputStream (fos);

     int byte_;
     while ((byte_ = bis.read ()) != -1)
       bos.write (byte_);
   }
   catch (FileNotFoundException e)
   {
     System.out.println ("File not found");
     // Do other stuff related to that exception (if necessary).
   }
   catch (IOException e)
   {
     System.out.println ("I/O Problem: " + e.getMessage ());
     // Do other stuff related to that exception (if necessary).
   }
   finally
   {
     if (bis != null)
       try
       {
         bis.close ();
       }
       catch (IOException e)
       {
       }

     if (bos != null)
       try
       {
         bos.close ();
       }
       catch (IOException e)
       {
       }
   }
  }
}

There is one interesting item to note about BufferedCopy's source code: bis.close() and bos.close() appear instead of fis.close() and fos.close(). All of the stream classes thus far presented contain close() methods. When you chain a buffered stream to a file stream, you might not know which close() method to call. The answer, as demonstrated by BufferedCopy, is to call the close() method on the stream that chains itself to another stream. In BufferedCopy, those methods are bis.close() and bos.close().

NOTE

The BufferedInputStream and BufferedReader classes support the capabilities of marking a particular point in a stream and coming back to that point to reread a sequence of bytes (or characters). Those capabilities manifest by way of the mark() and reset() methods. Use mark() to "remember" a point in the input stream and reset() to cause all bytes that have been read since the most recent mark operation to be reread, before new bytes are read from the stream to which the buffered input stream is chained.

Because the mark() and reset() methods are declared in InputStream and Reader, you might think every class supports those methods. However, that is not the case. Although BufferedInputStream and BufferedReader support mark() and reset(), many other input streams do not. Before calling those methods, find out if an input stream supports mark() and reset(), by calling the markSupported() method. If an input stream supports the mark() and reset() methods, markSupported() returns true.

Data Stream Classes

A problem with the FileInputStream and FileOutputStream classes is that they only work at the byte level. What do you do when you need to read integers, write floating-point values, and read or write some other non-byte value from/to a file? The answer is to use Java's DataInputStream and DataOutputStream classes (located in the java.io package portion of Java's standard class library).

As with the buffered stream classes, the data stream classes are designed so that their objects can be chained to other streams. However, you can only chain data stream objects to byte-oriented streams. For example, you can chain a data input stream to a FileInputStream object and call the data input stream's methods to read integer, floating-point, and other data items, but you cannot directly chain a data input stream object to a FileReader object.

For a glimpse of using DataOutputStream and DataInputStream to write and read non-byte-oriented data items to and from underlying FileOutputStream and FileInputStream objects, examine the DOSDISDemo source code in Listing 3.

Listing 3: DOSDISDemo.java.

// DOSDISDemo.java

import java.io.*;

class DOSDISDemo
{
  public static void main (String [] args)
  {
   DataOutputStream dos = null;

   try
   {
     FileOutputStream fos = new FileOutputStream ("data.dat");
     dos = new DataOutputStream (fos);

     dos.writeInt (256);
     dos.writeDouble (Math.PI);
     dos.writeUTF ("Java");

   }
   catch (IOException e)
   {
     System.out.println (e.getMessage ());
     return;
   }
   finally
   {
     if (dos != null)
       try
       {
         dos.close ();
       }
       catch (IOException e)
       {
       }
   }

   DataInputStream dis = null;

   try
   {
     FileInputStream fis = new FileInputStream ("data.dat");
     dis = new DataInputStream (fis);

     System.out.println (dis.readInt ());
     System.out.println (dis.readDouble ());
     System.out.println (dis.readUTF ());
   }
   catch (IOException e)
   {
     System.out.println (e.getMessage ());
     return;
   }
   finally
   {
     if (dis != null)
       try
       {
         dis.close ();
       }
       catch (IOException e)
       {
       }
   }
  }
}

DOSDISDemo introduces the UTF concept, by way of its writeUTF() and readUTF() method calls. UTF stands for Unicode Text Format and it is an encoding format used for efficiently storing and retrieving text characters. According to the format used by Java, which is a slight variant of UTF-8:

  • All characters whose Unicode values range from \u0001 to \u007f are represented by a single byte, with the most significant bit set to 0.

  • The null character Unicode value (\u0000) and all characters whose Unicode values range from \u0080 to \u07ff are represented by two bytes, with the most significant three bits of the most significant byte being 1, 1, and 0 (in a left-to-right order), and the most significant two bits of the least significant byte being 1 and 0 (in a left-to-right order).

  • All characters whose Unicode values range from \u0800 to \uffff are represented by three bytes, with the most significant four bits of the most significant byte being 1, 1, 1 and 0 (in a left-to-right order) and the most significant two bits of each of the remaining two bytes being 1 and 0 (in a left-to-right order).

When run, DOSDISDemo produces the following output:

256
3.141592653589793
Java

NOTE

Objects created from either the buffered stream or the data stream classes are known as filter streams. That name derives from their use in filtering bytes (or characters) that flow into a buffered input stream or filtering bytes that flow into a data input stream. Furthermore, that name derives from their use in filtering bytes (or characters) that flow out of the buffered output stream or filtering bytes that flow out of the data output stream. In addition to buffered and data stream classes, Java's standard class library includes other classes that are used to perform filtering operations.

Piped Stream Classes

Threads are often required to communicate. A technique that is often used by threads wishing to communicate involves piped streams.

The idea behind piped streams is to connect a piped output stream to a piped input stream. Then, one thread writes data to the piped output stream and another thread reads that data by way of the piped input stream. Although there are no synchronization problems with piped streams, those streams have limited sizes. As a result, a writing thread could write more output to a piped output stream than that stream can accommodate, and the excess output would be lost. To prevent that from happening, the reading thread must be responsive. To support piped streams, Java supplies the PipedInputStream, PipedReader, PipedOutputStream, and PipedWriter classes in its standard class library. (Those classes are located in the java.io package.)

CAUTION

Deadlock might occur if a single thread uses a piped output stream connected to a piped input stream, and performs both writing and reading operations on that stream.

Creating a piped input stream connected to a piped output stream is not difficult, as the following code fragment attests:

PipedWriter pw = new PipedWriter ();
PipedReader pr = new PipedReader (pw);

The code fragment first creates a piped output stream (as represented by the PipedWriter object) and then creates a piped input stream (as represented by a PipedReader object) that binds itself to the piped output stream. When that's done, a writing thread can call pw.write() to output data to the piped output stream, whereas a reading thread can call pr.read() to read that output over its piped input stream.

Listing 4 presents source code to a PipedThreads application that demonstrates one thread piping output to another thread, via piped streams.

Listing 4: PipedThreads.java.

// PipedThreads.java

import java.io.*;

class MyThread extends Thread
{
  private PipedReader pr;
  private PipedWriter pw;

  MyThread (String name, PipedReader pr, PipedWriter pw)
  {
   super (name);

   this.pr = pr;
   this.pw = pw;
  }

  public void run ()
  {
   try
   {
     if (getName ().equals ("src"))
     {
       for (int i = 0; i < 15; i++)
         pw.write ("src " + " A" + i + "\n"); // src writes

       pw.close ();
     }
     else
     {
       int item;
       while ((item = pr.read ()) != -1)
        System.out.print ((char) item);    // dst reads

       pr.close ();
     }
   }
   catch (IOException e)
   {
   }
  }
}

class PipedThreads
{
  public static void main (String [] args) throws IOException
  {
   PipedWriter pw = new PipedWriter ();
   PipedReader pr = new PipedReader (pw);

   MyThread mt1 = new MyThread ("src", pr, pw);
   MyThread mt2 = new MyThread ("dst", pr, pw);

   mt1.start ();

   try
   {
     Thread.sleep (2000);
   }
   catch (InterruptedException e)
   {
   }

   mt2.start ();
  }
}

When you run PipedThreads, you will see the following output:

src A0
src A1
src A2
src A3
src A4
src A5
src A6
src A7
src A8
src A9
src A10
src A11
src A12
src A13
src A14

TIP

For an additional example of piped streams, check out How to Use Pipe Streams in the Essential Java Classes trail of Sun's online Java Tutorial (http://java.sun.com/docs/books/tutorial/essential/io/pipedstreams.html).

Zip Stream Classes

Did you know that Java makes it easy to read and write Zip files? Zip support manifests itself in the standard class library by way of the ZipInputStream and ZipOutputStream filter stream classes, and other classes that (along with ZipInputStream and ZipOutputStream) are part of the java.util.zip package. By using those classes, it is possible to create a command-line version of the popular WinZip utility.

To give you a taste for working with Zip stream classes, Listing 5 presents source code to a ZipReader application. That application uses ZipInputStream to retrieve all entries in a Zip file. For each entry, that entry's name prints.

Listing 5: ZipReader.java.

// ZipReader.java

import java.io.*;
import java.util.zip.*;

class ZipReader
{
  public static void main (String [] args)
  {
   if (args.length != 1)
   {
     System.out.println ("usage: java ZipReader pathname");
     return;
   }

   ZipInputStream zis = null;

   try
   {
     FileInputStream fis = new FileInputStream (args [0]);
     zis = new ZipInputStream (fis);

     ZipEntry ze;

     while ((ze = zis.getNextEntry ()) != null)
       System.out.println (ze.getName ());
   }
   catch (IOException e)
   {
     System.out.println (e.getMessage ());
   }
   finally
   {
     try
     {
       zis.close ();
     }
     catch (IOException e)
     {
     }
   }
  }
}

To run ZipReader, you need access to either a Zip file or a Jar file (which is basically a Zip file with a .jar extension). For example, assuming the SDK's tools.jar file is placed in the same directory as ZipReader.class, issue java ZipReader tools.jar to obtain a list of all packages and classes contained in that Jar file.

TIP

For another example of Zip file extraction, check out Sun's Unpacking Zip Files TechTip (http://developer.java.sun.com/developer/TechTips/1998/tt0421.html).

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