Java Fundamental Classes Reference

Chapter 6 - I/O

Contents:
Input Streams and Readers
Output Streams and Writers
File Manipulation

The java.io package contains the fundamental classes for performing input and output operations in Java. These I/O classes can be divided into four basic groups:

All fundamental I/O in Java is based on streams. A stream represents a flow of data, or a channel of communication. Conceptually, there is a reading process at one end of the stream and a writing process at the other end. Java 1.0 supported only byte streams, which meant that Unicode characters were not always handled correctly. As of Java 1.1, there are classes in java.io for both byte streams and character streams. The character stream classes, which are called readers and writers, handle Unicode characters appropriately.

The rest of this chapter describes the classes in java.io that read from and write to streams, as well as the classes that manipulate files. The classes for serializing objects are described in Chapter 7, Object Serialization.

6.1 Input Streams and Readers

The InputStream class is an abstract class that defines methods to read sequentially from a stream of bytes. Java provides subclasses of the InputStream class for reading from files, StringBuffer objects, and byte arrays, among other things. Other subclasses of InputStream can be chained together to provide additional logic, such as keeping track of the current line number or combining multiple input sources into one logical input stream. It is also easy to define a subclass of InputStream that reads from any other kind of data source.

In Java 1.1, the Reader class is an abstract class that defines methods to read sequentially from a stream of characters. Many of the byte-oriented InputStream subclasses have character-based Reader counterparts. Thus, there are subclasses of Reader for reading from files, character arrays, and String objects.

InputStream

The InputStream class is the abstract superclass of all other byte input stream classes. It defines three read() methods for reading from a raw stream of bytes:

read()
read(byte[] b)
read(byte[] b, int off, int len)

If there is no data available to read, these methods block until input is available. The class also defines an available() method that returns the number of bytes that can be read without blocking and a skip() method that skips ahead a specified number of bytes. The InputStream class defines a mechanism for marking a position in the stream and returning to it later, via the mark() and reset() methods. The markSupported() method returns true in subclasses that support these methods.

Because the InputStream class is abstract, you cannot create a "pure" InputStream. However, the various subclasses of InputStream can be used interchangeably. For example, methods often take an InputStream as a parameter. Such a method accepts any subclass of InputStream as an argument.

InputStream is designed so that read(byte[]) and read(byte[], int, int) both call read(). Thus, when you subclass InputStream, you only need to define the read() method. However, for efficiency's sake, you should also override read(byte[], int, int) with a method that can read a block of data more efficiently than reading each byte separately.

Reader

The Reader class is the abstract superclass of all other character input stream classes. It defines nearly the same methods as InputStream, except that the read() methods deal with characters instead of bytes:

read()
read(char[] cbuf)
read(char[] cbuf, int off, int len)

The available() method of InputStream has been replaced by the ready() method of Reader, which simply returns a flag that indicates whether or not the stream must block to read the next character.

Reader is designed so that read() and read(char[]) both call read(char[], int, int). Thus, when you subclass Reader, you only need to define the read(char[], int, int) method. Note that this design is different from, and more efficient than, that of InputStream.

InputStreamReader

The InputStreamReader class serves as a bridge between InputStream objects and Reader objects. Although an InputStreamReader acts like a character stream, it gets its input from an underlying byte stream and uses a character encoding scheme to translate bytes into characters. When you create an InputStreamReader, specify the underlying InputStream and, optionally, the name of an encoding scheme. For example, the following code fragment creates an InputStreamReader that reads characters from a file that is encoded using the ISO 8859-5 encoding:

String fileName = "encodedfile.txt"; String encodingName = "8859_5";
InputStreamReader in;
try {
   x FileInputStream fileIn = new FileInputStream(fileName);
    in = new InputStreamReader(fileIn, encodingName);
} catch (UnsupportedEncodingException e1) {
    System.out.println(encodingName + " is not a supported encoding scheme.");
} catch (IOException e2) {
    System.out.println("The file " + fileName + " could not be opened.");
}

FileInputStream and FileReader

The FileInputStream class is a subclass of InputStream that allows a stream of bytes to be read from a file. The FileInputStream class has no explicit open method. Instead, the file is implicitly opened, if appropriate, when the FileInputStream is created. There are three ways to create a FileInputStream:

