Cover | Table of Contents
java.io
and java.nio
packages. These packages support several different styles of I/O. One distinction is between byte-oriented I/O, which is handled by input and output streams, and character-I/O, which is handled by readers and writers. Another distinction is between the old-style stream-based I/O and the new-style channel- and buffer-based I/O. These all have their place and are appropriate for different needs and use cases. None of them should be ignored.System.in
. Similarly, an output stream may have a definite number of bytes to output or an indefinite number of bytes.System.in. This is the same thing as stdin in C—generally some sort of console window, probably the one in which the Java program was launched. If input is redirected so the program reads from a file, then System.in is changed as well. For instance, on Unix, the following command redirects stdin so that when the MessageServer program reads from System.inint, a 4-byte, big-endian, two's complement integer. An int can take on all values between -2,147,483,648 and 2,147,483,647. When you type a literal integer such as 7, -8345, or 3000000000 in Java source code, the compiler treats that literal as an int. In the case of 3000000000 or similar numbers too large to fit in an int, the compiler emits an error message citing "Numeric overflow."long
s are 8-byte, big-endian, two's complement integers that range all the way from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807. long literals are indicated by suffixing the number with a lower- or uppercase L. An uppercase L is preferred because the lowercase l is too easily confused with the numeral 1 in most fonts. For example, 7L, -8345L, and 3000000000L are all 64-bit long literals.short
and the byte. shorts are 2-byte, big-endian, two's complement integers with ranges from -32,768 to 32,767. They're rarely used in Java and are included mainly for compatibility with C.bytes, however, are very much used in Java. In particular, they're used in I/O. A byte is an 8-bit, two's complement integer that ranges from −128 to 127. Note that like all numeric data types in Java, a byte is signed. The maximum byte value is 127. 128, 129, and so on through 255 are not legal values for bytes.byte value between 0 and 127 from a stream, then cast it to a char, the result would be the corresponding ASCII character.chars according to a specified encoding format before passing them along. Similarly, writers convert chars to bytes according to a specified encoding before writing them onto some underlying stream.java.io.Reader and java.io.Writer classes are abstract superclasses for classes that read and write character-based data. The subclasses are notable for handling the conversion between different character sets. The core Java API includes nine reader and eight writer classes, all in the java.io package:BufferedReader
BufferedWriter
CharArrayReader
CharArrayWriter
FileReader
FileWriter
FilterReader
FilterWriter
InputStreamReader
LineNumberReader
OutputStreamWriterIOException. IOException is a checked exception, so you must either declare that your methods throw it or enclose the call that can throw it in a try/catch block. The only real exceptions to this rule are the PrintStream and PrintWriter classes. Because it would be inconvenient to wrap a try/catch block around each call to System.out.println( ), Sun decided to have PrintStream (and later PrintWriter) catch and eat any exceptions thrown inside a print( ) or println( ) method. If you do want to check for exceptions inside a print( ) or println( ) method, you can call checkError( ):public boolean checkError( )
checkError( ) method returns true if an exception has occurred on this print stream, false if one hasn't. It tells you only that an error occurred. It does not tell you what sort of error occurred. If you need to know more about the error, you'll have to use a different output stream or writer class.IOException has many subclasses—15 in java.io alone—and methods often throw a more specific exception that subclasses IOException; for instance, EOFException on an unexpected end of file or UnsupportedEncodingException when you try read text in an unknown character set. However, methods usually declare only that they throw an System.out or System.err and the default source of input for System.in. On most platforms the console is the command-line environment from which the Java program was initially launched, perhaps an xterm or a DOS prompt as shown in Figure 1-1. The word console is something of a misnomer, since on Unix systems the console refers to a very specific command-line shell rather than to command-line shells overall.
System.out is the first instance of the OutputStream class most programmers encounter. In fact, it's often encountered before students know what a class or an output stream is. Specifically, System.out is the static out field of the java.lang.System class. It's an instance of SecurityException. It's not always obvious whether a particular method or class will cause problems. The write( )java.io.OutputStream class declares the three basic methods you need to write bytes of data onto a stream. It also has methods for closing and flushing streams:public abstract void write(int b) throws IOException public void write(byte[] data) throws IOException public void write(byte[] data, int offset, int length) throws IOException public void flush( ) throws IOException public void close( ) throws IOException
OutputStream is an abstract class. Subclasses provide implementations of the abstract write(int b) method. They may also override the four nonabstract methods. For example, the FileOutputStream class overrides all five methods with methods that call native code to write files. Although OutputStream is abstract, often you only need to know that the object you have is an OutputStream; the more specific subclass of OutputStream is hidden from you. For example, the getOutputStream( ) method of java.net.URLConnection
has this signature:public OutputStream getOutputStream( ) throws IOException
URLConnection object, the actual class of the output stream that's returned may be a sun.net.TelnetOutputStream, a sun.net.smtp.SmtpPrintStream, a sun.net.www.http.KeepAliveStream, or something else completely. All you know as a programmer, and all you need to know, is that the object returned is some kind of OutputStream.OutputStream. And since methods that are inherited are not included in the API documentation, it's important to remember that they're there. For example, the java.io.DataOutputStream class does not declare a close( ) method, but you can still call the method it inherits from its superclass.OutputStream class is write( )
:OutputStream class is write( )
:public abstract void write(int b) throws IOException
AsciiChart, is a simple program that writes the printable ASCII characters (32 to 126) on the console. The console interprets the numeric values as ASCII characters, not as numbers. This is a feature of the console, not of the OutputStream class or the specific subclass of which System.out is an instance. The write( ) method merely sends a particular bit pattern to a particular output stream. How that bit pattern is interpreted depends on what's connected to the other end of the stream.
import java.io.*;
public class AsciiChart {
public static void main(String[] args) {
for (int i = 32; i < 127; i++) {
System.out.write(i);
// break line after every eight characters.
if (i % 8 == 7) System.out.write('\n');
else System.out.write('\t');
}
System.out.write('\n');
}
}
char literals '\t' and '\n'. The compiler converts these to the numbers 9 and 10, respectively. When these numbers are written on the console, the console interprets them as a tab and a linefeed, respectively. The same effect could have been achieved by writing the if clause like this:if (i % 8 == 7) System.out.write(10); else System.out.write(9);
% java AsciiChart
! " # $ % & '
( ) * + , - . /
0 1 2 3 4 5 6 7
8 9 : ; < = > ?
@ A B C D E F G
H I J K L M N O
P Q R S T U V W
X Y Z [ \ ] ^ _
` a b c d e f g
h i j k l m n o
p q r s t u v w
x y z { | } ∼
write( ) method do this:public void write(byte[] data) throws IOException public void write(byte[] data, int offset, int length) throws IOException
byte array data. The second writes only the subarray of data starting at offset and continuing for length bytes. For example, the following code fragment blasts the bytes in a string onto System.out:String s = "How are streams treating you?"; byte[] data = s.getBytes( ); System.out.write(data);
write( ).
import java.io.*;
public class AsciiArray {
public static void main(String[] args) {
byte[] b = new byte[(127-31)*2];
int index = 0;
for (int i = 32; i < 127; i++) {
b[index++] = (byte) i;
// Break line after every eight characters.
if (i % 8 == 7) b[index++] = (byte) '\n';
else b[index++] = (byte) '\t';
}
b[index++] = (byte) '\n';
try {
System.out.write(b);
}
catch (IOException ex) {
System.err.println(ex);
}
}
}
close( ) method:public void close( ) throws IOException
out is an OutputStream, calling out.close( )
closes the stream and frees any underlying resources such as file handles or network ports associated with the stream.IOException, though there are a few classes where this doesn't happen.System.out is a partial exception because, as a PrintStream, all exceptions it throws are eaten. Once you close System.out, you can't write to it. Trying to do so won't throw any exceptions; however, your output will not appear on the console.
try {
OutputStream out = new FileOutputStream("numbers.dat");
// Write to the stream...
out.close( );
}
catch (IOException ex) {
System.err.println(ex);
}
IOException is thrown while writing, the stream won't be closed. It's more reliable to close the stream in a flush( )
method forces the data to be written whether or not the buffer is full:public void flush( ) throws IOException
flush( ). (Then sync( ) method in the FileDescriptor class, discussed in Chapter 17, can sometimes empty these buffers.)close( ) method is invoked. You flush an output stream explicitly only if you want to make sure data is sent before you're through with the stream. For example, a program that sends bursts of data across the network periodically should flush after each burst of data is written to the stream.flush( ) after each write, you can't be sure the data that appears in the output indicates the point at which the program crashed. In fact, the program may have continued to run for some time past that point before it crashed.System.out, System.err, and some (but not all) other print streams automatically flush after each call to println( ) and after each time a new line character ('\n') appears in the string being written. You can enable or disable auto-flushing in the PrintStream constructor.OutputStream is an abstract class that mainly describes the operations available with any OutputStream object. Specific subclasses know how to write bytes to particular destinations. For instance, a FileOutputStream uses native code to write data in files. A ByteArrayOutputStream uses pure Java to write its output in an expanding byte array.write( ) method in OutputStream, one abstract, two concrete:public abstract void write(int b) throws IOException public void write(byte[] data) throws IOException public void write(byte[] data, int offset, int length) throws IOException
write(int b) method. They often also override the third variant, write(byte[], data int offset, int length), to improve performance. The implementation of the three-argument version of the write( ) method in OutputStream simply invokes write(int b) repeatedly—that is:
public void write(byte[] data, int offset, int length) throws IOException {
for (int i = offset; i < offset+length; i++) write(data[i]);
}
write( ) merely invokes write(data, 0, data.length); if the three-argument variant has been overridden, this method will perform reasonably well. However, a few subclasses may override it anyway.NullOutputStream that mimics the behavior of /dev/null on Unix operating systems. Data written into a null output stream is lost.
package com.elharo.io;
import java.io.*;
public class NullOutputStream extends OutputStream {
private boolean closed = false;
public void write(int b) throws IOException {
if (closed) throw new IOException("Write to closed stream");
}
public void write(byte[] data, int offset, int length)
throws IOException {
if (data == null) throw new NullPointerException("data is null");
if (closed) throw new IOException("Write to closed stream");
}
public void close( ) {
closed = true;
}
}
javax.swing.JTextArea that can be connected to an output stream. As data is written onto the stream, it is appended to the text area in the default character set. (This isn't ideal. Since text areas contain text, a writer would be a better source for this data. In later chapters, I'll expand on this class to use a writer instead. For now, this makes a neat example.) This subclass is shown in Example 2-4.JStreamedTextArea class. Each JStreamedTextArea component contains a TextAreaOutputStream object in its theOutput field. Client programmers access this object via the getOutputStream( ) method. The JStreamedTextArea class has four overloaded constructors that imitate the four constructors in the javax.swing.JTextArea class, each taking a different combination of text, rows, and columns. The first three constructors merely pass their arguments and suitable defaults to the most general fourth constructor using this( ). The fourth constructor calls the most general superclass constructor, then calls setEditable(false) to ensure that the user doesn't change the text while output is streaming into it.
package com.elharo.io.ui;
import javax.swing.*;
import java.io.*;
public class JStreamedTextArea extends JTextArea {
private OutputStream theOutput = new TextAreaOutputStream( );
public JStreamedTextArea( ) {
this("", 0, 0);
}
public JStreamedTextArea(String text) {
this(text, 0, 0);
}
public JStreamedTextArea(int rows, int columns) {
this("", rows, columns);
}
public JStreamedTextArea(String text, int rows, int columns) {
super(text, rows, columns);
setEditable(false);
}
public OutputStream getOutputStream( ) {
return theOutput;
}
private class TextAreaOutputStream extends OutputStream {
private boolean closed = false;
public void write(int b) throws IOException {
checkOpen( );
// recall that the int should really just be a byte
b &= 0x000000FF;
// must convert byte to a char in order to append it
char c = (char) b;
append(String.valueOf(c));
}
private void checkOpen( ) throws IOException {
if (closed) throw new IOException("Write to closed stream");
}
public void write(byte[] data, int offset, int length)
throws IOException {
checkOpen( );
append(new String(data, offset, length));
}
public void close( ) {
this.closed = true;
}
}
}
java.io.InputStream is the abstract superclass for all input streams. It declares the three basic methods needed to read bytes of data from a stream. It also has methods for closing streams, checking how many bytes of data are available to be read, skipping over input, marking a position in a stream and resetting back to that position, and determining whether marking and resetting are supported.InputStream class is read( ). This method reads a single unsigned byte of data and returns the integer value of the unsigned byte. This is a number between 0 and 255:public abstract int read( ) throws IOException
read( ) is declared abstract; therefore, InputStream is abstract. Hence, you can never instantiate an InputStream directly; you always work with one of its concrete subclasses.System.in input stream and stores them in the int array data:
int[] data = new int[10];
for (int i = 0; i < data.length; i++) {
data[i] = System.in.read( );
}
read( ) is reading a byte, it returns an int. If you want to store the raw bytes instead, you can cast the int to a byte. For example:
byte[] b = new byte[10];
for (int i = 0; i < b.length; i++) {
b[i] = (byte) System.in.read( );
}
read( ) method (that is, a byte in the range −128 to 127 instead of 0 to 255). As long as you're clear in your mind and in your code about whether you're working with signed or unsigned data, you won't have any trouble. Signed bytes can be converted back to ints in the range of 0 to 255 like this:int i = (b >= 0) ? b : 256 + b;
read( ), you also have to catch the IOException that it might throw, or declare that your methods throw it. However, there's no IOExceptionInputStream class is read( ). This method reads a single unsigned byte of data and returns the integer value of the unsigned byte. This is a number between 0 and 255:public abstract int read( ) throws IOException
read( ) is declared abstract; therefore, InputStream is abstract. Hence, you can never instantiate an InputStream directly; you always work with one of its concrete subclasses.System.in input stream and stores them in the int array data:
int[] data = new int[10];
for (int i = 0; i < data.length; i++) {
data[i] = System.in.read( );
}
read( ) is reading a byte, it returns an int. If you want to store the raw bytes instead, you can cast the int to a byte. For example:
byte[] b = new byte[10];
for (int i = 0; i < b.length; i++) {
b[i] = (byte) System.in.read( );
}
read( ) method (that is, a byte in the range −128 to 127 instead of 0 to 255). As long as you're clear in your mind and in your code about whether you're working with signed or unsigned data, you won't have any trouble. Signed bytes can be converted back to ints in the range of 0 to 255 like this:int i = (b >= 0) ? b : 256 + b;
read( ), you also have to catch the IOException that it might throw, or declare that your methods throw it. However, there's no IOException if read( ) encounters the end of the input stream; in this case, it returns −1. You use this as a flag to watch for the end of stream. The following code fragment shows how to catch the IOException and test for the end of the stream:
try {
InputStream in = new FileInputStream("file.txt");
int[] data = new int[10];
for (int i = 0; i < data.length; i++) {
int datum = in.read( );
if (datum == −1) break;
data[i] = datum;
}
}
catch (IOException ex) {
System.err.println(ex.getMessage( ));
}
read( )
methods that read chunks of contiguous data into a byte array. The first variant tries to read enough data to fill the array. The second variant tries to read length bytes of data starting at position offset into the array. Neither of these methods is guaranteed to read as many bytes as you want. Both methods return the number of bytes actually read, or −1 on end of stream.public int read(byte[] data) throws IOException public int read(byte[] data, int offset, int length) throws IOException
java.io.InputStream class merely calls the basic read( ) method enough times to fill the requested array or subarray. Thus, reading 10 bytes of data takes 10 times as long as reading 1 byte of data. However, most subclasses of InputStream override these methods with more efficient methods, perhaps native, that read the data from the underlying source as a block.System.in, you could write the following code:
try {
byte[] b = new byte[10];
System.in.read(b);
}
catch (IOException ex) {
System.err.println("Couldn't read from System.in!");
}
read( ) throws an ArrayIndexOutOfBoundsException. For example, the following code loops repeatedly until it either fills the array or sees the end of stream:InputStream class's available( )
method tells you how many bytes you can read without blocking. It returns 0 if there's no data available to be read.public int available( ) throws IOException
try {
byte[] b = new byte[100];
int offset = 0;
while (offset < b.length) {
int a = System.in.available( );
int bytesRead = System.in.read(b, offset, a);
if (bytesRead == −1) break; // end of stream
offset += bytesRead;
}
catch (IOException ex) {
System.err.println("Couldn't read from System.in!");
}
available( ) returns, like this:
try {
byte[] b = new byte[System.in.available( )];
System.in.read(b);
}
catch (IOException ex) {
System.err.println("Couldn't read from System.in!");
}
available( ) method in java.io.InputStream always returns 0. Subclasses are supposed to override it, but I've seen a few that don't. You may be able to read more bytes from the underlying stream without blocking than available( ) suggests; you just can't guarantee that you can. If this is a concern, place input in a separate thread so that blocked input doesn't block the rest of the program.skip( )
method jumps over a certain number of bytes in the input:public long skip(long bytesToSkip) throws IOException
skip( ) is the number of bytes to skip. The return value is the number of bytes actually skipped, which may be less than bytesToSkip. −1 is returned if the end of stream is encountered. Both the argument and return value are longs, allowing skip( ) to handle extremely long input streams. Skipping is often faster than reading and discarding the data you don't want. For example, when an input stream is attached to a file, skipping bytes just requires that the position in the file be changed, whereas reading involves copying bytes from the disk into memory. For example, to skip the next 80 bytes of the input stream in:
try {
long bytesSkipped = 0;
long bytesToSkip = 80;
while (bytesSkipped < bytesToSkip) {
long n = in.skip(bytesToSkip - bytesSkipped);
if (n == −1) break;
bytesSkipped += n;
}
}
catch (IOException ex) {
System.err.println(ex);
}
close( )
method:public void close( ) throws IOException
IOException (though there are a few exceptions).System.in generally does not need to be closed, for example. However, streams associated with files and network connections should always be closed when you're done with them. As with output streams, it's best to do this in a finally block to guarantee that the stream is closed, even if an exception is thrown while the stream is open. For example:
// Initialize this to null to keep the compiler from complaining
// about uninitialized variables
InputStream in = null;
try {
URL u = new URL("http://www.msf.org/");
in = u.openStream( );
// Read from the stream...
}
catch (IOException ex) {
System.err.println(ex);
}
finally {
if (in != null) {
try {
in.close( );
}
catch (IOException ex) {
System.err.println(ex);
}
}
}
// Initialize this to null to keep the compiler from complaining
// about uninitialized variables
InputStream in = null;
try {
URL u = new URL("http://www.msf.org/");
in = u.openStream( );
// Read from the stream...
}
finally {
if (in != null) in.close( );
}
<, <<, or <<= until you've read one too many characters. It would be useful to be able to back up and reread the token once you know which token you've read.java.io.InputStream class handle marking
and resetting:public void mark(int readLimit) public void reset( ) throws IOException public boolean markSupported( )
markSupported( ) method returns true if this stream supports marking and false if it doesn't. If marking is not supported, reset( ) throws an IOException and mark( )
does nothing. Assuming the stream does support marking, the mark( ) method places a bookmark at the current position in the stream. You can rewind the stream to this position later with reset( ) as long as you haven't read more than readLimit bytes. There can be only one mark in the stream at any given time. Marking a second location erases the first mark.java.io that always support marking are BufferedInputStream (of which System.in is an instance) and ByteArrayInputStream.DataInputStream may support marking if they're chained to a buffered input stream first.Resettable interface that declares these three methods and then have subclasses implement that interface or not as they choose. You could then tell whether marking and resetting were supported with a simple InputStream must provide an implementation of the abstract read( ) method. They may also override some of the nonabstract methods. For example, the default markSupported( ) method returns false, mark( ) does nothing, and reset( ) throws an IOException. Any class that allows marking and resetting must override these three methods. Subclasses should also override available( ) to return something other than 0. Furthermore, they may override skip( ) and the other two read( ) methods to provide more efficient implementations.RandomInputStream that "reads" random bytes of data. This provides a useful source of unlimited data you can use in testing. A java.util.Random object provides the data.
package com.elharo.io;
import java.util.*;
import java.io.*;
public class RandomInputStream extends InputStream {
private Random generator = new Random( );
private boolean closed = false;
public int read( ) throws IOException {
checkOpen( );
int result = generator.nextInt( ) % 256;
if (result < 0) result = -result;
return result;
}
public int read(byte[] data, int offset, int length) throws IOException {
checkOpen( );
byte[] temp = new byte[length];
generator.nextBytes(temp);
System.arraycopy(temp, 0, data, offset, length);
return length;
}
public int read(byte[] data) throws IOException {
checkOpen( );
generator.nextBytes(data);
return data.length;
}
public long skip(long bytesToSkip) throws IOException {
checkOpen( );
// It's all random so skipping has no effect.
return bytesToSkip;
}
public void close( ) {
this.closed = true;
}
private void checkOpen( ) throws IOException {
if (closed) throw new IOException("Input stream closed");
}
public int available( ) {
// Limited only by available memory and the size of an array.
return Integer.MAX_VALUE;
}
}