JAVA CERTIFICATION EXAM OBJECTIVES
COVERED IN THIS CHAPTER:
3.2 Given a scenario involving navigating file systems, reading
from files, or writing to files, develop the correct solution
using the following classes (sometimes in combination) from
java.io: BufferedReader,BufferedWriter, File, FileReader,
FileWriter and PrintWriter.
3.3 Develop code that serializes and/or de-serializes objects
using the following APIs from java.io: DataInputStream,
DataOutputStream, FileInputStream, FileOutputStream,
ObjectInputStream, ObjectOutputStream, and Serializable.
In addition, develop Serializable classes that correctly
declare and use transient variables and private readObject
and writeObject methods. Given a scenario and/or code
example, recognize when, if, and which constructors will be
called in an object's inheritance chain during deserialization.
2. Text, UTF, and Unicode
• Programs may use UTF(UTF stands for UCS
Transformation Format, and UCS in turn stands for
Universal Character Set. to read and write Unicode.
• Programs may use readers and writers to convert
between internal Unicode and external8-bit encodings.
Ben Abdallah Helmi Architect
2
3. File Input and Output
• Java’s java.io.File and java.io.RandomAccessFile classes provide
functionality for navigating the local file system, describing files and
directories, and accessing files in non-sequential order. (Accessing
files sequentially is done with streams, readers, and writers, which are
described later in this chapter.) All file access begins with the creation
of an instance of one of these classes.
• File(String pathname);
• It is important to know that constructing an instance of File does not
create a file on the local file system. Calling the constructor simply
creates an instance that encapsulates the specified string.
Ben Abdallah Helmi Architect
3
4. There are two other versions of the File constructor:
File(String dir, String subpath);
File(File dir, String subpath);
1.File f1 =
2. new File(“/tmp”, “xyz”); // Assume /tmp is a dir
3. File f2 = new File(f1, “Xyz.java”);
1. File f1 =
2. new File(“C:a”); // Assume C:a is a dir
3. File f2 = new File(f1, “Xyz.java”);
Eventually, if you try to access a file via a File instance that uses
the wrong semantics, you’ll get a FileNotFoundException
Ben Abdallah Helmi Architect
4
5. The major methods that support
navigation are as follows:
• boolean exists() Returns true if the file or directory exists;
otherwise returns false .
• String getAbsolutePath() Returns the absolute (that is, not
relative) path of the file or directory.
• String getCanonicalPath() Returns the canonical path of the file or
directory. This method is similar to getAbsolutePath() , but the
symbols . And .. are resolved.
• String getName() Returns the name of the file or directory. The
name is the last element of the path.
• String getParent() Returns the name of the directory that contains
the File .
• boolean isDirectory() Returns true if the File object describes a
directory that exists on the file system.
• boolean isFile() Returns true if the File object describes a file that
exists on the file system.
• String[] list() Returns an array containing the names of the files
and directories within the File instance, which File must describe a
Ben Abdallah Helmi Architect file.
directory, not a
5
6. Some non-navigation methods are as
follows:canRead() Returns true if the file or directory may be
• boolean
•
•
•
•
•
•
read.
boolean canWrite() Returns true if the file or directory may be
modified.
boolean createNewFile()Creates a new empty disk file as
described by the current object, if such a file does not already
exist. Returns true if the file was created.
boolean delete() Attempts to delete the file or directory.
long length() Returns the length of the file.
boolean mkdir() Attempts to create a directory whose path is
described by the current instance of File
boolean renameTo(File newname ) Renames the file or
directory. This method returns true if the renaming succeeded;
otherwise it returns false
.
Ben Abdallah Helmi Architect
6
7. here is a summary of the key points concerning the File
class:
• An instance of File describes a file or directory.
• The file or directory might or might not exist.
• Constructing/garbage collecting an instance of File has
no effect on the local file system.
Ben Abdallah Helmi Architect
7
8. The RandomAccess File Class
• This class presents a model of files that is incompatible
with the stream/reader/writer model.
• With a random-access file, you can seek to a desired
position within a file and then read or write a desired
amount of data. The RandomAccessFile class provides
methods that support seeking, reading, and writing.
Ben Abdallah Helmi Architect
8
9. • The mode string should be either “r” or “rw”. Use “r” to
open the file for reading only, and use “rw” to open for
both reading and writing. In revision 5.0 two more modes
were introduced:
• “rws” and “rwd.” With a mode of “rws” the file is opened
for reading and writing, and any changes to the file’s
content or metadata take place immediately. (Metadata is
all the data that describes a file: its permission
modes, ownership, last-modified time, etc.) With the “rwd”
mode, the file is opened for reading and writing, and
changes to the files content, but not its metadata, take
place immediately.
Ben Abdallah Helmi Architect
9
10. 1. File file = new File(path);
2. if (!file.isFile() ||
3. !file.canRead() ||
4. !file.canWrite())
5. throw new IOException();
6. RandomAccessFile raf = new RandomAccessFile(file, “rw”);
• When the named file does not exist, constructing a
RandomAccessFile is different from constructing an ordinary File. In
this situation, if the random-access file is constructed in read-only
mode, a FileNotFoundException is thrown. If the random-access file
is constructed in readwrite mode, then a zero-length file is created.
• Java’s random-access files support only seeking relative to the
beginning of the file but methods exist that report the current
position and the length of the file, so you can effectively perform the
other kinds of seeking as long as you are willing to do the
arithmetic.
Ben Abdallah Helmi Architect
10
11. The methods that support seeking are
as follows:
• long getFilePointer() throws IOException Returns the
current position within the file, in bytes. Subsequent
reading and writing will take place starting at this position.
• long length() throws IOException Returns the length of
the file, in bytes.
• void seek(long position) throws IOException Sets the
current position within the file, in bytes. Subsequent
reading and writing will take place starting at this position.
Files start at position 0.
Ben Abdallah Helmi Architect
11
12. import java.io.*;
class GeneralRAF extends RandomAccessFile {
public GeneralRAF(File path, String mode)
throws IOException {
super(path, mode);
}
public GeneralRAF(String path, String mode)
throws IOException {
super(path, mode);
}
public void seekFromEnd(long offset)
throws IOException {
seek(length() - offset);
}
public void seekFromCurrent(long offset)
throws IOException {
seek(getFilePointer() + offset);
}}
Ben Abdallah Helmi Architect
12
13. int read() throws IOException Returns the next byte from the file
(stored in the low-order eight bits of an int) or -1 if at the end of the
file.
int read(byte dest[]) throws IOException Attempts to read enough
bytes to fill array dest[]. It returns the number of bytes read, or -1 if
at the end of the file.
int read(byte dest[], int offset, int len) throws IOException Attempts
to read len bytes into array dest[], starting at offset. It returns the
number of bytes read, or -1 if at the end of the file.
void write(int b) throws IOException Writes the low-order byte of b.
void write(byte b[]) throws IOException Writes all of byte array b[].
void write(byte b[], int offset, int len) throws IOException Writes
len bytes from byte array b[], starting at offset.
When a random-access file is no longer needed, it should be closed:
void close() throws IOException
Ben Abdallah Helmi Architect
13
14. To summarize, random-access files offer the following
functionality:
• Seeking to any position within a file
• Reading and writing single or multiple bytes
• Reading and writing groups of bytes, treated as higherlevel data types
• Closing
Ben Abdallah Helmi Architect
14
15. Streams, Readers, and Writers
• Java’s stream, reader, and writer classes view input and
output as ordered sequences of bytes.
• You have already seen how the RandomAccessFile class
allows you to read and write all of Java’s primitive data types.
The readInt() method, for example, reads four bytes from a
file, pieces them together, and returns an int. Java’s general
I/O classes provide a similar structured approach.
• The stream, reader, and writer classes are not very
complicated. The easiest way to review them is to begin with
the low-level streams.
Ben Abdallah Helmi Architect
15
16. Low-Level Streams
•
•
•
•
•
•
•
•
•
The two most common file input stream constructors are
FileInputStream(String pathname)
FileInputStream(File file)
int read() throws IOException Returns the next byte from the file (stored in the
low-order eight bits of an int) or -1 if at the end of the file.
int read(byte dest[]) throws IOException Attempts to read enough bytes to fill
array dest[]. It returns the number of bytes read or -1 if at the end of the file.
int read(byte dest[], int offset, int len) throws IOException Attempts to read len
bytes into array dest[], starting at offset. It returns the number of bytes read or -1 if at
the end of the file
int available() throws IOException Returns the number of bytes that can be read
without blocking.
void close() throws IOException Releases non-memory system resources
associated with the file. A file input stream should always be closed when no longer
needed.
long s k ip(long n b y tes) throws IOException Attempts to read and discard
nbytes bytes. Returns the number of bytes actually skipped.
Ben Abdallah Helmi Architect
16
17. byte b;
byte bytes[] = new byte[100];
byte morebytes[] = new byte[50];
try {
FileInputStream fis = new FileInputStream(“fname”);
b = (byte) fis.read(); // Single byte
fis.read(bytes); // Fill the array
fis.read(morebytes, 0, 20); // 1st 20 elements
fis.close();
}
catch (IOException e) {}
Ben Abdallah Helmi Architect
17
18. • FileOutputStream(String pathname)
• FileOutputStream(File file)
• void write(int b) throws IOException Writes the low-order
byte of b.
• void write(byte bytes[]) throws IOException Writes all
members of byte array bytes[].
• void write(byte bytes[], int offset, int len) throws
IOException Writes len bytes from array bytes[], starting
at offset.
Ben Abdallah Helmi Architect
18
19. In addition to the two classes described earlier, the java.io pack age has
other low-level input and output stream classes:
• InputStream and OutputStream These are the superclasses of the
other low-level stream classes. They can be used for reading and
writing network sockets.
• ByteArrayInputStream and ByteArrayOutputStream These
classes read and write arrays of bytes. Byte arrays are certainly not
hardware I/O devices, but the classes are useful when you want to
process or create sequences of bytes.
• PipedInputStream and PipedOutputStream These classes provide a
mechanism for synchronized communication between threads.
Ben Abdallah Helmi Architect
19
20. High-Level Streams
java supports high-level I/O with high-level streams. The
most common of extend from the superclasses
FilterInputStream and FilterOutputStream. High-le vel inp
ut streams do not re a d from inpu t devices s uch a s
files or sockets; rather, they read from other streams. H i
gh-level outpu t streams do not write to output devices but
to other streams.
A good example of a high-level input stream is the data input
stream. This class has only one constructor:
D ataInputStream(InputStream instream)
The constructor requires you to pass in an input stream.
This instance might be a file input stream (because
FileInputStream extends InputStream), an input stream
Ben Abdallah Helmi Architect
20
21. The commonly used input methods of the DataInputStream
class are as follows:
boolean read Boolean() throws IOException
byte read Byte() throws IOException
char readChar () throws IOException
double read Double () throws IOException
float readFloat () throws IOException
int readInt() throws IOException
long readLong() throws IOException
short readShort() throws IOException
String read UTF() throws IOException
There is, of course, a close() method.
Ben Abdallah Helmi Architect
21
22. try {
// Construct the chain
FileInputStream fis = new FileInputStream(“fname”);
DataInputStream dis = new DataInputStream(fis);
// R ead
double d = dis.readDouble();
int i = dis.readInt();
String s = dis.readUTF();
// Close the chain
dis.close(); // Close dis first, because it
fis.close(); // was created last
}
catch (IOException e) { }
Ben Abdallah Helmi Architect
22
23. • The DataOutputStream class is the mirror image of the
DataInputStream class. The constructor is
DataOutputStream(OutputStream ostream).
• The constructor requires you to pass in an output stream.
When you write to the data output stream, it converts the
parameters of the write methods to bytes and writes them
to ostream.
Ben Abdallah Helmi Architect
23
24. The commonly used output methods of the
DataOutputStream class are as follows:
void write Boolean(boolean b) throws Exception
void write Byte(int b) throws Exception
void write Bytes(String s) throws Exception
void writeChar(int c) throws Exception
void write D ouble(double d) throws Exception
void writeFloat(float b) throws Exception
void writeInt(int i) throws Exception
void writeLong(long l) throws Exception
void writeShort(int s) throws Exception
void writeUTF(String s) throws Exception
Ben Abdallah Helmi Architect
24
26. try {
// Create the chain
FileOutputStream fos = new FileOutputStream(“txt”);
DataOutputStream dos = new DataOutputStream(fos);
// W rite
dos.write D ouble(123.456);
dos.writeInt(55);
dos.writeUTF(“The moving finger writes”);
// Close the chain
dos.close();
fos.close();
}
catch (IOException e) { }
Ben Abdallah Helmi Architect
26
27. • BufferedInputStream and B ufferedOutputStream These
classes have internal buffers so that bytes can be read or
written in large blocks, thus minimizing I/O overhead.
• PrintStream This class can be asked to write text or primitives.
Primitives are converted to character representations. The
System.out and System.err objects are examples of this class.
The class has a println() method that outputs a string followed
by a newline character. It also has a format() method that
prints formatted text. Text formatting is covered in Chapter
8, “The java.lang and java.util packages.”
• PushbackInputStream This class allows the most recently
read byte to be put back into the stream, as if it had not yet
been read. This functionality is very useful for certain kinds of
parsers.
Ben Abdallah Helmi Architect
27
28. • It is possible to create stream chains of arbitrary length.
For example, the following code fragment implements a
data input stream that reads from a buffered input
stream, which in turn reads from a file input stream:
• FileInputStream f = new FileInputStream(“text”);
• BufferedInputStream b = new BufferedInputStream(f);
• DataInputStream d = new DataInputStream(b);
Ben Abdallah Helmi Architect
28
30. Readers and Writers
• Readers and writers are like input and output streams: The
low-level varieties communicate with I/O devices, and the
high-level varieties communicate with low-level varieties.
What makes readers and writers different is that they are
exclusively oriented to Unicode characters.
The other low-level reader and writer classes are as follows:
• CharArrayReader and CharArrayWriter Read and write
char arrays.
• PipedReader and PipedWriter Provide a mechanism for
thread communication.
• StringReader and StringWriter Read and write strings.
Ben Abdallah Helmi Architect
30
31. • int read() throws IOException Returns the next char
(stored in the low-order 16 bits of the int return value)
or -1 if at the end of input.
• int read(char dest[]) throws IOException Attempts to
read enough chars to fill array dest[]. It returns the
number of chars read or -1 if at the end of input.
• int read(char dest[], int offset, int len) throws
IOException Attempts to read len chars into array
dest[], starting at offset. It returns the number of chars
read or -1 if at the end of input
Ben Abdallah Helmi Architect
31
32. • int read() throws IOException Returns the next char (stored in
the low-order 16 bits of the int return value) or -1 if at the end of
input.
• int read(char dest[]) throws IOException Attempts to read
enough chars to fill array dest[]. It returns the number of chars
read or -1 if at the end of input.
• int read(char dest[], int offset, int len) throws IOException
Attempts to read len chars into array dest[], starting at offset. It
returns the number of chars read or -1 if at the end of input
• LineNumberReader This class views its input as a sequence of
lines of text. A method called readLine() returns the next
line, and the class keeps track of the current line number.
• PrintWriter This class is similar to PrintStream, but it writes
chars rather than bytes.
• PushbackReader This class is similar to
PushbackInputStream, but it reads chars rather than bytes.
Ben Abdallah Helmi Architect
32
35. • The preceding discussion has carefully avoided a crucial point.
Consider a file reader, which reads bytes from a file and returns
strings of Unicode. How does the reader know how to translate an
8-bit character on the disk into a 16-bit character inside the JVM?
Similarly, how does a file writer know how to translate a 16-bit
Unicode character into an 8-bit byte?
• An encoding is a mapping between 8-bit characters and Unicode.
Ben Abdallah Helmi Architect
35
36. Object Streams and Serialization
• As you have seen, data input and output streams allow
you to read and write primitives and strings, rather than
individual bytes. Object streams go one step beyond data
streams by allowing you to read and write entire objects.
• The process of writing an object is called serialization. To
serialize an object, first create an instance of
java.io.ObjectOutputStream.
Ben Abdallah Helmi Architect
36
38. • The ObjectOutputStream class has a writeUTF() method, as
well as writeByte(), writeShort(), and all other write-primitive
methods that also appear in data output streams.
• The ObjectInputStream class has corresponding reading
methods. So if you want to create a file that contains
serialized primitives and strings as well as serialized
objects, you can do it with a single output stream.
• Static fields are not, because it would not be appropriate to
change a static variable, which is shared by all instances of a
class, just because one instance of the class got deserialized.
(To deserialize is to convert a serialized representation into a
replica of the original object.) Transient fields are also not
serialized.
• All non-static non-transient fields are written to object output
streams,regardless of whether they are
public, private, default, or protected.
Ben Abdallah Helmi Architect
38
39. • When an object is serialized, it will probably be
deserialized by a different JVM. Any JVM that tries to
deserialize an object must have access to that object’s
class definition. In other words, if the class is not already
loaded, its class file must appear in the new JVM’s
classpath. If this is not the case, readObject() will throw
an exception.
• In the terminology of serialization, when an object is
serialized, its entire graph is serialized. An object’s graph
is the object itself, plus all the objects it references, plus
all the objects those objects reference, and so on. When
an object input stream deserializes an object, the entire
graph is deserialized.
Ben Abdallah Helmi Architect
39
40. • public void writeObject(Object obj)
• However, if you look at the method detail section for
writeObject(), you’ll see that it throws
NotSerializableException if “some object to be serialized
does not implement the java.io.Serializable interface.” So
even though the method declares that its argument is an
object, it contains code that checks for the precondition that
the argument must be an instanceof Serializable, and it
throws the exception if the precondition is not met. Even if the
object being passed into the method implements
Serializable, the exception might still be thrown. Since the
object’s entire graph is serialized, all objects referenced by the
object must implement Serializable, and all objects referenced
by those objects must do the same, and so on.
Ben Abdallah Helmi Architect
40
41. • You probably recognize the Serializable interface from
your reading of the API pages. Most of the core Java
classes implement it. All the wrapper classes do so, and
so do the collection classes. In fact, the only core Java
classes that do not implement Serializable are like
Threads
• Empty interfaces such as Serializable are known as
tagging interfaces. They identify implementing classes as
having certain properties, without requiring those classes
to actually implement any methods.
• Arrays of primitives or serializable objects are themselves
serializable. That should not be serialized
Ben Abdallah Helmi Architect
41
42. • Deserializing involves a lot of tricky business behind the
scenes. The object input stream creates a blank instance of
the object being deserialized and then sets its field values. In
order for this to happen, a bizarre condition must apply. Think
about the class hierarchy of the object being deserialized.
• The object itself implements Serializable; the object’s parent
class might or might not implement Serializable. As you work
your way up the inheritance hierarchy, you eventually get to
the first superclass that is not itself serializable. That
superclass must have a no-args constructor.
• Often the first non-serializable superclass is Object, which
does have the right kind of constructor. If the condition isn’t
met, readObject() throws java.io.InvalidClassException. The
exception message says, “no valid constructor.”
Ben Abdallah Helmi Architect
42
43. • Notice the private access. When an object output stream
executes its writeObject() method, it checks to see if the
object has a writeObject() method matching the above
signature. If this is the case, the stream bypasses its
default behavior (which is to serialize the object’s nonstatic non-transient fields) and just calls the object’s
writeObject() method.
• Similarly, when an object input stream executes its
readObject() method, it checks to see if the object has a
readObject() method matching the above signature, and if
so the ordinary deserialization process is bypassed in
favor of a call to the readObject() method.
Ben Abdallah Helmi Architect
43
44. • If a class wants its writeObject() method to take special
action in addition to, rather than instead of, the default
serialization behavior, it can call the output stream’s
defaultWriteObject() method, which serializes the object’s
non-static non-transient fields.
• As you might expect, the ObjectInputStream class has a
defaultReadObject() method, which deserializes the
current object from the object input stream.
Ben Abdallah Helmi Architect
44
45. Import java.io.*;
class DoItMyself implements Serializable {
private String id;
protected int n;
transient byte notMe;
private void writeObject(ObjectOutputStream oos)
throws IOException {
oos.writeUTF(“The password is swordfish”);
oos.defaultWriteObject();
}
private void readObject(ObjectInputStream ois)
throws IOException, ClassNotFoundException {
String password = ois.readUTF();
if (!password.equals(“The password is swordfish”))
throw new SecurityException(“Bad password”);
ois.defaultReadObject();
Ben}}Abdallah Helmi Architect
When an instance of this class
is serialized, the object output
stream notices that the
instance
has a writeObject() method
with the appropriate signature.
This method is called. A
password
string is serialized. Then the
output stream’s
defaultWriteObject() is
called, and default serialization
is performed.
45
46. Another way to provide custom serialization and deserialization is to
implement a subinterface of Serializable, called Externalizable.
(Notice that since Externalizable extends Serializable, the
implements Serializable precondition in writeObject() is met by any
object that implements Externalizable.
Externalizable contains two method signatures:
void writeExternal(ObjectOutput out)
throws IOException
void readExternal(ObjectInput in)
throws IOException, ClassNotFoundException
• ObjectOutput and ObjectInput are interfaces that are implemented
by ObjectOutputStream and ObjectInputStream. They
define, respectively, writeObject() and readObject() methods
Ben Abdallah Helmi Architect
46
47. import java.io.*;
public class Account implements Externalizable {
private String ownerName;
private String password;
private float balance;
private String reverse(String reverseMe) {
String reversed = "";
for (int i=reverseMe.length()-1; i>=0; i--)
reversed += reverseMe.charAt(i);
return reversed;
}
public void writeExternal(ObjectOutput outStream)
throws IOException {
outStream.writeObject(ownerName);
outStream.writeObject(reverse(password));
When an instance of this class is passed to an
object output stream, the default serialization
procedure is bypassed; instead, the stream calls the
instance’s writeExternal() method. When an object
input stream reads a serialized instance of
Account, a call is made to the no-args constructor
for the Account class; this constructor must be
public. Then the newly constructed object receives a
readExternal() call so that it can reconstitute its
serialized values.
outStream.writeObject(new Float(balance));
}
public void readExternal(ObjectInput inStream)
throws IOException, ClassNotFoundException {
ownerName = (String)inStream.readObject();
String reversedPassword =
(String)inStream.readObject();
password = reverse(reversedPassword);
balance = ((Float)inStream.readObject()).floatValue();
Ben Abdallah Helmi Architect
}}
47
48. • Notice that the readObject() mechanism of
ObjectInputStream relies on the existence of a no-args
constructor for any class that implements Externalizable.
If an externalizable class has no no-args constructor, the
readObject() method will throw
java.io.InvalidClassException.
Ben Abdallah Helmi Architect
48