·             FileInputStream f1 = new FileInputStream("foo.txt");
·             File f = new File("foo.txt");
·             FileInputStream f2 = new FileInputStream(f);
·             RandomAccessFile raf;
·             raf = new RandomAccessFile("z.txt","r");
·             FileInputStream f3 = new FileInputStream(raf.getFD());

The FileReader class is a subclass of Reader that reads a stream of characters from a file. The bytes in the file are converted to characters using the default character encoding scheme. If you do not want to use the default encoding scheme, you need to wrap an InputStreamReader around a FileInputStream, as shown above. You can create a FileReader from a filename, a File object, or a FileDescriptor object, as described above for FileInputStream.

StringReader and StringBufferInputStream

The StringReader class is a subclass of Reader that gets its input from a String object. The StringReader class supports mark-and-reset functionality via the mark() and reset() methods. The following example shows the use of StringReader:

StringReader sr = new StringReader("abcdefg");
try {
    char[] buffer = new char[3];
    sr.read(buffer);
    System.out.println(buffer);
} catch (IOException e) {
    System.out.println("There was an error while reading.");
}

This code fragment produces the following output:

abc

The StringBufferInputStream class is the byte-based relative of StringReader. The entire class is deprecated as of Java 1.1 because it does not properly convert the characters of the string to a byte stream; it simply chops off the high eight bits of each character. Although the markSupported() method of StringBufferInputStream returns false, the reset() method causes the next read operation to read from the beginning of the String.

CharArrayReader and ByteArrayInputStream

The CharArrayReader class is a subclass of Reader that reads a stream of characters from an array of characters. The CharArrayReader class supports mark-and-reset functionality via the mark() and reset() methods. You can create a CharArrayReader by passing a reference to a char array to a constructor like this:

char[] c;
...
CharArrayReader r;
r = new CharArrayReader(c);

You can also create a CharArrayReader that only reads from part of an array of characters by passing an offset and a length to the constructor. For example, to create a CharArrayReader that reads elements 5 through 24 of a char array you would write:

r = new CharArrayReader(c, 5, 20);

The ByteArrayInputStream class is just like CharArrayReader, except that it deals with bytes instead of characters. In Java 1.0, ByteArrayInputStream did not fully support mark() and reset(); in Java 1.1 these methods are completely supported.

PipedInputStream and PipedReader

The PipedInputStream class is a subclass of InputStream that facilitates communication between threads. Because it reads bytes written by a connected PipedOutputStream, a PipedInputStream must be connected to a PipedOutputStream to be useful. There are a few ways to connect a PipedInputStream to a PipedOutputStream. You can first create the PipedOutputStream and pass it to the PipedInputStream constructor like this:

PipedOutputStream po = new PipedOutputStream();
PipedInputStream pi = new PipedInputStream(po);

You can also create the PipedInputStream first and pass it to the PipedOutputStream constructor like this:

PipedInputStream pi = new PipedInputStream();
PipedOutputStream po = new PipedOutputStream(pi);

The PipedInputStream and PipedOutputStream classes each have a connect() method you can use to explicitly connect a PipedInputStream and a PipedOutputStream as follows:

PipedInputStream pi = new PipedInputStream();
PipedOutputStream po = new PipedOutputStream();
pi.connect(po);

Or you can use connect() as follows:

PipedInputStream pi = new PipedInputStream();
PipedOutputStream po = new PipedOutputStream();
po.connect(pi);

Multiple PipedOutputStream objects can be connected to a single PipedInputStream at one time, but the results are unpredictable. If you connect a PipedOutputStream to an already connected PipedInputStream, any unread bytes from the previously connected PipedOutputStream are lost. Once the two PipedOutputStream objects are connected, the PipedInputStream reads bytes written by either PipedOutputStream in the order that it receives them. The scheduling of different threads may vary from one execution of the program to the next, so the order in which the PipedInputStream receives bytes from multiple PipedOutputStream objects can be inconsistent.

The PipedReader class is the character-based equivalent of PipedInputStream. It works in the same way, except that a PipedReader is connected to a PipedWriter to complete the pipe, using either the appropriate constructor or the connect() method.

