The java.io
package provides an extensive library of classes for dealing with input and output. Java provides streams as a general mechanism for dealing with data I/O. Streams implement sequential access of data. There are two kinds of streams: byte streams and character streams (a.ka. binary streams and text streams, respectively). An input stream is an object that an application can use to read a sequence of data, and an output stream is an object that an application can use to write a sequence of data. An input stream acts as a source of data, and an output stream acts as a destination of data. The following entities can act as both input and output streams:
• an array of bytes or characters
• a file
• a pipe (a mechanism by which a program can communicate data to another progam during execution)
• a network connection
Streams can be chained with filters to provide new functionality. In addition to dealing with bytes and characters, streams are provided for input and output of Java primitive values and objects. The java.io
package also provides a general interface to interact with the file system of the host platform.
The File
class provides a general machine-independent interface for the file system of the underlying platform. A File
object represents the pathname of a file or directory in the host file system. An application can use the functionality provided by the File
class for handling files and directories in the file system. The File
class is not meant for handling the contents of files. For that purpose, there are the FileInputStream
and FileOutputStream
classes, which are discussed later in this chapter.
The pathname for a file or directory is specified using the naming conventions of the host system. However, the File
class defines platform-dependent constants that can be used to handle file and directory names in a platform-independent way:
public static final char separatorChar
public static final String separator
Defines the character or string that separates the directory and the file components in a pathname. This separator is '/'
, ''
or ':'
for Unix, Windows, and Macintosh, respectively.
public static final char pathSeparatorChar
public static final String pathSeparator
Defines the character or string that separates the file or directory names in a “path list.” This character is ':'
or ';'
for Unix and Windows, respectively.
Some examples of pathnames are:
/book/chapter1
on UnixC:ookchapter1
on WindowsHD:book:chapter1
on Macintosh
Some examples of path lists are:
/book:/manual:/draft
on UnixC:ook;D:manual;A:draft
on Windows
Files and directories can be referenced using both absolute and relative pathnames, but the pathname must follow the conventions of the host platform. On Unix platforms, a pathname is absolute if its first character is the separator character. On Windows platforms, a path is absolute if the ASCII ''
is the first character, or follows the volume name (e.g., C:
), in a pathname. On the Macintosh, a pathname is absolute if it begins with a name followed by a colon. Java programs should not rely on system-specific pathname conventions. The File
class provides facilities to construct pathnames in a platform-independent way.
The File
class has various constructors for associating a file or a directory pathname to an object of the File
class. Creating a File
object does not mean creation of any file or directory based on the pathname specified. A File
instance, called the abstract pathname, is a representation of the pathname of a file and directory. The pathname cannot be changed once the File
object is created.
File(String pathname)
The pathname (of a file or a directory) can be an absolute pathname or a pathname relative to the current directory. An empty string as argument results in an abstract pathname for the current directory.
// "/book/chapter1" – absolute pathname of a file
File chap1 = new File(File.separator + "book" + File.separator + "chapter1");
// "draft/chapters" – relative pathname of a directory
File draftChapters = new File("draft" + File.separator + "chapters");
File(String directoryPathname, String fileName)
This creates a File
object whose pathname is as follows: directoryPathname
+ separator + fileName.
// "/book/chapter1" – absolute pathname of a file
File updatedChap1 = new File(File.separator + "book", "chapter1");
File(File directory, String fileName)
If the directory
argument is null
, the resulting File
object represents a file in the current directory. If the directory argument is not null
, it creates a File
object that represents a file in the given directory. The pathname of the file is then the pathname of the directory File
object + separator + fileName.
// "chapter13" – relative pathname of a file
File parent = null;
File chap13 = new File(parent, "chapter13");
// "draft/chapters/chapter13" – relative pathname of a file
File draftChapters = new File("draft" + File.separator + "chapters");
File updatedChap13 = new File(draftChapters, "chapter13");
An object of the File
class provides a handle to a file or directory in the file system, and can be used to create, rename, and delete the entry.
A File
object can also be used to query the file system for information about a file or directory:
• whether the entry exists
• whether the File
object represents a file or directory
• get and set read, write, or execute permissions for the entry
• get pathname information about the file or directory
• list all entries under a directory in the file system
Many methods of the File
class throw a SecurityException
in the case of a security violation, for example if read or write access is denied. Some methods also return a boolean value to indicate whether the operation was successful.
The File
class provides a number of methods for obtaining the platform-dependent representation of a pathname and its components.
On Unix, the name part of "/book/chapters/one"
is "one"
.
On Windows platforms, the name part of "c:javainjavac"
is "javac"
.
On the Macintosh, the name part of "HD:java-tools:javac"
is "javac"
.
The strings "."
and ".."
generally designate the current directory and the parent directory in pathnames, respectively.
String getPath()
The method returns the (absolute or relative) pathname of the file represented by the File
object.
String getAbsolutePath()
If the File
object represents an absolute pathname, this pathname is returned, otherwise the returned pathname is constructed by concatenating the current directory pathname, the separator character and the pathname of the File
object.
String getCanonicalPath() throws IOException
Also platform-dependent, the canonical path usually specifies an absolute pathname in which all relative references have been completely resolved.
For example, if the File
object represented the absolute pathname "c:ook chapter1"
on Windows, this pathname would be returned by these methods. On the other hand, if the File
object represented the relative pathname "..ookchapter1"
and the current directory had the absolute pathname "c:documents"
, the pathname returned by the getPath()
, getAbsolutePath()
, and getCanonicalPath()
methods would be "..ookchapter1"
, "c:documents..ookchapter1"
and "c:ookchapter1"
, respectively.
String getParent()
The parent part of the pathname of this File
object is returned if one exists, otherwise the null
value is returned. The parent part is generally the prefix obtained from the pathname after deleting the file or directory name component found after the last occurrence of the separator character. However, this is not true for all platforms.
On Unix, the parent part of "/book/chapter1"
is "/book"
, whose parent part is "/"
, which in turn has no parent.
On Windows platforms, the parent part of "c:java-tools"
is "c:"
, which in turn has no parent.
On the Macintosh, the parent part of "HD:java-tools"
is "HD:"
, which in turn has no parent.
boolean isAbsolute()
Whether a File
object represents an absolute pathname can be determined using this method.
The following three methods can be used to query the file system about the modification time of a file or directory, determine the size (in bytes) of a file, and ascertain whether two pathnames are identical.
long lastModified()
The modification time returned is encoded as a long
value, and should only be compared with other values returned by this method.
long length()
Returns the size (in bytes) of the file represented by the File
object.
boolean equals(Object obj)
This method just compares the pathnames of the File
objects, and returns true
if they are identical. On Unix systems, alphabetic case is significant in comparing pathnames; on Windows systems it is not.
A File
object is created using a pathname. Whether this pathname denotes an entry that actually exists in the file system can be checked using the exists()
method:
Since a File
object can represent a file or a directory, the following methods can be used to distinguish whether a given File
object represents a file or a directory, respectively:
Write, read and execute permissions can be set by calling the following methods. If the first argument is true
, the operation permission is set; otherwise it is cleared. If the second argument is true
, the permission only affects the owner; otherwise it affects all users. These methods throw a SecurityException
if permission cannot be changed. It should be noted that the exact interpretation of these permissions is platform dependent.
To check whether the specified file has write, read, or execute permissions, the following methods can be used. They throw a SecurityException
if general access is not allowed, i.e., the application is not even allowed to check whether it can read, write or execute a file.
The entries in a specified directory can be obtained as an array of file names or abstract pathnames using the following list()
methods. The current directory and the parent directory are excluded from the list.
String[] list()
String[] list(FilenameFilter filter)
File[] listFiles()
File[] listFiles(FilenameFilter filter)
File[] listFiles(FileFilter filter)
The filter
argument can be used to specify a filter that determines whether an entry should be included in the list. These methods return null
if the abstract pathname does not denote a directory, or if an I/O error occurs. A filter is an object of a class that implements either of these two interfaces:
interface FilenameFilter {
boolean accept(File currentDirectory, String entryName);
}
interface FileFilter {
boolean accept(File pathname);
}
The list()
methods call the accept()
methods of the filter for each entry to determine whether the entry should be included in the list.
The File
class can be used to create files and directories. A file can be created whose pathname is specified in a File
object using the following method:
boolean createNewFile() throws IOException
It creates a new, empty file named by the abstract pathname if, and only if, a file with this name does not already exist. The returned value is true
if the file was successfully created, false
if the file already exists. Any I/O error results in an IOException
.
A directory whose pathname is specified in a File
object can be created using the following methods:
boolean mkdir()
boolean mkdirs()
The mkdirs()
method creates any intervening parent directories in the pathname of the directory to be created.
A file or a directory can be renamed, using the following method which takes the new pathname from its argument. It throws a SecurityException
if access is denied.
A file or a directory can be deleted using the following method. In the case of a directory, it must be empty before it can be deleted. It throws a SecurityException
if access is denied.
Example 11.1 Listing Files Under a Directory
import java.io.File;
import java.io.IOException;
public class DirectoryLister {
public static void main(String[] args) {
if (args.length == 0) { // (1)
System.err.println("Please specify a directory name.");
return;
}
File entry = new File(args[0]); // (2)
listDirectory(entry);
}
public static void listDirectory(File entry) {
try {
if (!entry.exists()) { // (3)
System.out.println(entry.getName() + " not found.");
return;
}
if (entry.isFile()) {
// Write the pathname of the entry:
System.out.println(entry.getCanonicalPath()); // (4)
} else if (entry.isDirectory()) {
// Create list of entries for this directory:
String[] entryNames = entry.list(); // (5)
for (String entryName : entryNames) {
// Create a File object for each entry name:
File thisEntry = new File(entry.getPath(), entryName); // (6)
// List this entry by a recursive call:
listDirectory(thisEntry); // (7)
}
}
} catch(IOException e) { System.out.println("Error: " + e); }
}
}
Running the program on a Windows platform:
java DirectoryLister D:docsJC-Bookspecial
produces the following output:
D:docsJC-Bookspecialook19990308JC-14-applets.fm
D:docsJC-Bookspecialook19990308JC-16-swing.fm
D:docsJC-BookspecialJC-11-awtlayout.fm
The class DirectoryLister
in Example 11.1 lists all entries in a directory specified in the command line. If no directory is given, an error message is printed; either by the print statement at (1) or as a result of an exception at (2). In the method listDirectory()
, each entry is tested to see if it exists, as shown at (3). The entry could be an alias (symbolic link in Unix or shortcut in Windows terminology) and its destination might not exist. The method determines whether the entry is a file, in which case the absolute pathname is listed, as shown at (4). In the case of a directory, an array of entry names is created, as shown at (5). For each entry in the directory, a File
object is created, as shown at (6). The method listDirectory()
is called recursively for each entry, as shown at (7).
The abstract classes InputStream
and OutputStream
are the root of the inheritance hierarchies for handling the reading and writing of bytes (Figure 11.1). Their subclasses, implementing different kinds of input and output streams, override the following methods from the InputStream
and OutputStream
classes to customize the reading and writing of bytes, respectively:
The InputStream
class:
int read() throws IOException
int read(byte[] b) throws IOException
int read(byte[] b, int off, int len) throws IOException
Note that the first read()
method reads a byte, but returns an int
value. The byte read resides in the eight least significant bits of the int
value, while the remaining bits in the int
value are zeroed out. The read()
methods return the value –1 when the end of the stream is reached.
The OutputStream
class:
void write(int b) throws IOException
void write(byte[] b) throws IOException
void write(byte[] b, int off, int len) throws IOException
The first write()
method takes an int
as argument, but truncates it down to the eight least significant bits before writing it out as a byte.
void close() throws IOException
void flush() throws IOException Only for OutputStream
A stream should be closed when no longer needed, to free system resources. Closing an output stream automatically flushes the stream, meaning that any data in its internal buffer is written out. An output stream can also be manually flushed by calling the second method.
Read and write operations on streams are synchronous (blocking) operations, i.e., a call to a read or write method does not return before a byte has been read or written.
Many methods in the classes contained in the java.io
package throw the checked IOException
. A calling method must therefore either catch the exception explicitly, or specify it in a throws
clause.
Table 11.1 and Table 11.2 give an overview of the byte streams. Usually an output stream has a corresponding input stream of the same type.
The classes FileInputStream
and FileOutputStream
define byte input and output streams that are connected to files. Data can only be read or written as a sequence of bytes.
An input stream for reading bytes can be created using the following constructors:
FileInputStream(String name) throws FileNotFoundException
FileInputStream(File file) throws FileNotFoundException
FileInputStream(FileDescriptor fdObj)
The file can be specified by its name, through a File
object, or using a FileDescriptor
object.
If the file does not exist, a FileNotFoundException
is thrown. If it exists, it is set to be read from the beginning. A SecurityException
is thrown if the file does not have read access.
An output stream for writing bytes can be created using the following constructors:
FileOutputStream(String name) throws FileNotFoundException
FileOutputStream(String name, boolean append) throws FileNotFoundException
FileOutputStream(File file) throws IOException
FileOutputStream(FileDescriptor fdObj)
The file can be specified by its name, through a File
object, or using a File Descriptor
object.
If the file does not exist, it is created. If it exists, its contents are reset, unless the appropriate constructor is used to indicate that output should be appended to the file. A SecurityException
is thrown if the file does not have write access or it cannot be created.
The FileInputStream
class provides an implementation for the read()
methods in its superclass InputStream
. Similarly, the FileOutputStream
class provides an implementation for the write()
methods in its superclass OutputStream
.
Example 11.2 demonstrates usage of writing and reading bytes to and from file streams. It copies the contents of one file to another file. The input and the output file names are specified on the command line. The streams are created at (1) and(2). The input file is read one byte at a time and written straight to the output file, as shown in the try
block at (3). The end of file is reached when the read()
method returns the value -1. The streams are explicitly closed, as shown at (4). Note that most of the code consists of try
-catch
constructs to handle the various exceptions. The example could be optimized by using buffering for reading and writing several bytes at a time.
Example 11.2 Copy a File
/* Copy a file.
Command syntax: java CopyFile <from_file> <to_file>
*/
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
class CopyFile {
public static void main(String[] args) {
FileInputStream fromFile;
FileOutputStream toFile;
// Assign the files
try {
fromFile = new FileInputStream(args[0]); // (1)
toFile = new FileOutputStream(args[1]); // (2)
} catch(FileNotFoundException e) {
System.err.println("File could not be copied: " + e);
return;
} catch(ArrayIndexOutOfBoundsException e) {
System.err.println("Usage: CopyFile <from_file> <to_file>");
return;
}
// Copy bytes
try { // (3)
while (true) {
int i = fromFile.read();
if(i == -1) break; // check end of file
toFile.write(i);
}
} catch(IOException e) {
System.err.println("Error reading/writing.");
}
// Close the files
try { // (4)
fromFile.close();
toFile.close();
} catch(IOException e) {
System.err.println("Error closing file.");
}
}
}
A filter is a high-level stream that provides additional functionality to an underlying stream to which it is chained. The data from the underlying stream is manipulated in some way by the filter. The FilterInputStream
and FilterOutputStream
classes, together with their subclasses, define input and output filter streams. The subclasses BufferedInputStream
and BufferedOutputStream
implement filters that buffer input from and output to the underlying stream, respectively. The subclasses DataInputStream
and DataOutputStream
implement filters that allow binary representation of Java primitive values to be read and written, respectively, to and from an underlying stream.
The java.io
package contains the two interfaces DataInput
and DataOutput
, that streams can implement to allow reading and writing of binary representations of Java primitive values (boolean
, char
, byte
, short
, int
, long
, float
, double
). The methods for writing binary representations of Java primitive values are named write
X, where X is any Java primitive data type. The methods for reading binary representations of Java primitive values are similarly named read
X. Table 11.3 gives an overview of the read
X()
and write
X()
methods found in these two interfaces. A file containing binary values (i.e., binary representation of Java primitive vales) is usually called a binary file.
Note the methods provided for reading and writing strings. Whereas the methods readChar()
and writeChar()
handle a single character, the methods readLine()
and writeChars()
handle a string of characters. The methods readUTF()
and writeUTF()
also read and write characters, but use the UTF-8 character encoding. However, the recommended practice for reading and writing characters is to use character streams, called readers and writers, that are discussed in Section 11.4.
The filter streams DataOutputStream
and DataInputStream
implement DataOutput
and DataInput
interfaces, respectively, and can be used to read and write binary representations of Java primitive values to and from an underlying stream. Both the write
X()
and read
X()
methods throw an IOException
in the event of an I/O error. In particular, the read
X()
methods throw an EOFException
(a subclass of IOEXception
) if the input stream does not contain the correct number of bytes to read. Bytes can also be skipped from a DataInput
stream, using the skipBytes(int n)
method which skips n
bytes. The following constructors can be used to set up filters for reading and writing Java primitive values, respectively, from an underlying stream:
To write the binary representation of Java primitive values to a binary file, the following procedure can be used, which is also depicted in Figure 11.2.
1. Create a FileOutputStream
:
FileOutputStream outputFile = new FileOutputStream("primitives.data");
2. Create a DataOutputStream
which is chained to the FileOutputStream
:
DataOutputStream outputStream = new DataOutputStream(outputFile);
3. Write Java primitive values using relevant write
X()
methods:
outputStream.writeBoolean(true);
outputStream.writeChar('A'), // int written as Unicode char
outputStream.writeByte(Byte.MAX_VALUE); // int written as 8-bits byte
outputStream.writeShort(Short.MIN_VALUE); // int written as 16-bits short
outputStream.writeInt(Integer.MAX_VALUE);
outputStream.writeLong(Long.MIN_VALUE);
outputStream.writeFloat(Float.MAX_VALUE);
outputStream.writeDouble(Math.PI);
Note that in the case of char
, byte
, and short
data types, the int
argument to the write
X()
method is converted to the corresponding type, before it is written (see Table 11.3).
4. Close the filter stream, which also closes the underlying stream:
outputStream.close();
To read the binary representation of Java primitive values from a binary file the following procedure can be used, which is also depicted in Figure 11.2.
1. Create a FileInputStream
:
FileInputStream inputFile = new FileInputStream("primitives.data");
2. Create a DataInputStream
which is chained to the FileInputStream
:
DataInputStream inputStream = new DataInputStream(inputFile);
3. Read the (exact number of) Java primitive values in the same order they were written out, using relevant readX()
methods:
boolean v = inputStream.readBoolean();
char c = inputStream.readChar();
byte b = inputStream.readByte();
short s = inputStream.readShort();
int i = inputStream.readInt();
long l = inputStream.readLong();
float f = inputStream.readFloat();
double d = inputStream.readDouble();
4. Close the filter stream, which also closes the underlying stream:
inputStream.close();
Example 11.3 uses both procedures described above: first to write and then to read some Java primitive values to and from a file. It also checks to see if the end of the stream has been reached, signalled by an EOFException
. The values are also written to the standard input stream.
Example 11.3 Reading and Writing Binary Values
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.EOFException;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class BinaryValuesIO {
public static void main(String[] args) throws IOException {
// Create a FileOutputStream.
FileOutputStream outputFile = new FileOutputStream("primitives.data");
// Create a DataOutputStream which is chained to the FileOutputStream.
DataOutputStream outputStream = new DataOutputStream(outputFile);
// Write Java primitive values in binary representation:
outputStream.writeBoolean(true);
outputStream.writeChar('A'), // int written as Unicode char
outputStream.writeByte(Byte.MAX_VALUE); // int written as 8-bits byte
outputStream.writeShort(Short.MIN_VALUE); // int written as 16-bits short
outputStream.writeInt(Integer.MAX_VALUE);
outputStream.writeLong(Long.MIN_VALUE);
outputStream.writeFloat(Float.MAX_VALUE);
outputStream.writeDouble(Math.PI);
// Close the output stream, which also closes the underlying stream.
outputStream.flush();
outputStream.close();
// Create a FileInputStream.
FileInputStream inputFile = new FileInputStream("primitives.data");
// Create a DataInputStream which is chained to the FileInputStream.
DataInputStream inputStream = new DataInputStream(inputFile);
// Read the binary representation of Java primitive values
// in the same order they were written out:
boolean v = inputStream.readBoolean();
char c = inputStream.readChar();
byte b = inputStream.readByte();
short s = inputStream.readShort();
int i = inputStream.readInt();
long l = inputStream.readLong();
float f = inputStream.readFloat();
double d = inputStream.readDouble();
// Check for end of stream:
try {
int value = inputStream.readByte();
System.out.println("More input: " + value);
} catch (EOFException eofe) {
System.out.println("End of stream");
} finally {
// Close the input stream, which also closes the underlying stream.
inputStream.close();
}
// Write the values read to the standard input stream:
System.out.println("Values read:");
System.out.println(v);
System.out.println(c);
System.out.println(b);
System.out.println(s);
System.out.println(i);
System.out.println(l);
System.out.println(f);
System.out.println(d);
}
}
Output from the program:
End of stream
Values read:
true
A
127
-32768
2147483647
-9223372036854775808
3.4028235E38
3.141592653589793
11.1 Which of these can act both as the source of an input stream and as the destination of an output stream, based on the classes provided by the java.io
package?
Select the four correct answers.
(a) A file
(b) A network connection
(c) A pipe
(d) A string
(e) An array of chars
11.2 Which of these statements about the constant named separator
of the File
class are true?
Select the two correct answers.
(a) The variable is of type char
.
(b) The variable is of type String
.
(c) It can be assumed that the value of the variable always is the character '/'
.
(d) It can be assumed that the value of the variable always is one of '/'
, ''
or ':'
.
(e) The separator can consist of more than one character.
11.3 Which one of these methods in the File
class will return the name of the entry, excluding the specification of the directory in which it resides?
Select the one correct answer.
(a) getAbsolutePath()
(b) getName()
(c) getParent()
(d) getPath()
(e) None of the above.
11.4 What will the method length()
in the class File
return?
Select the one correct answer.
(a) The number of characters in the file.
(b) The number of kilobytes in the file.
(c) The number of lines in the file.
(d) The number of words in the file.
(e) None of the above.
11.5 Given the following program:
import java.io.File;
import java.io.IOException;
public final class Filing {
public static void main (String[] args) throws IOException {
File file = new File("./documents","../book/../chapter1");
System.out.println(file.getPath());
System.out.println(file.getAbsolutePath());
System.out.println(file.getCanonicalPath());
System.out.println(file.getName());
System.out.println(file.getParent());
}
}
Assume that the current or working directory has the absolute path "/wrk"
. Which lines below will not be included in the output from the program?
Select the two correct answers.
(a) ./documents/../book/../chapter1
(b) ./documents/book/chapter1
(c) /wrk/./documents/../book/../chapter1
(d) /wrk/documents/book/chapter1
(e) /wrk/chapter1
(f) chapter1
(g) ./documents/../book/..
11.6 Given the following program:
import java.io.File;
public class ListingFiles {
public static void main(String[] args) {
File currentDirectory = new File(".");
printFiles1(currentDirectory);
printFiles2(currentDirectory);
printFiles3(currentDirectory);
}
public static void printFiles1(File currentDirectory) {
String[] entryNames = currentDirectory.list();
for (String entryName : entryNames) {
System.out.println(entryName);
}
}
public static void printFiles2(File currentDirectory) {
File[] entries = currentDirectory.listFiles();
for (File entry : entries) {
System.out.println(entry);
}
}
public static void printFiles3(File currentDirectory) {
File[] entries = currentDirectory.listFiles();
for (File entry : entries) {
System.out.println(entry.getPath());
}
}
}
Assume that the current or working directory has the absolute path "/wrk"
and contains only one file with the name "ListingFiles.class"
.
Which statement is true about the program?
Select the one correct answer.
(a) All three methods printFiles1()
, printFiles2()
, and printFiles3()
will produce the same output.
(b) Only the methods printFiles1()
and printFiles2()
, will produce the same output.
(c) Only the methods printFiles2()
and printFiles3()
, will produce the same output.
(d) Only the methods printFiles1()
and printFiles3()
, will produce the same output.
(e) The program does not compile because the list()
method does not exist in the File
class.
11.7 A file is readable but not writable on the file system of the host platform. What will be the result of calling the method canWrite()
on a File
object representing this file?
Select the one correct answer.
(a) A SecurityException
is thrown.
(b) The boolean value false
is returned.
(c) The boolean value true
is returned.
(d) The file is modified from being unwritable to being writable.
(e) None of the above.
11.8 What is the type of the parameter given to the method renameTo()
in the class File
?
Select the one correct answer.
(a) File
(b) FileDescriptor
(c) FileNameFilter
(d) String
(e) char[]
11.9 If write(0x01234567)
is called on an instance of OutputStream
, what will be written to the destination of the stream?
Select the one correct answer.
(a) The bytes 0x01
, 0x23
, 0x34
, 0x45
, and 0x67
, in that order.
(b) The bytes 0x67
, 0x45
, 0x34
, 0x23
, and 0x01
, in that order.
(c) The byte 0x01
.
(d) The byte 0x67
.
(e) None of the above.
11.10 Given the following code, under which circumstances will the method return false
?
public static boolean test(InputStream is) throws IOException {
int value = is.read();
return value >= 0;
}
Select the one correct answer.
(a) A character of more than 8 bits was read from the input stream.
(b) An I/O error occurred.
(c) Never.
(d) The end of the stream was reached in the input stream.
11.11 Which of these classes provides methods for writing binary representations of Java primitive values?
Select the two correct answers.
(a) DataOutputStream
(b) FileOutputStream
(c) ObjectOutputStream
(d) PrintStream
(e) BufferedOutputStream
11.12 Given the following program:
import java.io.DataInputStream;
import java.io.EOFException;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class Endings {
public static void main(String[] args) {
try {
FileInputStream fos = new FileInputStream("info.dat");
DataInputStream dis = new DataInputStream(fos);
int i = dis.readByte();
while (i != -1) {
System.out.print((byte)i + "|");
i = dis.readByte();
}
} catch (FileNotFoundException fnf) {
System.out.println("File not found");
} catch (EOFException eofe) {
System.out.println("End of stream");
} catch (IOException ioe) {
System.out.println("Input error");
}
}
}
Assume that the file "info.dat"
exits in the current directory and has only the byte
values 10
, 20
and 30
, stored in that order.
Which statement is true about the program?
Select the one correct answer.
(a) The program will not compile because a certain unchecked exception is not caught.
(b) The program will compile and print 10|20|30|Input error
.
(c) The program will compile and print 10|20|30|End of stream
.
(d) The program will compile and print 10|20|30|
, and then block in order to read from the file.
(e) The program will compile and print 10|20|30|
, and terminate because of an uncaught exception.
A character encoding is a scheme for representing characters. Java programs represent values of the char
type internally in the 16-bit Unicode character encoding, but the host platform might use another character encoding to represent and store characters externally. For example, the ASCII (American Standard Code for Information Interchange) character encoding is widely used to represent characters on many platforms. However, it is only one small subset of the Unicode standard.
The abstract classes Reader
and Writer
are the roots of the inheritance hierarchies for streams that read and write Unicode characters using a specific character encoding (as shown in Figure 11.3). A reader is an input character stream that reads a sequence of Unicode characters, and a writer is an output character stream that writes a sequence of Unicode characters. Character encodings are used by readers and writers to convert between external encoding and internal Unicode characters. Table 11.4 and Table 11.5 give an overview of some selected character streams found in the java.io
package.
Readers use the following methods for reading Unicode characters:
int read() throws IOException
int read(char cbuf[]) throws IOException
int read(char cbuf[], int off, int len) throws IOException
Note that the read()
methods read the character as an int
in the range 0 to 65535 (0x0000–0xFFFF). The value –1 is returned if the end of the stream has been reached.
long skip(long n) throws IOException
A reader can skip over characters using the skip()
method.
Writers use the following methods for writing Unicode characters:
void write(int c) throws IOException
The write()
method takes an int
as argument, but writes only the least significant 16 bits.
void write(char[] cbuf) throws IOException
void write(String str) throws IOException
void write(char[] cbuf, int off, int length) throws IOException
void write(String str, int off, int length) throws IOException
These methods write the characters from an array of characters or a string.
void close() throws IOException
void flush() throws IOException
Like byte streams, a character stream should be closed when no longer needed to free system resources. Closing a character output stream automatically flushes the stream. A character output stream can also be manually flushed.
Like byte streams, many methods of the character stream classes throw an IOException
that a calling method must either catch explicitly or specify in a throws
clause.
The capabilities of the OutputStreamWriter
and the InputStreamReader
classes are limited, as they primarily write and read characters.
In order to write a text representation of Java primitive values and objects, a PrintWriter
should be chained to either a writer, a byte output stream, a File
, or a String
file name, using one of the following constructors:
PrintWriter(Writer out)
PrintWriter(Writer out, boolean autoFlush)
PrintWriter(OutputStream out)
PrintWriter(OutputStream out, boolean autoFlush)
PrintWriter(File file)
PrintWriter(File file, String charsetName)
PrintWriter(String fileName)
PrintWriter(String fileName, String charsetName)
The autoFlush
argument specifies whether the PrintWriter
should be flushed when any println()
method of the PrintWriter
class is called.
When the underlying writer is specified, the character encoding supplied by the underlying writer is used. However, an OutputStream
has no notion of any character encoding, so the necessary intermediate OutputStreamWriter
is automatically created, which will convert characters into bytes, using the default character encoding.
When supplying the File
object or the file name, the character encoding can be specified explicitly.
The PrintWriter
class provides the following methods for writing text representation of Java primitive values and objects (Table 11.6):
The println()
methods write the text representation of their argument to the underlying stream, and then append a line-separator. The println()
methods use the correct platform-dependent line-separator. For example, on Unix platforms the line-separator is '
'
(newline), while on Windows platforms it is "
"
(carriage return + newline) and on the Macintosh it is '
'
(carriage return).
The print()
methods create a text representation of an object by calling the toString()
method on the object. The print()
methods do not throw any IOException
. Instead, the checkError()
method of the PrintWriter
class must be called to check for errors.
In addition, the PrintWriter
class provides the format()
method and the convenient printf()
method to write formatted values. Details on formatting values can be found in Section 12.7, p. 593.
When writing text to a file using the default character encoding, 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 11.4a):
1. Create a FileOutputStream
:
FileOutputStream outputFile = new FileOutputStream("info.txt");
2. Create an OutputStreamWriter
which is chained to the FileOutputStream
:
OutputStreamWriter outputStream = new OutputStreamWriter(outputFile);
The OutputStreamWriter
uses the default character encoding for writing the characters to the file.
3. Create a PrintWriter
which is chained to the OutputStreamWriter
:
PrintWriter printWriter1 = new PrintWriter(outputStream, true);
Setting up a PrintWriter
based on a FileOutputStream
(Figure 11.4b):
1. Create a FileOutputStream
:
FileOutputStream outputFile = new FileOutputStream("info.txt");
2. Create a PrintWriter
which is chained to the FileOutputStream
:
PrintWriter printWriter2 = new PrintWriter(outputFile, true);
The intermediate OutputStreamWriter
to convert the characters using the default encoding is automatically supplied.
Setting up a PrintWriter
based on a FileWriter
(Figure 11.4c):
1. Create a FileWriter
which is a subclass of OutputStreamWriter
:
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 11.4a.
2. Create a PrintWriter
which is chained to the FileWriter
:
PrintWriter printWriter3 = new PrintWriter(fileWriter, true);
Setting up a PrintWriter
, given the file name (Figure 11.4d):
1. Create a PrintWriter
, supplying the file name:
PrintWriter printWriter3 = new PrintWriter("info.txt");
The underlying OutputStreamWriter
is created to write the characters to the file in the default encoding, as shown in Figure 11.4d. In this case, there is no automatic flushing.
If a specific character encoding is desired for the writer, the first procedure (Figure 11.4a) can be used, the encoding being specified for the OutputStreamWriter
:
FileOutputStream outputFile = new FileOutputStream("info.txt");
OutputStreamWriter outputStream = new OutputStreamWriter(outputFile, "8859_1");
PrintWriter printWriter4 = new PrintWriter(outputStream, true);
This writer will use the 8859_1 character encoding to write the characters to the file. Alternatively, we can use one of the two PrintWriter
constructors that accept a character encoding:
PrintWriter printWriter5 = new PrintWriter("info.txt", "8859_1");
A BufferedWriter
can be also used to improve the efficiency of writing characters to the underlying stream (explained later in this subsection).
Java primitive values and objects cannot be read directly from their text representation. Characters must be read and converted to the relevant values explicitly. One common strategy is to write lines of text and tokenize the characters as they are read, a line at a time (see the subsection The java.util.Scanner
Class, p. 571). Such files are usually called text files.
When reading characters from a file using the default character encoding, the following two procedures for setting up an InputStreamReader
can be used.
Setting up an InputStreamReader
which is chained to a FileInputStream
(Figure 11.5a):
1. Create a FileInputStream
:
FileInputStream inputFile = new FileInputStream("info.txt");
2. Create an InputStreamReader
which is chained to the FileInputStream
:
InputStreamReader reader = new InputStreamReader(inputFile);
The InputStreamReader
uses the default character encoding for reading the characters from the file.
Setting up a FileReader
which is a subclass of InputStreamReader
(Figure 11.5b):
1. Create a FileReader
:
FileReader fileReader = new FileReader("info.txt");
This is equivalent to having an InputStreamReader
chained to a FileInputStream
for reading the characters from the file, using the default character encoding. Other constructors of the FileReader
class accept a File
or a FileDescriptor
.
If a specific character encoding is desired for the reader, the first procedure must be used (Figure 11.5a), the encoding being specified for the InputStreamReader
:
FileInputStream inputFile = new FileInputStream("info.txt");
InputStreamReader reader = new InputStreamReader(inputFile, "8859_1");
This reader will use the 8859_1 character encoding to read the characters from the file. A BufferedReader
can also be used to improve the efficiency of reading characters from the underlying stream, as explained later in this section.
A BufferedWriter
can be chained to the underlying writer by using one of the following constructors:
BufferedWriter(Writer out)
BufferedWriter(Writer out, int size)
The default buffer size is used, unless the buffer size is explicitly specified.
Characters, strings, and arrays of characters can be written using the methods for a Writer
, but these now use buffering to provide efficient writing of characters. In addition, the BufferedWriter
class provides the method newLine()
for writing the platform-dependent line-separator.
The following code creates a PrintWriter
whose output is buffered and the characters are written using the 8859_1 character encoding (Figure 11.6a):
FileOutputStream outputFile = new FileOutputStream("info.txt");
OutputStreamWriter outputStream = new OutputStreamWriter(outputFile, "8859_1");
BufferedWriter bufferedWriter1 = new BufferedWriter(outputStream);
PrintWriter printWriter1 = new PrintWriter(bufferedWriter1, true);
The following code creates a PrintWriter
whose output is buffered, and the characters are written using the default character encoding (Figure 11.6b):
FileWriter fileWriter = new FileWriter("info.txt");
BufferedWriter bufferedWriter2 = new BufferedWriter(fileWriter);
PrintWriter printWriter2 = new PrintWriter(bufferedWriter2, true);
Note that in both cases, the PrintWriter
is used to write the characters. The Buffered Writer
is sandwiched between the PrintWriter
and the underlying OutputStream Writer
.
A BufferedReader
can be chained to the underlying reader by using one of the following constructors:
BufferedReader(Reader in)
BufferedReader(Reader in, int size)
The default buffer size is used, unless the buffer size is explicitly specified.
In addition to the methods of the Reader
class, the BufferedReader
class provides the method readLine()
to read a line of text from the underlying reader:
String readLine() throws IOException
The null
value is returned when the end of the stream is reached. The returned string must explicitly be converted to other values.
The following code creates a BufferedReader
that can be used to read text lines from a file, using the 8859_1 character encoding (Figure 11.7a):
FileInputStream inputFile = new FileInputStream("info.txt");
InputStreamReader reader = new InputStreamReader(inputFile, "8859_1");
BufferedReader bufferedReader1 = new BufferedReader(reader);
The following code creates a BufferedReader
that can be used to read text lines from a file, using the default character encoding (Figure 11.7b):
FileReader fileReader = new FileReader("lines.txt");
BufferedReader bufferedReader2 = new BufferedReader(fileReader);
Note that in both cases the BufferedReader
object is used to read the text lines.
In contrast to Example 11.3, which demonstrated the reading and writing of binary representations of primitive data values, Example 11.4 illustrates the reading and writing of text representations of primitive data values.
The CharEncodingDemo
class in Example 11.4 writes text representations of Java primitive values, using the 8859_1 character encoding (Figure 11.6a). The PrintWriter
is buffered. Its underlying writer uses the specified encoding, as shown at (1). Values are written out with the text representation of one value on each line, as shown at (2), and the writer is closed, as shown at (3). The example uses the same character encoding to read the text file. A BufferedReader
is created (Figure 11.7a). Its underlying reader uses the specified encoding, as shown at (4). The text representation of the values is read in the same order the values were written out, one value per line. The characters in the line are explicitly converted to an appropriate type of value, as shown at (5). An alternate approach to extracting values from a text line is to use a scanner (p. 571).
We check for the end of the stream at (6), which is signalled by the null
value returned by the readLine()
method of the BufferedReader
class. The BufferedReader
is closed, as shown at (7), and the values are echoed on the standard output stream, as shown at (8). Note the exceptions that are specified in the throws
clause of the main()
method.
Example 11.4 Demonstrating Readers and Writers, and Character Encoding
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
public class CharEncodingDemo {
public static void main(String[] args)
throws IOException, NumberFormatException {
// Character encoding. (1)
FileOutputStream outputFile = new FileOutputStream("info.txt");
OutputStreamWriter writer = new OutputStreamWriter(outputFile, "8859_1");
BufferedWriter bufferedWriter1 = new BufferedWriter(writer);
PrintWriter printWriter = new PrintWriter(bufferedWriter1, true);
System.out.println("Writing using encoding: " + writer.getEncoding());
// Print Java primitive values, one on each line. (2)
printWriter.println(true);
printWriter.println('A'),
printWriter.println(Byte.MAX_VALUE);
printWriter.println(Short.MIN_VALUE);
printWriter.println(Integer.MAX_VALUE);
printWriter.println(Long.MIN_VALUE);
printWriter.println(Float.MAX_VALUE);
printWriter.println(Math.PI);
// Close the writer, which also closes the underlying stream (3)
printWriter.flush();
printWriter.close();
// Create a BufferedReader which uses 8859_1 character encoding (4)
FileInputStream inputFile = new FileInputStream("info.txt");
InputStreamReader reader = new InputStreamReader(inputFile, "8859_1");
BufferedReader bufferedReader = new BufferedReader(reader);
System.out.println("Reading using encoding: " + reader.getEncoding());
// Read the (exact number of) Java primitive values (5)
// in the same order they were written out, one on each line
boolean v = bufferedReader.readLine().equals("true")? true : false;
char c = bufferedReader.readLine().charAt(0);
byte b = (byte) Integer.parseInt(bufferedReader.readLine());
short s = (short) Integer.parseInt(bufferedReader.readLine());
int i = Integer.parseInt(bufferedReader.readLine());
long l = Long.parseLong(bufferedReader.readLine());
float f = Float.parseFloat(bufferedReader.readLine());
double d = Double.parseDouble(bufferedReader.readLine());
// Check for end of stream: (6)
String line = bufferedReader.readLine();
if (line != null ) {
System.out.println("More input: " + line);
} else {
System.out.println("End of stream");
}
// Close the reader, which also closes the underlying stream (7)
bufferedReader.close();
// Write the values read on the terminal (8)
System.out.println("Values read:");
System.out.println(v);
System.out.println(c);
System.out.println(b);
System.out.println(s);
System.out.println(i);
System.out.println(l);
System.out.println(f);
System.out.println(d);
}
}
Output from the program:
Writing using encoding: ISO8859_1
Reading using encoding: ISO8859_1
End of stream
Values read:
true
A
127
-32768
2147483647
-9223372036854775808
3.4028235E38
3.141592653589793
The standard output stream (usually the display) is represented by the PrintStream
object System.out
. The standard input stream (usually the keyboard) is represented by the InputStream
object System.in
. In other words, it is a byte input stream. The standard error stream (also usually the display) is represented by System.err
which is another object of the PrintStream
class. The PrintStream
class offers print()
methods which act as corresponding print()
methods from the PrintWriter
class. These methods can be used to write output to System.out
and System.err
. In other words, both System.out
and System.err
act like PrintWriter
, but in addition they have write()
methods for writing bytes.
In order to read characters typed by the user, the Console
class is recommended (see the next section).
It is instructive to see which byte streams correspond to which character streams. Table 11.7 shows the correspondence between byte and character streams. Note that not all classes have a corresponding counterpart.
A console is a unique character-based device associated with a JVM. Whether a JVM has a console depends on the platform, and also on the manner in which the JVM is invoked. When the JVM is started from a command line, and the standard input and output streams have not been redirected, the console will normally correspond to the keyboard and the display (Figure 11.8). In any case, the console will be represented by an instance of the class Console
. This Console
instance is obtained by calling the static method console()
of the System
class. If there is no console associated with the JVM, the null
value is returned by this method.
// Obtaining the console:
Console console = System.console();
if (console == null) {
System.err.println("No console available.");
return;
}
// Continue ...
For creating dialog for console-based applications, the Console
class provides the following functionality:
• Prompt and read a line of character-based response.
String username = console.readLine("Enter the user name (%d chars): ", 4);
The readLine()
method first prints the formatted prompt on the console, and then returns the characters typed at the console when the line is terminated by the ENTER key.
• Prompt and read passwords without echoing the characters on the console.
char[] password;
do {
password = console.readPassword("Enter password (min. %d chars): ", 6);
} while (password.length < 6);
The readPassword()
method first prints the formatted prompt, and returns the password characters typed by the user in an array of char
when the line is terminated by the ENTER key. The password characters are not echoed on the display.
Since a password is sensitive data, the recommended practice is to have it stored in memory only as long as it is necessary and to zero-fill the char
array as soon as possible in order to overwrite the password characters.
• Print formatted strings to the console.
The Console
class provides the format()
and the printf()
methods for this purpose. Using these methods and creating formatted strings are covered in Section 12.7, p. 593.
Note that the console only returns character-based input. For reading other types of values from the standard input stream, the Scanner
class (p. 571) can be considered.
The Console
class provides methods for formatted prompting and reading from the console, and obtaining the reader associated with it.
The first method reads a single line of text from the console. The second method prints a formatted prompt first, then reads a single line of text from the console. The prompt is constructed by formatting the specified args
according to the specified format
.
char[] readPassword()
char[] readPassword(String format, Object... args)
The first method reads a password or a password phrase from the console with echoing disabled. The second method does the same, but first prints a formatted prompt.
Reader reader()
This retrieves the unique Reader
object associated with this console.
The Console
class provides the following methods for writing formatted strings to the console, and obtaining the writer associated with it:
Console format(String format, Object... args)
Console printf(String format, Object... args)
These methods write a formatted string to this console’s output stream using the specified format string and arguments, according to the default locale.
PrintWriter writer()
The method retrieves the unique PrintWriter
object associated with this console.
void flush()
This method flushes the console and forces any buffered output to be written immediately.
Example 11.5 illustrates a typical use of the Console
class to change the password of a user. A password file can first be generated by running the program in the class MakePasswordFile
. Each user name (String
) and the hash value (int
) of the corresponding password are stored on a single line in a text file, separated by a space. At the start of the program, this information is read into a map declared at (1) by the method readPWStore()
declared at (9). This method uses a BufferedReader
chained to a FileReader
to read the lines in the text file. The login name and the password hash values are extracted using a Scanner
and put into the password map.
At the end of the program, the updated password map is written back to the file at (6) by the method writePWStore()
declared at (10). This method uses a PrintWriter
chained to a FileWriter
to write the information. It uses the printf()
method of the PrintWriter
to format the login/password information on each line that is written to the file.
The console is obtained at (2). The login name and the current password are read at (4) by calling the readLine()
and the readPassword()
methods, respectively:
...
login = console.readLine("Enter your login: ");
oldPassword = console.readPassword("Enter your current password: ");
...
User verification is done by the verifyPassword()
method at (7). The char
array is first converted to a string by calling the String.copyValueOf()
method. The hash value of this string is compared with the hash code of the password for the user looked up in the password map.
The code at (5) implements the procedure for changing the password. The user is asked to submit the new password, and then asked to confirm it. Note the password characters are not echoed. The respective char
arrays returned with this input are compared for equality by the static method equals()
in the java.util.Arrays
class, that compares two arrays. The password is changed by the changePassword()
method at (8). This puts a new entry in the password map, whose value is the hash value of the new password.
The char
arrays with the passwords are zero-filled by calling the Arrays.fill()
method when they are no longer needed.
Example 11.5 Changing Passwords
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
/** Class to create a password file */
public final class MakePasswordFile {
public static void main (String[] args) throws IOException {
Map<String, Integer> pwStore = new TreeMap<String, Integer>();
pwStore.put("tom", "123".hashCode());
pwStore.put("dick", "456".hashCode());
pwStore.put("harry", "789".hashCode());
PrintWriter destination = new PrintWriter(new FileWriter("pws.txt"));
Set<Map.Entry<String, Integer>> pwSet = pwStore.entrySet();
for (Map.Entry<String, Integer> entry : pwSet) {
// Format: login password
destination.printf("%s %s%n", entry.getKey(), entry.getValue());
}
destination.flush();
destination.close();
}
}
import java.io.BufferedReader;
import java.io.Console;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.Map;
import java.util.Scanner;
import java.util.Set;
import java.util.TreeMap;
/** Class to change the password of a user */
public class ChangePassword {
// Map for storing login/password info. (1)
private static Map<String, Integer> pwStore;
public static void main (String[] args) throws IOException {
// Obtain the console: (2)
Console console = System.console();
if (console == null) {
System.err.println("No console available.");
return;
}
// Read the login/password info from a file: (3)
readPWStore();
// Verify user: (4)
String login;
char[] oldPassword;
do {
login = console.readLine("Enter your login: ");
oldPassword = console.readPassword("Enter your current password: ");
} while (login.length() == 0 || oldPassword.length == 0 ||
!verifyPassword(login, oldPassword));
Arrays.fill(oldPassword, '0'),
// Changing the password: (5)
boolean noMatch = false;
do {
// Read the new password and its confirmation:
char[] newPasswordSelected
= console.readPassword("Enter your new password: ");
char[] newPasswordConfirmed
= console.readPassword("Confirm your new password: ");
// Compare the supplied passwords:
noMatch = newPasswordSelected.length == 0 ||
newPasswordConfirmed.length == 0 ||
!Arrays.equals(newPasswordSelected, newPasswordConfirmed);
if (noMatch) {
console.format("Passwords don't match. Please try again.%n");
} else {
changePassword(login, newPasswordSelected);
console.format("Password changed for %s.%n", login);
}
// Zero-fill the password arrays:
Arrays.fill(newPasswordSelected, '0'),
Arrays.fill(newPasswordConfirmed, '0'),
} while (noMatch);
// Save the login/password info to a file: (6)
writePWStore();
}
/** Verifies the password. */ // (7)
private static boolean verifyPassword(String login, char[] password) {
Integer suppliedPassword = String.copyValueOf(password).hashCode();
Integer storedPassword = pwStore.get(login);
return storedPassword != null && storedPassword.equals(suppliedPassword);
}
/** Changes the password for the user. */ // (8)
private static void changePassword(String login, char[] password) {
Integer newPassword = String.copyValueOf(password).hashCode();
pwStore.put(login, newPassword);
}
/** Reads login/password from a file */ // (9)
private static void readPWStore() throws IOException {
pwStore = new TreeMap<String, Integer>();
BufferedReader source = new BufferedReader(new FileReader("pws.txt"));
while (true) {
String txtLine = source.readLine();
if (txtLine == null) break; // EOF?
Scanner scanner = new Scanner(txtLine);
// Format: <login string> <password int hash value>
String login = scanner.next();
Integer password = scanner.nextInt();
pwStore.put(login, password);
}
source.close();
}
/** Writes login/password to a file */ // (10)
private static void writePWStore() throws IOException {
PrintWriter destination = new PrintWriter(new FileWriter("pws.txt"));
Set<Map.Entry<String, Integer>> pwSet = pwStore.entrySet();
for (Map.Entry<String, Integer> entry : pwSet) {
// Format: <login string> <password int hash value>
destination.printf("%s %s%n", entry.getKey(), entry.getValue());
}
destination.close();
}
}
Running the program:
>java ChangePassword
Enter your login: tom
Enter your current password:
Enter your new password:
Confirm your new password:
Password changed for tom
11.13 Which of these are valid parameter types for the write()
methods of the Writer
class?
Select the three correct answers.
(a) String
(b) char
(c) char[]
(d) int
11.14 What is the default encoding for an OutputStreamWriter
?
Select the one correct answer.
(a) 8859_1
(b) UTF8
(c) Unicode
(d) The default is system-dependent.
(e) The default is not system-dependent, but is none of the above.
11.15 Which of these integer types do not have their own print()
method in the PrintWriter
class?
Select the one correct answer.
(a) byte
(b) char
(c) int
(d) long
(e) All have their own print()
method.
11.16 How can one access the standard error stream?
Select the one correct answer.
(a) It is accessed via a member of the System.err
class.
(b) It is accessed via the static variable named out
in the System
class.
(c) It is accessed via the static variable named err
in the System
class.
(d) It is accessed via the static variable named err
in the Runtime
class.
(e) It is returned by a method in the System
class.
11.17 How can we programmatically guarantee that a call to a print()
method of the PrintWriter
class was successful or not?
Select the one correct answer.
(a) Check if the return value from the call is -1
.
(b) Check if the return value from the call is null
.
(c) Catch the IOException
that is thrown when an I/O error occurs.
(d) Call the checkError()
method of the PrinterWriter
class immediately after the print()
method call returns to see if an IOException
was thrown.
11.18 Given the following program:
import java.io.EOFException;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
public class MoreEndings {
public static void main(String[] args) {
try {
FileInputStream fis = new FileInputStream("seq.txt");
InputStreamReader isr = new InputStreamReader(fis);
int i = isr.read();
while (i != -1) {
System.out.print((char)i + "|");
i = isr.read();
}
} catch (FileNotFoundException fnf) {
System.out.println("File not found");
} catch (EOFException eofe) {
System.out.println("End of stream");
} catch (IOException ioe) {
System.out.println("Input error");
}
}
}
Assume that the file "seq.txt"
exists in the current directory, has the required access permissions, and contains the string "Hello"
.
Which statement about the program is true?
Select the one correct answer.
(a) The program will not compile because a certain unchecked exception is not caught.
(b) The program will compile and print H|e|l|l|o|Input error
.
(c) The program will compile and print H|e|l|l|o|End of stream
.
(d) The program will compile, print H|e|l|l|o|
, and then terminate normally.
(e) The program will compile, print H|e|l|l|o|
, and then block in order to read from the file.
(f) The program will compile, print H|e|l|l|o|
, and terminate because of an uncaught exception.
11.19 Which code, when inserted at (1), will result in the program compiling and running without errors?
import java.io.*;
public class MakeLines {
public static void main(String[] args) {
try {
String fileName = "greetings.txt";
// (1) INSERT CODE HERE ...
writeGreetings(stream);
stream.close();
} catch (IOException ioe) {
System.out.println("I/O error");
}
}
private static void writeGreetings(Writer writer) {
try {
BufferedWriter bw = new BufferedWriter(writer);
bw.write("Hello");
bw.newLine();
bw.write("Howdy");
bw.newLine();
bw.flush();
} catch (IOException ioe) {
System.out.println("I/O error");
}
}
}
Select the three correct answers.
(a) FileOutputStream fos = new FileOutputStream(fileName);
(b) OutputStreamWriter stream = new OutputStreamWriter(fos);
(c) FileOutputStream fos = new FileOutputStream(fileName);
(d) InputStreamWriter stream = new InputStreamWriter(fos);
(e) FileOutputStream stream = new FileOutputStream(fileName);
(f) PrintWriter stream = new PrintWriter(fileName);
(g) FileWriter stream = new FileWriter(fileName);
11.20 Given the following program:
import java.io.BufferedReader;
import java.io.EOFException;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
public class NoEndings {
public static void main(String[] args) {
try {
FileReader fr = new FileReader("greetings.txt");
BufferedReader br = new BufferedReader(fr);
System.out.print(br.readLine() + "|");
System.out.print(br.readLine() + "|");
System.out.print(br.readLine() + "|");
} catch (EOFException eofe) {
System.out.println("End of stream");
} catch (IOException ioe) {
System.out.println("Input error");
}
}
}
Assume that the file "greeting.txt"
exists in the current directory, has the required access permissions, and contains the following two lines of text:
Hello
Howdy
Which statement is true about the program?
Select the one correct answer.
(a) The program will not compile because the FileNotFoundException
is not caught.
(b) The program will compile, print Hello|Howdy|null|
, and then terminate normally.
(c) The program will compile and print Hello|Howdy|Input error
.
(d) The program will compile and print Hello|Howdy|End of stream
.
(e) The program will compile, print Hello|Howdy|
, and then block in order to read from the file.
(f) The program will compile, print Hello|Howdy|
, and terminate because of an uncaught exception.
11.21 Given the following program:
import java.io.Console;
public class ConsoleInput {
public static void main(String[] args){
Console console = System.console();
if (console == null) {
System.err.println("No console available.");
return;
}
String username = console.readLine("Enter user name (%d chars): ", 4);
char[] password = console.readPassword("Enter password (%d chars): ", 4);
System.out.println("Username: " + username);
System.out.println("Password: " + String.valueOf(password));
}
}
Assume that the user types the strings "java dude"
and "fort knox"
when prompted for the user name and the password, respectively.
Which statement about the program is true?
Select the one correct answer.
(a) The program will print:
Username: java
Password: fort knox
(b) The program will print:
Username: java dude
Password: fort
(c) The program will print:
Username: java dude
Password: fort knox
(d) The program will print:
Username: java
Password: fort
Object serialization allows an object to be transformed into a sequence of bytes that can later be re-created (deserialized) into the original object. After deserialization, the object has the same state as it had when it was serialized, barring any data members that were not serializable. This mechanism is generally known as persistence. Java provides this facility through the ObjectInput
and ObjectOutput
interfaces, which allow the reading and writing of objects from and to streams. These two interfaces extend the DataInput
and DataOutput
interfaces, respectively (see Figure 11.1, p. 476).
The ObjectOutputStream
class and the ObjectInputStream
class implement the ObjectOutput
interface and the ObjectInput
interface, respectively, providing methods to write and read binary representation of objects as well as Java primitive values. Figure 11.9 gives an overview of how these classes can be chained to underlying streams and some selected methods they provide. The figure does not show the methods inherited from the abstract OutputStream
and InputStream
superclasses.
The read and write methods in the two classes can throw an IOException
, and the read methods will throw an EOFException
if the end of the stream has been reached.
The class ObjectOutputStream
can write objects to any stream that is a subclass of the OutputStream
, e.g., to a file or a network connection (socket). An Object OutputStream
must be chained to an OutputStream
using the following constructor:
For example, in order to store objects in a file and thus provide persistent storage for objects, an ObjectOutputStream
can be chained to a FileOutputStream
:
FileOutputStream outputFile = new FileOutputStream("obj-storage.dat");
ObjectOutputStream outputStream = new ObjectOutputStream(outputFile);
Objects can be written to the stream using the writeObject()
method of the ObjectOutputStream
class:
The writeObject()
method can be used to write any object to a stream, including strings and arrays, as long as the object implements the java.io.Serializable
interface, which is a marker interface with no methods. The String
class, the primitive wrapper classes and all array types implement the Serializable
interface. A serializable object can be any compound object containing references to other objects, and all constituent objects that are serializable are serialized recursively when the compound object is written out. This is true even if there are cyclic references between the objects. Each object is written out only once during serialization. The following information is included when an object is serialized:
• the class information needed to reconstruct the object.
• the values of all serializable non-transient and non-static members, including those that are inherited.
An exception of the type java.io.NotSerializableException
is thrown if a non-serializable object is encountered during the serialization process. Note also that objects of subclasses that extend a serializable class are always serializable.
An ObjectInputStream
is used to restore (deserialize) objects that have previously been serialized using an ObjectOutputStream
. An ObjectInputStream
must be chained to an InputStream
, using the following constructor:
For example, in order to restore objects from a file, an ObjectInputStream
can be chained to a FileInputStream
:
FileInputStream inputFile = new FileInputStream("obj-storage.dat");
ObjectInputStream inputStream = new ObjectInputStream(inputFile);
The method readObject()
of the ObjectInputStream
class is used to read an object from the stream:
Note that the reference returned is of type Object
regardless of the actual type of the retrieved object, and can be cast to the desired type. Objects and values must be read in the same order as when they were serialized.
Serializable, non-transient data members of an object, including those data members that are inherited, are restored to the values they had at the time of serialization. For compound objects containing references to other objects, the constituent objects are read to re-create the whole object structure. In order to deserialize objects, the appropriate classes must be available at runtime. Note that new objects are created during deserialization, so that no existing objects are overwritten.
The class ObjectSerializationDemo
in Example 11.6 serializes some objects in the writeData()
method at (1), and then deserializes them in the readData()
method at(2). The readData()
method also writes the data to the standard output stream.
The writeData()
method writes the following values to the output stream: an array of strings (strArray
), a long
value (num
), an array of int
values (intArray
), and lastly a String
object (commonStr
) which is shared with the array of strings, strArray
. However, this shared String
object is actually only serialized once. Duplication is automatically avoided when the same object is serialized several times. Note that the array elements and the characters in a String
object are not written out explicitly one by one. It is enough to pass the object reference in the writeObject()
method call. The method also recursively goes through the array of strings, strArray
, serializing each String
object in the array.
The method readData()
deserializes the data in the order in which it was written. An explicit cast is needed to convert the reference of a deserialized object to a subtype. Note that new objects are created by the readObject()
method, and that an object created during the deserialization process has the same state as the object that was serialized.
Example 11.6 Object Serialization
//Reading and Writing Objects
import java.io.EOFException;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Arrays;
public class ObjectSerializationDemo {
void writeData() { // (1)
try {
// Set up the output stream:
FileOutputStream outputFile = new FileOutputStream("obj-storage.dat");
ObjectOutputStream outputStream = new ObjectOutputStream(outputFile);
// Write data:
String[] strArray = {"Seven", "Eight", "Six"};
long num = 2008;
int[] intArray = {1, 3, 1949};
String commonStr = strArray[2]; // "Six"
outputStream.writeObject(strArray);
outputStream.writeLong(num);
outputStream.writeObject(intArray);
outputStream.writeObject(commonStr);
// Flush and close the output stream:
outputStream.flush();
outputStream.close();
} catch (FileNotFoundException e) {
System.err.println("File not found: " + e);
} catch (IOException e) {
System.err.println("Write error: " + e);
}
}
void readData() { // (2)
try {
// Set up the input stream:
FileInputStream inputFile = new FileInputStream("obj-storage.dat");
ObjectInputStream inputStream = new ObjectInputStream(inputFile);
// Read the data:
String[] strArray = (String[]) inputStream.readObject();
long num = inputStream.readLong();
int[] intArray = (int[]) inputStream.readObject();
String commonStr = (String) inputStream.readObject();
// Write data to the standard output stream:
System.out.println(Arrays.toString(strArray));
System.out.println(Arrays.toString(intArray));
System.out.println(commonStr);
// Close the stream:
inputStream.close();
} catch (FileNotFoundException e) {
System.err.println("File not found: " + e);
} catch (EOFException e) {
System.err.println("End of stream: " + e);
} catch (IOException e) {
System.err.println("Read error: " + e);
} catch (ClassNotFoundException e) {
System.err.println("Class not found: " + e);
}
}
public static void main(String[] args) {
ObjectSerializationDemo demo = new ObjectSerializationDemo();
demo.writeData();
demo.readData();
}
}
Output from the program:
[Seven, Eight, Six]
[1, 3, 1949]
Six
Example 11.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 SerialClient
provides two methods, writeData()
and readData()
, declared at (4) and (5), respectively. The writeData()
method serializes a unicycle with a wheel of size 65 to a file. The readData()
method deserializes the bytes on the file. 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.
If we run the program with the following declarations for the Wheel
and the Unicycle
classes, where both classes are serializable:
class Wheel implements Serializable { // (1)
private int wheelSize;
...
}
class Unicycle implements Serializable { // (2)
private Wheel wheel; // (3)
...
}
we get the following output, showing that both serialization and deserialization were successful:
Before writing: Unicycle with wheel size: 65
After reading: Unicycle with wheel size: 65
Example 11.7 Non-Serializable Objects
import java.io.Serializable;
//public class Wheel implements Serializable { // (1)
public class Wheel { // (1a)
private int wheelSize;
Wheel(int ws) { wheelSize = ws; }
public String toString() { return "wheel size: " + wheelSize; }
}
____________________________________________________
import java.io.Serializable;
public class Unicycle implements Serializable { // (2)
private Wheel wheel; // (3)
//transient private Wheel wheel; // (3a)
Unicycle (Wheel wheel) { this.wheel = wheel; }
public String toString() { return "Unicycle with " + wheel; }
}
____________________________________________________
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class SerialClient {
public static void main(String args[])
throws IOException, ClassNotFoundException {
SerialClient demo = new SerialClient();
demo.writeData();
demo.readData();
}
void writeData() throws IOException { // (4)
// Set up the output stream:
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);
// Close the stream:
outputStream.flush();
outputStream.close();
}
void readData() throws IOException, ClassNotFoundException { // (5)
// Set up the input streams:
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);
// Close the stream.
inputStream.close();
}
}
If we make the wheel
field of the Unicycle
class transient, (3a):
class Wheel implements Serializable { // (1)
private int wheelSize;
...
}
class Unicycle implements Serializable { // (2)
transient private Wheel wheel; // (3a)
...
}
we get the following output, showing that the wheel
field of the Unicycle
object was not serialized:
Before writing: Unicycle with wheel size: 65
After reading: Unicycle with null
As noted earlier, static fields are not serialized, as these are not part of the state of an object.
If the class Wheel
in Example 11.7 is not serializable, (1a):
class Wheel { // (1a)
private int wheelSize;
...
}
class Unicycle implements Serializable { // (2)
private Wheel wheel; // (3)
...
}
we get the following output when we run the program, i.e., a Unicycle
object cannot be serialized because its constituent Wheel
object is not serializable:
>java SerialClient
Before writing: Unicycle with wheel size: 65
Exception in thread "main" java.io.NotSerializableException: Wheel
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1156)
at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1509)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1474)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1392)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1150)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:326)
at SerialClient.writeData(SerialClient.java:25)
at SerialClient.main(SerialClient.java:12)
As we have seen, the class of the object must implement the Serializable
interface if we want the object to be serialized. If this object is a compound object, then all its constituent objects must also be serializable, and so on.
It is not always possible for a client to declare that a class is Serializable
. It might be declared final
, and therefore not extendable. The client might not have access to the code, or extending this class with a serializable subclass might not be an option. Java provides a customizable solution for serializing objects in such cases.
The basic idea behind the scheme is to use default serialization as much as possible, and provide “hooks” in the code for the serialization mechanism to call specific methods to deal with objects or values that should not or cannot be serialized by the default methods of the object streams.
Customizing serialization is illustrated in Example 11.8. The serializable class Unicycle
would like to use the Wheel
class, but this class is not serializable. If the wheel
field in the Unicycle
class is declared to be transient, it will be ignored by the default serialization procedure. This is not a viable option, as the unicycle will be missing the wheel size when a serialized unicycle is deserialized, as was illustrated in Example 11.7.
Any serializable object has the option of customizing its own serialization if it implements the following pair of methods:
private void writeObject(ObjectOutputStream) throws IOException;
private void readObject(ObjectInputStream)
throws IOException, ClassNotFoundException;
These methods are not part of any interface. Although private, these methods can be called by the JVM. The first method is called on the object when its serialization starts. The serialization procedure uses the reference value of the object passed in the ObjectOutputStream.writeObject()
method to call the first method on this object. The second method is called on the object created when the deserialization procedure is initiated by the call to the ObjectInputStream.readObject()
method.
Customizing serialization for objects of the class Unicycle
in Example 11.8 is achieved by the methods at (3b) and (3c). Note that the field wheel
is declared transient at (3a) and excluded by the normal serialization process.
In the method writeObject()
at (3b), the pertinent lines of code are the following:
oos.defaultWriteObject();
oos.writeInt(wheel.getWheelSize());
The call to the defaultWriteObject()
method of the ObjectOutputStream
does what its name implies: normal serialization of the current object. The second line of code does the customization: it writes the binary int
value of the wheel size to the ObjectOutputStream
. Code for customization can be called both before and after the call to the defaultWriteObject()
method, as long as the same order is used during deserialization.
In the method readObject()
at (3c), the pertinent lines of code are the following:
ois.defaultReadObject();
int wheelSize = ois.readInt();
wheel = new Wheel(wheelSize);
The call to the defaultReadObject()
method of the ObjectInputStream
does what its name implies: normal deserialization of the current object. The second line of code reads the binary int
value of the wheel size from the ObjectInputStream
. The third line of code creates a Wheel
object passing this value in the constructor call, and assigns its reference value to the wheel
field of the current object. Again, code for customization can be called both before and after the call to the defaultReadObject()
method, as long as it is in correspondence with the customization code in the writeObject()
method.
The client class SerialClient
in Example 11.8 is the same as the one in Example 11.7. The output from the program confirms that the object state prior to serialization is identical to the object state after deserialization.
Example 11.8 Customized Serialization
public class Wheel { // (1a)
private int wheelSize;
Wheel(int ws) { wheelSize = ws; }
int getWheelSize() { return wheelSize; }
public String toString() { return "wheel size: " + wheelSize; }
}
______________________________________________________
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
public class Unicycle implements Serializable { // (2)
transient private Wheel wheel; // (3a)
Unicycle(Wheel wheel) { this.wheel = wheel; }
public String toString() { return "Unicycle with " + wheel; }
private void writeObject(ObjectOutputStream oos) { // (3b)
try {
oos.defaultWriteObject();
oos.writeInt(wheel.getWheelSize());
} catch (IOException e) {
e.printStackTrace();
}
}
private void readObject(ObjectInputStream ois) { // (3c)
try {
ois.defaultReadObject();
int wheelSize = ois.readInt();
wheel = new Wheel(wheelSize);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
______________________________________________________
public class SerialClient { // Same as in Example 11.7 }
Output from the program:
Before writing: Unicycle with wheel size: 65
After reading: Unicycle with wheel size: 65
The inheritance hierarchy of an object also determines what its state will be after it is deserialized. An object will have the same state at deserialization as it had at the time it was serialized if all its superclasses are also serializable. This is because the normal object creation procedure using constructors is not run during deserialization (see Section 9.11, p. 416, on constructing the initial object state).
However, if any superclass of an object is not serializable, then the normal creation procedure using constructors is run, starting at the first non-serializable superclass, all the way up to the Object
class. This means that the state at deserialization might not be the same as at the time the object was serialized, because superconstructors run during deserialization may have initialized the object’s state.
Example 11.9 illustrates how inheritance affects serialization. The Student
class is a subclass of the Person
class. Whether the superclass Person
is serializable or not has implications for serializing objects of the Student
subclass, in particular, when their byte representation is deserialized.
The following code in the method writeData()
declared at (1) in the class Serial-Inheritance
serializes a Student
object:
Student student = new Student("Pendu", 1007);
System.out.println("Before writing: " + student);
outputStream.writeObject(student);
The corresponding code for deserialization is in the method readData()
declared at (2) in the class SerialInheritance
:
Student student = (Student) inputStream.readObject();
System.out.println("After reading: " + student);
We get the following output from the program in Example 11.9 when it is run with (1a) in the Person
class and the Student
class, i.e., when the superclass is serializable and so is the subclass, by virtue of inheritance. The results show that the object state prior to serialization is identical to the object state after deserialization. In this case, no superclass constructors were run during deserialization.
Before writing: Student state(Pendu, 1007)
After reading: Student state(Pendu, 1007)
However, this is not the case when the superclass Person
is not serializable. We get the following output from the program in Example 11.9 when it is run with (1b) in the Person
class and the Student
class, i.e. when only the subclass is serializable, but not the superclass. The output shows that the object state prior to serialization is not identical to the object state after deserialization.
Before writing: Student state(Pendu, 1007)
After reading: Student state(null, 1007)
During deserialization, the default constructor of the Person
superclass at (2) is run. As we can see from the declaration of the Person
class in Example 11.9, this default constructor does not initialize the name
field, which remains initialized with the default value for reference types, i.e., null
.
Example 11.9 Serialization and Inheritance
// A superclass
// public class Person implements Serializable { // (1a)
public class Person { // (1b)
private String name;
Person() {} // (2)
Person(String name) { this.name = name; }
public String getName() { return name; }
}
______________________________________________________
import java.io.Serializable;
//public class Student extends Person { // (1a)
public class Student extends Person implements Serializable { // (1b)
private long studNum;
Student(String name, long studNum) {
super(name);
this.studNum = studNum;
}
public String toString() {
return "Student state(" + getName() + ", " + studNum + ")";
}
}
______________________________________________________
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class SerialInheritance {
public static void main(String[] args)
throws IOException, ClassNotFoundException {
SerialInheritance demo = new SerialInheritance();
demo.writeData();
demo.readData();
}
void writeData() throws IOException { // (1)
// Set up the output stream:
FileOutputStream outputFile = new FileOutputStream("storage.dat");
ObjectOutputStream outputStream = new ObjectOutputStream(outputFile);
// Write the data:
Student student = new Student("Pendu", 1007);
System.out.println("Before writing: " + student);
outputStream.writeObject(student);
// Close the stream:
outputStream.flush();
outputStream.close();
}
void readData() throws IOException, ClassNotFoundException { // (2)
// Set up the input stream:
FileInputStream inputFile = new FileInputStream("storage.dat");
ObjectInputStream inputStream = new ObjectInputStream(inputFile);
// Read data.
Student student = (Student) inputStream.readObject();
// Write data on standard output stream.
System.out.println("After reading: " + student);
// Close the stream.
inputStream.close();
}
}
11.22 How many methods are defined in the Serializable
interface?
Select the one correct answer.
(a) None
(b) One
(c) Two
(d) Three
(e) None of the above.
11.23 Which of the following best describes the data written by an ObjectOutputStream
?
Select the one correct answer.
(a) Bytes and other Java primitive types.
(b) Object hierarchies.
(c) Object hierarchies and Java primitive types.
(d) Single objects.
(e) Single objects and Java primitive types.
11.24 Given the following code:
public class Person {
protected String name;
Person() { }
Person(String name) { this.name = name; }
}
____________________________________________________
import java.io.Serializable;
public class Student extends Person implements Serializable {
private long studNum;
Student(String name, long studNum) {
super(name);
this.studNum = studNum;
}
public String toString() { return "(" + name + ", " + studNum + ")"; }
}
____________________________________________________
import java.io.*;
public class RQ800_10 {
public static void main(String args[])
throws IOException, ClassNotFoundException {
FileOutputStream outputFile = new FileOutputStream("storage.dat");
ObjectOutputStream outputStream = new ObjectOutputStream(outputFile);
Student stud1 = new Student("Aesop", 100);
System.out.print(stud1);
outputStream.writeObject(stud1);
outputStream.flush();
outputStream.close();
FileInputStream inputFile = new FileInputStream("storage.dat");
ObjectInputStream inputStream = new ObjectInputStream(inputFile);
Student stud2 = (Student) inputStream.readObject();
System.out.println(stud2);
inputStream.close();
}
}
Which statement about the program is true?
Select the one correct answer.
(a) It fails to compile.
(b) It compiles, but throws an exception at runtime.
(c) It prints (Aesop, 100)(Aesop, 100)
.
(d) It prints (Aesop, 100)(null, 100)
.
(e) It prints (Aesop, 100)( , 100)
.
11.25 Given the following code:
public class Person {
protected String name;
Person() { this.name = "NoName"; }
Person(String name) { this.name = name; }
}
____________________________________________________
import java.io.Serializable;
public class Student extends Person implements Serializable {
private long studNum;
Student(String name, long studNum) {
super(name);
this.studNum = studNum;
}
public String toString() { return "(" + name + ", " + studNum + ")"; }
}
____________________________________________________
import java.io.*;
public class RQ800_20 {
public static void main(String args[])
throws IOException, ClassNotFoundException {
FileOutputStream outputFile = new FileOutputStream("storage.dat");
ObjectOutputStream outputStream = new ObjectOutputStream(outputFile);
Student stud1 = new Student("Aesop", 100);
System.out.print(stud1);
outputStream.writeObject(stud1);
outputStream.flush();
outputStream.close();
FileInputStream inputFile = new FileInputStream("storage.dat");
ObjectInputStream inputStream = new ObjectInputStream(inputFile);
Student stud2 = (Student) inputStream.readObject();
System.out.println(stud2);
inputStream.close();
}
}
Which statement about the program is true?
Select the one correct answer.
(a) It fails to compile.
(b) It compiles, but throws an exception at runtime.
(c) It prints (Aesop, 100)(Aesop, 100)
.
(d) It prints (Aesop, 100)(null, 100)
.
(e) It prints (Aesop, 100)(NoName, 100)
.
11.26 Given the following code:
import java.io.Serializable;
public class Person implements Serializable {
protected String name;
Person() { this.name = "NoName"; }
Person(String name) { this.name = name; }
}
____________________________________________________
public class Student extends Person {
private long studNum;
Student(String name, long studNum) {
super(name);
this.studNum = studNum;
}
public String toString() { return "(" + name + ", " + studNum + ")"; }
}
____________________________________________________
import java.io.*;
public class RQ800_30 {
public static void main(String args[])
throws IOException, ClassNotFoundException {
FileOutputStream outputFile = new FileOutputStream("storage.dat");
ObjectOutputStream outputStream = new ObjectOutputStream(outputFile);
Student stud1 = new Student("Aesop", 100);
System.out.print(stud1);
outputStream.writeObject(stud1);
outputStream.flush();
outputStream.close();
FileInputStream inputFile = new FileInputStream("storage.dat");
ObjectInputStream inputStream = new ObjectInputStream(inputFile);
Student stud2 = (Student) inputStream.readObject();
System.out.println(stud2);
inputStream.close();
}
}
Which statement about the program is true?
Select the one correct answer.
(a) It fails to compile.
(b) It compiles, but throws an exception at runtime.
(c) It prints (Aesop, 100)(Aesop, 100)
.
(d) It prints (Aesop, 100)(null, 100)
.
(e) It prints (Aesop, 100)(NoName, 100)
.
11.27 Given the following code:
import java.io.Serializable;
public class Person implements Serializable {
protected transient String name;
Person(String name) { this.name = name; }
}
____________________________________________________
public class Student extends Person {
private long studNum;
Student(String name, long studNum) {
super(name);
this.studNum = studNum;
}
public String toString() { return "(" + name + ", " + studNum + ")"; }
}
____________________________________________________
import java.io.*;
public class RQ800_40 {
public static void main(String args[])
throws IOException, ClassNotFoundException {
FileOutputStream outputFile = new FileOutputStream("storage.dat");
ObjectOutputStream outputStream = new ObjectOutputStream(outputFile);
Student stud1 = new Student("Aesop", 100);
System.out.print(stud1);
outputStream.writeObject(stud1);
outputStream.flush();
outputStream.close();
FileInputStream inputFile = new FileInputStream("storage.dat");
ObjectInputStream inputStream = new ObjectInputStream(inputFile);
Student stud2 = (Student) inputStream.readObject();
System.out.println(stud2);
inputStream.close();
}
}
Which statement about the program is true?
Select the one correct answer.
(a) It fails to compile.
(b) It compiles, but throws an exception at runtime.
(c) It prints (Aesop, 100)(Aesop, 100)
.
(d) It prints (Aesop, 100)(null, 100)
.
(e) It prints (Aesop, 100)(NoName, 100)
.
11.28 Given the following code:
import java.io.Serializable;
public class Person implements Serializable {
protected transient String name;
Person() { this.name = "NoName"; }
Person(String name) { this.name = name; }
}
____________________________________________________
import java.io.*;
public class Student extends Person {
private long studNum;
Student(String name, long studNum) {
super(name);
this.studNum = studNum;
}
public String toString() { return "(" + name + ", " + studNum + ")"; }
private void writeObject(ObjectOutputStream oos) throws IOException {
oos.defaultWriteObject();
oos.writeObject("NewName");
}
private void readObject(ObjectInputStream ois)
throws IOException, ClassNotFoundException {
ois.defaultReadObject();
name = (String) ois.readObject();
}
}
____________________________________________________
import java.io.*;
public class RQ800_50 {
public static void main(String args[])
throws IOException, ClassNotFoundException {
FileOutputStream outputFile = new FileOutputStream("storage.dat");
ObjectOutputStream outputStream = new ObjectOutputStream(outputFile);
Student stud1 = new Student("Aesop", 100);
System.out.print(stud1);
outputStream.writeObject(stud1);
outputStream.flush();
outputStream.close();
FileInputStream inputFile = new FileInputStream("storage.dat");
ObjectInputStream inputStream = new ObjectInputStream(inputFile);
Student stud2 = (Student) inputStream.readObject();
System.out.println(stud2);
inputStream.close();
}
}
Which statement about the program is true?
Select the one correct answer.
(a) It fails to compile.
(b) It compiles, but throws an exception at runtime.
(c) It prints (Aesop, 100)(Aesop, 100)
.
(d) It prints (Aesop, 100)(NewName, 100)
.
(e) It prints (Aesop, 100)(NoName, 100)
.
11.29 Given the following code:
public class Person {
protected transient String name;
Person() { this.name = "NoName"; }
Person(String name) { this.name = name; }
}
____________________________________________________
public class Student extends Person {
protected long studNum;
Student() { }
Student(String name, long studNum) {
super(name);
this.studNum = studNum;
}
}
____________________________________________________
import java.io.*;
public class GraduateStudent extends Student implements Serializable {
private int year;
GraduateStudent(String name, long studNum, int year) {
super(name, studNum);
this.year = year;
}
public String toString() {
return "(" + name + ", " + studNum + ", " + year + ")";
}
private void readObject(ObjectInputStream ois)
throws IOException, ClassNotFoundException {
ois.defaultReadObject();
name = "NewName";
studNum = 200;
year =2;
}
}
____________________________________________________
import java.io.*;
public class RQ800_70 {
public static void main(String args[])
throws IOException, ClassNotFoundException {
FileOutputStream outputFile = new FileOutputStream("storage.dat");
ObjectOutputStream outputStream = new ObjectOutputStream(outputFile);
GraduateStudent stud1 = new GraduateStudent("Aesop", 100, 1);
System.out.print(stud1);
outputStream.writeObject(stud1);
outputStream.flush();
outputStream.close();
FileInputStream inputFile = new FileInputStream("storage.dat");
ObjectInputStream inputStream = new ObjectInputStream(inputFile);
GraduateStudent stud2 = (GraduateStudent) inputStream.readObject();
System.out.println(stud2);
inputStream.close();
}
}
Which statement about the program is true?
Select the one correct answer.
(a) It fails to compile.
(b) It compiles, but throws an exception at runtime.
(c) It prints (Aesop, 100, 1)(Aesop, 100, 1)
.
(d) It prints (Aesop, 100, 1)(NewName, 0, 1)
.
(e) It prints (Aesop, 100, 1)(NewName, 200, 2)
.
11.30 Given the following code:
import java.io.Serializable;
public class Person implements Serializable {
protected transient String name;
Person(String name) { this.name = name; }
}
____________________________________________________
public class Student extends Person {
private static int numOfStudents;
private long studNum;
Student(String name, long studNum) {
super(name);
this.studNum = studNum;
++numOfStudents;
}
public String toString() {
return "(" + name + ", " + studNum + ", " + numOfStudents + ")";
}
}
_____________________________________________________
import java.io.*;
public class RQ800_80 {
public static void main(String args[])
throws IOException, ClassNotFoundException {
FileOutputStream outputFile = new FileOutputStream("storage.dat");
ObjectOutputStream outputStream = new ObjectOutputStream(outputFile);
Student stud1 = new Student("Aesop", 100);
System.out.print(stud1);
outputStream.writeObject(stud1);
outputStream.flush();
outputStream.close();
Student student = new Student("Mowgli", 300);
FileInputStream inputFile = new FileInputStream("storage.dat");
ObjectInputStream inputStream = new ObjectInputStream(inputFile);
Student stud2 = (Student) inputStream.readObject();
System.out.println(stud2);
inputStream.close();
}
}
Which statement about the program is true?
Select the one correct answer.
(a) It fails to compile.
(b) It compiles, but throws an exception at runtime.
(c) It prints (Aesop, 100, 1)(Aesop, 100, 1)
.
(d) It prints (Aesop, 100, 1)(null, 100, 2)
.
(e) It prints (Aesop, 100, 1)(null, 100, 1)
.
The following information was included in this chapter:
• Discussion of the File
class, which provides an interface to the host file system.
• Byte streams, as represented by the InputStream
and OutputStream
classes.
• File streams, as represented by the FileInputStream
and FileOutputStream
classes.
• Reading and writing binary files using the DataInputStream
and DataOutputStream
classes.
• Character streams, as represented by the Reader
and Writer
classes.
• Usage of character encodings, including Unicode and UTF8, by the InputStreamReader
and OutputStreamWriter
classes.
• Reading and writing text files.
• Buffered character streams, as represented by the BufferedReader
and Buffered Writer
classes.
• Standard input, output, and error streams represented by the fields System.in
, System.out
and System.err
, respectively.
• Object serialization: reading and writing objects.
11.1 Write a program that reads text from a source using one encoding, and writes the text to a destination using another encoding. The program should have four optional arguments:
• The first argument, if present, should specify the encoding of the source. The default source encoding should be "8859_1"
.
• The second argument, if present, should specify the encoding of the destination. The default destination encoding should be "UTF8"
.
• The third argument, if present, should specify a source file. If no argument is given, the standard input should be used.
• The fourth argument, if present, should specify a destination file. If no argument is given, the standard output should be used.
Use buffering, and read and write 512 bytes at a time to make the program efficient.
Errors should be written to the standard error stream.