6.2 Output Streams and WritersThe 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. OutputStreamThe 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. WriterThe 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. OutputStreamWriterThe 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 FileOutputStreamThe 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:
StringWriterThe 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 ByteArrayOutputStreamThe 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:
PipedOutputStream and PipedWriterThe 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 FilterWriterThe 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. DataOutputStreamThe 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 BufferedOutputStreamThe 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 PrintStreamThe 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.
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. |
|