FilterInputStream and FilterReader

The FilterInputStream class is a wrapper class for InputStream objects. Conceptually, an object that belongs to a subclass of FilterInputStream is wrapped around another InputStream object. The constructor for this class requires an InputStream. The constructor sets the object's in instance variable to reference the specified InputStream, so from that point on, the FilterInputStream is associated with the given InputStream. All of the methods in FilterInputStream work by calling the corresponding methods in the underlying InputStream. Because the close() method of a FilterInputStream calls the close() method of the InputStream that it wraps, you do not need to explicitly close the underlying InputStream.

A FilterInputStream does not add any functionality to the object that it wraps, so by itself it is not very useful. However, subclasses of the FilterInputStream class do add functionality to the objects that they wrap in two ways:

The FilterReader class is the character-based equivalent of FilterInputStream. A FilterReader is wrapped around an underlying Reader object; the methods of FilterReader call the corresponding methods of the underlying Reader. However, unlike FilterInputStream, FilterReader is an abstract class, so you cannot instantiate it directly.

DataInputStream

The DataInputStream class is a subclass of FilterInputStream that provides methods for reading a variety of data types. The DataInputStream class implements the DataInput interface, so it defines methods for reading all of the primitive Java data types.

You create a DataInputStream by passing a reference to an underlying InputStream to the constructor. Here is an example that creates a DataInputStream and uses it to read an int that represents the length of an array and then to read the array of long values:

long[] readLongArray(InputStream in) throws IOException {
    DataInputStream din = new DataInputStream(in);
    int count = din.readInt();
    long[] a = new long[count];
    for (int i = 0; i < count; i++) {
        a[i] = din.readLong();
    }
    return a;
}

BufferedReader and BufferedInputStream

The BufferedReader class is a subclass of Reader that buffers input from an underlying Reader. A BufferedReader object reads enough characters from its underlying Reader to fill a relatively large buffer, and then it satisfies read operations by supplying characters that are already in the buffer. If most read operations read just a few characters, using a BufferedReader can improve performance because it reduces the number of read operations that the program asks the operating system to perform. There is generally a measurable overhead associated with each call to the operating system, so reducing the number of calls into the operating system improves performance. The BufferedReader class supports mark-and-reset functionality via the mark() and reset() methods.

Here is an example that shows how to create a BufferedReader to improve the efficiency of reading from a file:

try {
    FileReader fileIn = new FileReader("data.dat");
    BufferedReader in = new BufferedReader(fileIn);
    // read from the file
} catch (IOException e) {
    System.out.println(e);
}

The BufferedInputStream class is the byte-based counterpart of BufferedReader. It works in the same way as BufferedReader, except that it buffers input from an underlying InputStream.

LineNumberReader and LineNumberInputStream

The LineNumberReader class is a subclass of BufferedReader. Its read() methods contain additional logic to count end-of-line characters and thereby maintain a line number. Since different platforms use different characters to represent the end of a line, LineNumberReader takes a flexible approach and recognizes "\n", "\r", or "\r\n" as the end of a line. Regardless of the end-of-line character it reads, LineNumberReader returns only "\n" from its read() methods.

You can create a LineNumberReader by passing its constructor a Reader. The following example prints out the first five lines of a file, with each line prefixed by its number. If you try this example, you'll see that the line numbers begin at 0 by default:

try {
    FileReader fileIn = new FileReader("text.txt");
    LineNumberReader in = new LineNumberReader(fileIn);
    for (int i = 0; i < 5; i++)
        System.out.println(in.getLineNumber() + " " + in.readLine());
}catch (IOException e) {
    System.out.println(e);
}

The LineNumberReader class has two methods pertaining to line numbers. The getLineNumber() method returns the current line number. If you want to change the current line number of a LineNumberReader, use setLineNumber(). This method does not affect the stream position; it merely sets the value of the line number.

The LineNumberInputStream is the byte-based equivalent of LineNumberReader. The entire class is deprecated in Java 1.1 because it does not convert bytes to characters properly. Apart from the conversion problem, LineNumberInputStream works the same as LineNumberReader, except that it takes its input from an InputStream instead of a Reader.

SequenceInputStream

The SequenceInputStream class is used to sequence together multiple InputStream objects. Consider this example:

FileInputStream f1 = new FileInputStream("data1.dat");
FileInputStream f2 = new FileInputStream("data2.dat");
SequenceInputStream s = new SequenceInputStream(f1, f2);

This example creates a SequenceInputStream that reads all of the bytes from f1 and then reads all of the bytes from f2 before reporting that it has encountered the end of the stream. You can also cascade SequenceInputStream object themselves, to allow more than two input streams to be read as if they were one. You would write it like this:

FileInputStream f3 = new FileInputStream("data3.dat");
SequenceInputStream s2 = new SequenceInputStream(s, f3);

The SequenceInputStream class has one other constructor that may be more appropriate for wrapping more than two InputStream objects together. It takes an Enumeration of InputStream objects as its argument. The following example shows how to create a SequenceInputStream in this manner:

Vector v = new Vector();
v.add(new FileInputStream("data1.dat"));
v.add(new FileInputStream("data2.dat"));
v.add(new FileInputStream("data3.dat"));
Enumeration e = v.elements();
SequenceInputStream s = new SequenceInputStream(e);

PushbackInputStream and PushbackReader

The PushbackInputStream class is a FilterInputStream that allows data to be pushed back into the input stream and reread by the next read operation. This functionality is useful for implementing things like parsers that need to read data and then return it to the input stream. The Java 1.0 version of PushbackInputStream supported only a one-byte pushback buffer; in Java 1.1 this class has been enhanced to support a larger pushback buffer.

To create a PushbackInputStream, pass an InputStream to its constructor like this:

FileInputStream ef = new FileInputStream("expr.txt");
PushbackInputStream pb = new PushbackInputStream(ef);

This constructor creates a PushbackInputStream that uses a default one-byte pushback buffer. When you have data that you want to push back into the input stream to be read by the next read operation, you pass the data to one of the unread() methods.

The PushbackReader class is the character-based equivalent of PushbackInputStream. In the following example, we create a PushbackReader with a pushback buffer of 48 characters:

FileReader fileIn = new FileReader("expr.txt");
PushbackReader in = new PushbackReader(fileIn, 48);

Here is an example that shows the use of a PushbackReader:

public String readDigits(PushbackReader pb) {
    char c;
    StringBuffer buffer = new StringBuffer();
    try {
        while (true) {
            c = (char)pb.read();
            if (!Character.isDigit(c))
                break;
            buffer.append(c);
        }
        if (c != -1)
            pb.unread(c);
    }catch (IOException e) {}
    return buffer.toString();
}

The above example shows a method that reads characters corresponding to digits from a PushbackReader. When it reads a character that is not a digit, it calls the unread() method so that the nondigit can be read by the next read operation. It then returns a string that contains the digits that were read.

 

6.2 Output Streams and Writers

The OutputStream class is an abstract class that defines methods to write a stream of bytes sequentially. Java provides subclasses of the OutputStream class for writing to files and byte arrays, among other things. Other subclasses of OutputStream can be chained together to provide additional logic, such as writing multibyte data types or converting data to a string representation. It is also easy to define a subclass of OutputStream that writes to another kind of destination.

In Java 1.1, the Writer class is an abstract class that defines methods to write to a stream of characters sequentially. Many of the byte-oriented subclasses of OutputStream have counterparts in the character-oriented world of Writer objects. Thus, there are subclasses of Writer for writing to files and character arrays.

OutputStream

The OutputStream class is the abstract superclass of all other byte output stream classes. It defines three write() methods for writing to a raw stream of bytes:

write(int b)
write(byte[] b)
write(byte[] b, int off, int len)

Some OutputStream subclasses may implement buffering to increase efficiency. OutputStream provides a method, flush(), that tells the OutputStream to write any buffered output to the underlying device, which may be a disk drive or a network.

Because the OutputStream class is abstract, you cannot create a "pure" OutputStream. However, the various subclasses of OutputStream can be used interchangeably. For example, methods often take OutputStream parameters. This means that such a method accepts any subclass of OutputStream as an argument.

OutputStream is designed so that write(byte[]) and write(byte[], int, int) call write(int). Thus, when you subclass OutputStream, you only need to define the write() method. However, for efficiency's sake, you should also override write(byte[], int, int) with a method that can write a block of data more efficiently than writing each byte separately.

Writer

The Writer class is the abstract parent class of all other character output stream classes. It defines nearly the same methods as OutputStream, except that the write() methods deal with characters instead of bytes:

write(int c)
write(char[] cbuf)
write(char[] cbuf, int off, int len)
write(String str)
write(String str, int off, int len)

Writer also includes a flush() method that forces any buffered data to be written to the stream.

Writer is designed so that write(int) and write(char[]) both call write(char[], int, int). Thus, when you subclass Writer, you only need to define the write(char[], int, int) method. Note that this design is different from, and more efficient than, that of OutputStream.

OutputStreamWriter

The OutputStreamWriter class serves as a bridge between Writer objects and OutputStream objects. Although an OutputStreamWriter acts like a character stream, it converts its characters to bytes using a character encoding scheme and writes them to an underlying OutputStream. This class is the output counterpart of InputStreamReader. When you create an OutputStreamWriter, specify the underlying OutputStream and, optionally, the name of an encoding scheme. The following example shows how to construct an OutputStreamWriter that writes characters to a file, encoded using the ISO 8859-5 encoding:

String fileName = "encodedfile.txt";
String encodingName = "8859_5";
OutputStreamWriter out;
try {
    FileOutputStream fileOut = new FileOutputStream (fileName);
    out = new OutputStreamWriter (fileOut, encodingName);
} catch (UnsupportedEncodingException e1) {
    System.out.println(encodingName + " is not a supported encoding scheme.");
} catch (IOException e2) {
    System.out.println("The file " + fileName + " could not be opened.");
}

FileWriter and FileOutputStream

The FileOutputStream class is a subclass of OutputStream that writes a stream of bytes to a file. The FileOutputStream class has no explicit open method. Instead, the file is implicitly opened, if appropriate, when you create the FileOutputStream object. There are several ways to create a FileOutputStream:

·        FileOutputStream f1 = new FileOutputStream("foo.txt");
·        FileOutputStream f1 = new FileOutputStream("foo.txt", true);
·        File f = new File("foo.txt");
·        FileOutputStream f2 = new FileOutputStream(f);
·        RandomAccessFile raf;
·        raf = new RandomAccessFile("z.txt","rw");
·        FileInputStream f3 = new FileOutputStream(raf.getFD());

The FileWriter class is a subclass of Writer that writes a stream of characters to a file. The characters to be written are converted to bytes using the default character encoding scheme. If you do not want to use the default encoding scheme, you need to wrap an OutputStreamWriter around a FileOutputStream as shown above. You can create a FileWriter from a filename, a File object, or a FileDescriptor object, as described above for FileOutputStream.

StringWriter

The StringWriter class is a subclass of Writer that stores its data in a String object. Internally, it uses a StringBuffer, which can be examined using getBuffer(). A String containing the data that has been written can be obtained with toString(). The following example creates a StringWriter and writes data into it:

StringWriter out = new StringWriter();
char[] buffer = {'b', 'o', 'o', '!', 'h', 'a'};
out.write('B');
out.write("uga");
out.write(buffer, 0, 4);
System.out.println(out.toString());

This example produces the following output:

Bugaboo!

CharArrayWriter and ByteArrayOutputStream

The CharArrayWriter class is a subclass of Writer that writes characters to an internal array. There are three ways to retrieve the data that has been written to the CharArrayWriter:

This example demonstrates how to create a CharArrayWriter, write data into it, and retrieve the data:

CharArrayWriter out = new CharArrayWriter();
try {
    out.write("Daphne"); 
}catch (IOException e) {}
char[] buffer = out.toCharArray();
System.out.println(buffer);
String result = out.toString();
System.out.println(result);

This example produces the following output:

Daphne
Daphne

The internal buffer of the CharArrayWriter is expanded as needed when data is written. If you know how many characters you will be writing, you can make your CharArrayWriter a little more efficient by passing an initial size to its constructor.

ByteArrayOutputStream is the byte-oriented equivalent of CharArrayWriter. It works in much the same way, with the following exceptions:

[1] In Java 1.1, the default encoding scheme is used for the conversion. In earlier versions, characters are simply created using the eight bits of each byte as the low eight bits of the character.

PipedOutputStream and PipedWriter

The PipedOuputStream class is a subclass of OutputStream that facilitates communication between threads. A PipedOutputStream must be connected to a PipedInputStream to be useful, as it writes bytes that can be read by a connected PipedInputStream. There are a few ways to connect a PipedOutputStream to a PipedInputStream. You can first create the PipedInputStream and pass it to the PipedOutputStream constructor like this:

PipedInputStream pi = new PipedInputStream();
PipedOutputStream po = new PipedOutputStream(pi);

You can also create the PipedOutputStream first and pass it to the PipedInputStream constructor like this:

PipedOutputStream po = new PipedOutputStream();
PipedInputStream pi = new PipedInputStream(po);

The PipedOutputStream and PipedInputStream classes each have a connect() method you can use to explicitly connect a PipedOutputStream and a PipedInputStream as follows:

PipedOutputStream po = new PipedOutputStream();
PipedInputStream pi = new PipedInputStream();
po.connect(pi);

Or you can use connect() as follows:

PipedOutputStream po = new PipedOutputStream();
PipedInputStream pi = new PipedInputStream();
pi.connect(po);

Only one PipedInputStream can be connected to a PipedOutputStream at a time. If you use a connect() method to connect a PipedOutputStream to an already connected PipedInputStream, any unread bytes from the previously connected PipedOutputStream are lost.

PipedWriter is the character-based equivalent of PipedOutputStream. It works in the same way, except that a PipedWriter is connected to a PipedReader to complete the pipe, using either the appropriate constructor or the connect() method.

FilterOutputStream and FilterWriter

The FilterOutputStream class is a wrapper class for OutputStream objects. Conceptually, objects that belong to a subclass of FilterOutputStream are wrapped around another OutputStream object. The constructor for this class requires an OutputStream. The constructor sets the object's out instance variable to reference the specified OutputStream, so from that point on, the FilterOutputStream is associated with the given OutputStream. All of the methods of FilterOutputStream work by calling the corresponding methods in the underlying OutputStream. Because the close() method of a FilterOutputStream calls the close() method of the OutputStream that it wraps, you do not need to explicitly close the underlying OutputStream.

A FilterOutputStream does not add any functionality to the object that it wraps, so by itself it is not very useful. However, subclasses of the FilterOutputStream class do add functionality to the objects that they wrap in two ways:

The FilterWriter class is the character-based equivalent of FilterOutputStream. A FilterWriter is wrapped around an underlying Writer object; the methods of FilterWriter call the corresponding methods of the underlying Writer. However, unlike FilterOutputStream, FilterWriter is an abstract class, so you cannot instantiate it directly.

DataOutputStream

The DataOutputStream class is a subclass of the FilterOutputStream class that provides methods for writing a variety of data types to an OutputStream. The DataOutputStream class implements the DataOutput interface, so it defines methods for writing all of the primitive Java data types.

You create a DataOutputStream by passing a reference to an underlying OutputStream to the constructor. Here is an example that creates a DataOutputStream and uses it to write the length of an array as an int and then to write the values in array as long values:

void writeLongArray(OutputStream out, long[] a) throws IOException {
    DataOutputStream dout = new DataOutputStream(out);
    dout.writeInt(a.length);
    for (int i = 0; i < a.length; i++) {
        dout.writeLong(a[i]);
    }
}

BufferedWriter and BufferedOutputStream

The BufferedWriter class is a subclass of Writer that stores output destined for an underlying Writer in an internal buffer. When the buffer fills up, the entire buffer is written, or flushed, to the underlying Writer. Using a BufferedWriter is usually faster than using a regular Writer because it reduces the number of calls that must be made to the underlying device, be it a disk or a network. You can use the flush() method to force a BufferedWriter to write the contents of the buffer to the underlying Writer.

The following example shows how to create a BufferedWriter around a network socket's output stream:

public Writer getBufferedWriter(Socket s) throws IOException {
    OutputStreamWriter converter = new OutputStreamWriter(s.getOutputStream());
    return new BufferedWriter(converter);
}

First, create an OutputStreamWriter that converts characters to bytes using the default encoding scheme. After they are converted, the bytes are written to the socket. Then simply wrap a BufferedWriter around the OutputStreamWriter to buffer the output.

The BufferedOutputStream class is the byte-based equivalent of BufferedWriter. It works in the same way as BufferedWriter, except that it buffers output for an underlying OutputStream. Here's how you would rewrite the previous example to create a BufferedOutputStream around a socket:

public OutputStream getBufferedOutputStream(Socket s) throws IOException {
    return new BufferedOutputStream(s.getOutputStream());
}

PrintWriter and PrintStream

The PrintWriter class is a subclass of Writer that provides a set of methods for printing string representations of every Java data type. A PrintWriter can be wrapped around an underlying Writer object or an underlying OutputStream object. In the case of wrapping an OutputStream, any characters written to the PrintWriter are converted to bytes using the default encoding scheme.[2] Additional constructors allow you to specify if the underlying stream should be flushed after every line-separator character is written.

[2] You can achieve the same effect using an OutputStreamWriter, but it is easier to use the PrintWriter(OutputStream) constructor. However, if you want to use an encoding scheme other than the default one, you need to create your own OutputStreamWriter.

The PrintWriter class provides a print() and a println() method for every primitive Java data type. As their names imply, the println() methods do the same thing as their print() counterparts, but also append a line separator character.

The following example demonstrates how to wrap a PrintWriter around an OutputStream:

boolean b = true;
char c = '%'
double d = 8.31451
int i = 42;
String s = "R = ";
PrintWriter out = new PrintWriter(System.out, true);
out.print(s);
out.print(d);
out.println();
out.println(b);
out.println(c);
out.println(i);

This example produces the following output:

R = 8.31451
true
%
42

PrintWriter objects are often used to report errors. For this reason, the methods of this class do not throw exceptions. Instead, the methods catch any exceptions thrown by any downstream OutputStream or Writer objects and set an internal flag, so that the object can remember that a problem occurred. You can query the internal flag by calling the checkError() method.

Although you can create a PrintWriter that flushes the underlying stream every time a line-separator character is written, this may not always be exactly what you want. Suppose that you are writing a program that has a character-based user interface, and that you want the program to output a prompt and then allow the user to input a response on the same line. In order to make this work with a PrintWriter, you need to get the PrintWriter to write the characters in its buffer without writing a line separator. You can do this by calling the flush() method.

PrintWriter is new as of Java 1.1; it is more capable than the PrintStream class. You should use PrintWriter instead of PrintStream because it uses the default encoding scheme to convert characters to bytes for an underlying OutputStream. The constructors for PrintStream are deprecated in Java 1.1. In fact, the whole class probably would have been deprecated, except that it would have generated a lot of compilation warnings for code that uses System.out and System.err.

 

6.3 File Manipulation

While streams are used to handle most types of I/O in Java, there are some nonstream-oriented classes in java.io that are provided for file manipulation. Namely, the File class represents a file on the local filesystem, while the RandomAccessFile class provides nonsequential access to data in a file. In addition, the FilenameFilter interface can be used to filter a list of filenames.

File

The File class represents a file on the local filesystem. You can use an instance of the File class to identify a file, obtain information about the file, and even change information about the file. The easiest way to create a File is to pass a filename to the File constructor, like this:

new File("readme.txt")

Although the methods that the File class provides for manipulating file information are relatively platform independent, filenames must follow the rules of the local filesystem. The File class does provide some information that can be helpful in interpreting filenames and path specifications. The variable separatorChar specifies the system-specific character used to separate the name of a directory from what follows.[3] In a Windows environment, this is a backslash (\), while in a UNIX or Macintosh environment it is a forward slash (/). You can create a File object that refers to a file called readme.txt in a directory called myDir as follows:

[3] This information is also available as System.getProperty('file.separator'), which is how the File class gets it.

new File("myDir" + File.separatorChar + "readme.txt")

The File class also provides some constructors that make this task easier. For example, there is a File constructor that takes two strings as arguments: the first string is the name of a directory and the second string is the name of a file. The following example does the exact same thing as the previous example:

new File("myDir", "readme.txt")

The File class has another constructor that allows you to specify the directory of a file using a File object instead of a String:

File dir = new File("myDir");
File f = new File(dir, "readme.txt");

Sometimes a program needs to process a list of files that have been passed to it in a string. For example, such a list of files is passed to the Java environment by the CLASSPATH environment variable and can be accessed by the expression:

System.getProperty("java.class.path")

This list contains one or more filenames separated by separator characters. In a Windows or Macintosh environment, the separator character is a semicolon (;), while in a UNIX environment, the separator character is a colon (:). The system-specific separator character is specified by the pathSeparatorChar variable. Thus, to turn the value of CLASSPATH into a collection of File objects, we can write:

StringTokenizer s;
Vector v = new Vector();
s = new StringTokenizer(System.getProperty("java.class.path"), 
                        File.pathSeparator);
while (s.hasMoreTokens())
    v.addElement(new File(s.nextToken()));

You can retrieve the pathname of the file represented by a File object with getPath(), the filename without any path information with getName(), and the directory name with getParent().

The File class also defines methods that return information about the actual file represented by a File object. Use exists() to check whether or not the file exists. isDirectory() and isFile() tell whether the file is a file or a directory. If the file is a directory, you can use list() to get an array of filenames for the files in that directory. The canRead() and canWrite() methods indicate whether or not a program is allowed to read from or write to a file. You can also retrieve the length of a file with length() and its last modified date with lastModified().

A few File methods allow you to change the information about a file. For example, you can rename a file with rename() and delete it with delete(). The mkdir() and mkdirs() methods provide a way to create directories within the filesystem.

Many of these methods can throw a SecurityException if a program does not have permission to access the filesystem, or particular files within it. If a SecurityManager has been installed, the checkRead() and checkWrite() methods of the SecurityManager verify whether or not the program has permission to access the filesystem.

FilenameFilter

The purpose of the FilenameFilter interface is to provide a way for an object to decide which filenames should be included in a list of filenames. A class that implements the FilenameFilter interface must define a method called accept(). This method is passed a File object that identifies a directory and a String that names a file. The accept() method is expected to return true if the specified file should be included in the list, or false if the file should not be included. Here is an example of a simple FilenameFilter class that only allows files with a specified suffix to be in a list:

import java.io.File;
import java.io.FilenameFilter;
public class SuffixFilter implements FilenameFilter {
    private String suffix;
    public SuffixFilter(String suffix) {
        this.suffix = "." + suffix;
    }
    public boolean accept(File dir, String name) {
        return name.endsWith(suffix);
    }
}

A FilenameFilter object can be passed as a parameter to the list() method of File to filter the list that it creates. You can also use a FilenameFilter to limit the choices shown in a FileDialog.

RandomAccessFile

The RandomAccessFile class provides a way to read from and write to a file in a nonsequential manner. The RandomAccessFile class has two constructors that both take two arguments. The first argument specifies the file to open, either as a String or a File object. The second argument is a String that must be either "r" or "rw". If the second argument is "r", the file is opened for reading only. If the argument is "rw", however, the file is opened for both reading and writing. The close() method closes the file. Both constructors and all the methods of the RandomAccessFile class can throw an IOException if they encounter an error.

The RandomAccessFile class defines three different read() methods for reading bytes from a file. The RandomAccessFile class also implements the DataInput interface, so it provides additional methods for reading from a file. Most of these additional methods are related to reading Java primitive types in a machine-independent way. Multibyte quantities are read assuming the most significant byte is first and the least significant byte is last. All of these methods handle an attempt to read past the end of file by throwing an EOFException.

The RandomAccessFile class also defines three different write() methods for writing bytes of output. The RandomAccessFile class also implements the DataOutput interface, so it provides additional methods for writing to a file. Most of these additional methods are related to writing Java primitive types in a machine-independent way. Again, multibyte quantities are written with the most significant byte first and the least significant byte last.

The RandomAccessFile class would not live up to its name if it did not provide a way to access a file in a nonsequential manner. The getFilePointer() method returns the current position in the file, while the seek() method provides a way to set the position. Finally, the length() method returns the length of the file in bytes.