Cell Phones
Pagers
Alarm Watches
etc.
and set your notebook's volume to zero
I really wanted to show people two things:
How to read a line of text from the console
How to do without printf()
Formatting numbers as strings and parsing numbers out of strings are separate operations.
Text is not assumed to be ASCII.
The console is assumed to be the least important target for I/O
Threads are used to perform non-blocking I/O
and more...
A sequence of bytes, generally external to the program
The console: System.in, System.out, System.err
Files
Network Connections
Java programs: byte array streams, piped streams, etc.
not GUIs
java.io.InputStream
java.io.OutputStream
java.io.Reader
java.io.Writer
From  BufferedInputStream  BufferedOutputStream  ByteArrayInputStream  ByteArrayOutputStream  DataInputStream  DataOutputStream  FileInputStream  FileOutputStream  FilterInputStream  FilterOutputStream  ObjectInputStream  ObjectOutputStream  PipedInputStream  PipedOutputStream  PrintStream  PushbackInputStream  SequenceInputStream  LineNumberInputStream  StringBufferInputStreamjava.io:
  
    
From  CheckedInputStream  CheckedOutputStream  InflaterInputStream  DeflaterOutputStream  GZIPInputStream  GZIPOutputStream  ZipInputStream  ZipOutputStream java.util.zip:
  
    
From  JarInputStream  JarOutputStream java.util.jar:
  
    
From  DigestInputStream  DigestOutputStream java.security:
  
    
From  CipherInputStream  CipherOutputStream javax.crypto:
  
    
Undocumented classes in the sun packages like
sun.net.TelnetInputStream and 
sun.net.TelnetOutputStream
Third party subclasses and subclasses you write yourself
Streams read and write bytes
The byte data type is signed
Java always promotes bytes to ints before working on them individually.
Many of the methods in the stream classes return or accept as arguments ints in the range of an unsigned byte (0-255).
The default destination for output written to 
    System.out or System.err, 
    and the default source of input for System.in.
Converts written bytes to characters
Cannot be put into raw mode
May or may not exist in an applet
import java.io.*;
public class WriteHello {
  public static void main(String[] args) throws IOException {
  
    byte[] hello = {72, 101, 108, 108, 111, 32, 87, 
                    111, 114, 108, 100, 33, 10, 13};
    System.out.write(hello);
  }
  
}
I/O is extremely limited in applets:
An applet cannot read a file.
An applet cannot write a file.
An applet cannot delete a file.
An applet cannot determine whether a file exists.
An applet cannot make a network connection to most hosts.
An applet cannot accept an incoming connection from an arbitrary host.
Java uses Unicode
char != byte
Most programs and operating systems don't use Unicode
When reading or writing text the text must be converted.
Therefore, reading and writing bytes is not the same as reading and writing text
 An OutputStream sends raw bytes of data to a 
target such as the console or a network server. 
OutputStream is an abstract class. 
 public abstract class OutputStream extends Object
Many methods in the 
class library are only specified to return OutputStream rather than 
the more specific subclass so you tend to 
use polymorphism.
Many of the methods of 
OutputStream are generally useful. These are:
 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
 The write() methods send raw bytes of data to whomever is 
 listening to this stream. 
import java.io.*;
public class HelloOutputStream {
  public static void main(String[] args) {
    String s = "Hello World\r\n";
    // Convert s to a byte array
    byte[] b = new byte[s.length()];
    s.getBytes(0, s.length()-1, b, 0);
    try {
      System.out.write(b);
      System.out.flush();
    }
    catch (IOException e) {
      System.err.println(e);
    }
  }
}
Sometimes output streams are buffered by the operating system for performance.
 The 
  flush() method forces the data to be written whether or not the 
  buffer is full.
This is not the same as the buffering performed by a 
BufferedOutputStream. That buffering is handled by the Java 
runtime. This buffering is at the native OS level. 
However, a call to  flush() should empty both buffers
The close() method closes the stream and  releases any resources 
associated with the stream.
 Once the stream is closed attempts to write to it 
throw IOExceptions.
You must implement
public abstract void write(int b) throws IOException
Expected semantics:
b is an int between 0 and 255
If b is not between 0 and 255, then the three high order 
  bytes of the int are thrown away.
You may override other methods for performance.
package com.macfaq.io;
import java.io.*;
public class NullOutputStream extends OutputStream {
  public void write(int b) {
  
  }
  public void write(byte[] data) {
  
  }
  public void write(byte[] data, int offset, int length) {
  }
}
java.io.InputStream is an abstract class that
contains the basic methods for reading raw bytes of data from a
stream.
Although InputStream is abstract,
many methods are only specified to return
an InputStream, so you'll often have to deal
directly with only the methods declared in this class. 
 public abstract int read() throws IOException
 public int read(byte[] data) throws IOException
 public int read(byte[] data, int offset, int length) throws IOException
 public long skip(long n) throws IOException
 public int available() throws IOException
 public void close() throws IOException
 public synchronized void mark(int readlimit)
 public synchronized void reset() throws IOException
 public boolean markSupported()
  public abstract int read() throws IOException
Reads a single unsigned byte of data
Returns an int value of between 0 and 255.
Returns -1 on end of stream
May block
import java.io.*;
public class Echo {
  public static void main(String[] args) {
  
    echo(System.in);
  
  }
  
  public static void echo(InputStream in) {
  
    try {
      while (true) {
        // Notice that although a byte is read, an int
        // with value between 0 and 255 is returned.
        // Then this is converted to an ISO Latin-1 char 
        // in the same range before being printed.   
        int i = in.read();
        // -1 is returned to indicate the end of stream
        if (i == -1) break;
        
        // without the cast a numeric string like "65"
        // would be printed instead of the character "A"
        char c = (char) i; 
        System.out.print(c);    
      }
    }
    catch (IOException e) {
      System.err.println(e); 
    }
    System.out.println();  
  
  }
}It's more efficient to read multiple bytes at a time:
 public int read(byte[] data) throws IOException
 public int read(byte[] data, int offset, int length) throws IOException
 
These methods block until there is some data available. Then
they read as many bytes as they can into b, or
until they've read length bytes.
Each returns the number of bytes actually read or -1 on end of stream.
The available() method tests how many bytes are
ready to be read from the stream without blocking.
 public int available() throws IOException
import java.io.*;
public class EfficientEcho {
  public static void main(String[] args) {
  
    echo(System.in);
  
  }
  
  public static void echo(InputStream in) {
  
    try {
      while (true) {
        int n = in.available();
        if (n > 0) {
          byte[] b = new byte[n];
          int result = in.read(b);
          if (result == -1) break;
          String s = new String(b);
          System.out.print(s); 
        } // end if   
      } // end while
    } // end try
    catch (IOException e) {
      System.err.println(e); 
    }
  
  }
}
The skip() method
reads a specified number of bytes and throws them away. 
 public int skip(long n) throws IOException
An example:
      case 171: // lookupswitch     
         pad = 3 - (position % 4);
         dis.skip(pad);
         defaultByte = dis.readInt();
         int npairs = dis.readInt();
         result = position + "    lookupswitch " + defaultByte + " " + npairs;
         for (int i = 0; i < npairs; i++) {
           int newPosition = position + pad + 12 + i*8;
           result += "\n" + newPosition + "    " 
            + dis.readInt() + " " + dis.readInt();
         }
The complete program is included in Chapter 5 of my book, Java Secrets, IDG Books, 1997.)
 public synchronized void mark(int readlimit)
 public synchronized void reset() throws IOException
 public boolean markSupported()
 markSupported() method returns
true if this stream supports marking and
false if it doesn't.
The
mark() method places a bookmark in the stream which
you can return to later using the reset() method.
There can be only one mark() in the stream at any
given time. Marking a second location erases the first mark.
 If
marking is not supported, these methods throw
IOExceptions.
The close() method closes the stream and  releases any resources 
associated with the stream.
 public void close() throws IOException 
 Once the stream is closed attempts to read from it 
throw IOExceptions.
You must implement
    public abstract int read() throws IOException
You may override others as well
import java.util.*;
import java.io.*;
public class RandomInputStream extends InputStream {
  private transient Random generator = new Random();
  public int read() {
    int result = generator.nextInt() % 256;
    if (result < 0) result = -result;
    return result;
  }
  public int read(byte[] data, int offset, int length) 
   throws IOException {
    byte[] temp = new byte[length];
    generator.nextBytes(temp);
    System.arraycopy(temp, 0, data, offset, length);
    return length;
  }
  public int read(byte[] data) throws IOException {
    generator.nextBytes(data);
    return data.length;
  }
  public long skip(long bytesToSkip) throws IOException {
  
    // It's all random so skipping has no effect
    return bytesToSkip;
  
  }
  
}
The java.io.FileOutputStream class represents an OutputStream that
writes bytes to a file. It has the following public methods:
 public FileOutputStream(String name) throws IOException
 public FileOutputStream(String name, boolean append) throws IOException
 public FileOutputStream(File file) throws IOException
 public FileOutputStream(FileDescriptor fdObj)
 public native 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 native void close() throws IOException
 public final FileDescriptor getFD() throws IOException
This example reads user input from System.in and writes it into 
the files specified on the command line. 
import java.io.*;
public class MultiType {
  public static void main(String[] args) {
    FileOutputStream[] fos = new FileOutputStream[args.length];
    for (int i = 0; i < args.length; i++) {
      try {
        fos[i] = new FileOutputStream(args[i]); 
      }
      catch (IOException e) {
        System.err.println(e); 
      }
    } // end for
    
    try {
       while (true) {
        int n = System.in.available();
        if (n > 0) {
          byte[] b = new byte[n];
          int result = System.in.read(b);
          if (result == -1) break;
          for (int i = 0; i < args.length; i++) {
            try {
              fos[i].write(b, 0, result); 
            }
            catch (IOException e) {
              System.err.println(e); 
            }
          } // end for
        } // end if   
      } // end while
    } // end try
    catch (IOException e) {
      System.err.println(e); 
    }
    for (int i = 0; i < args.length; i++) {
      try {
        fos[i].close(); 
       }
       catch (IOException e) {
         System.err.println(e); 
       }
    }
  } // end main
  
}It's often useful to be able to append data to an existing file rather
than overwriting it. To do this just pass the boolean value true 
as the second argument to the FileOutputStream() constructor.
For example,
FileOutputStream fos = new FileOutputStream("16.html", true);
import java.io.*;
public class Append {
  public static void main(String[] args) {
    FileOutputStream[] fos = new FileOutputStream[args.length];
    for (int i = 0; i < args.length; i++) {
      try {
        fos[i] = new FileOutputStream(args[i], true); 
      }
      catch (IOException e) {
        System.err.println(e); 
      }
    } // end for
    
    try {
       while (true) {
        int n = System.in.available();
        if (n > 0) {
          byte[] b = new byte[n];
          int result = System.in.read(b);
          if (result == -1) break;
          for (int i = 0; i < args.length; i++) {
            try {
              fos[i].write(b, 0, result); 
            }
            catch (IOException e) {
              System.err.println(e); 
            }
          } // end for
        } // end if   
      } // end while
    } // end try
    catch (IOException e) {
      System.err.println(e); 
    }
    for (int i = 0; i < args.length; i++) {
      try {
        fos[i].close(); 
      }
      catch (IOException e) {
        System.err.println(e); 
      }
    } // end for
  } // end main
  
}The FileInputStream class represents an 
InputStream that reads bytes from a file. It has the following public
methods:
 public FileInputStream(String filename) throws FileNotFoundException
 public FileInputStream(File file) throws FileNotFoundException
 public FileInputStream(FileDescriptor fdObj)
 public native int read() throws IOException
 public int read(byte[] data) throws IOException
 public int read(byte[] data, int offset, int length) throws IOException
 public native long skip(long n) throws IOException
 public native int available() throws IOException
 public native void close() throws IOException
 public final FileDescriptor getFD() throws IOException
 
The java.io.FilterInputStream and
java.io.FilterOutputStream classes are concrete
subclasses of InputStream and
OutputStream that somehow modify data read from an
underlying stream. You rarely use these classes directly, but
their subclasses are extremely important, especially
DataInputStream and DataOutputStream.
You connect filter streams to an underlying stream that supplies
the actual bytes of data by passing the original stream to the
filter stream's constructor. For example, to create a new
DataOutputStream from a
FileOutputStream you might do this:
FileOutputStream fos = new FileOutputStream("ln.txt");
DataOutputStream dos = new DataOutputStream(fos);
It's not uncommon to combine these into one line like this:
DataOutputStream dos = new DataOutputStream(new FileOutputStream("ln.txt"));
BufferedInputStream and BufferedOutputStream
DataInputStream and
      DataOutputStream 
      
      
PrintStream 
   
   
PushbackInputStream 
GZIPInputStream and GZIPOutputStream
DigestInputStream and DigestOutputStream
CipherInputStream and CipherOutputStream
ObjectInputStream and ObjectOutputStream
You can also create your own subclasses of
FilterInputStream and
FilterOutputStream that perform custom
filtering.
The java.io.BufferedInputStream and
java.io.BufferedOutputStream classes buffer reads
and writes by first storing the in a buffer (an internal array
of bytes). Then the program reads bytes from the stream without
calling the underlying native method until the buffer is empty.
The data is read from or written into the buffer in blocks;
subsequent accesses go straight to the buffer.  
The only real difference to the client between a regular stream and a buffered stream are the constructors:
 public BufferedInputStream(InputStream in)
 public BufferedInputStream(InputStream in, int size)
 public BufferedOutputStream(OutputStream out)
 public BufferedOutputStream(OutputStream out, int size)
The best size for the buffer is highly platform dependent and generally related to the block size of the disk, at least for file streams. Less than 512 bytes is probably too little and more than 4096 bytes is probably too much. Ideally you want an integral multiple of the block size of the disk. However, you should use smaller buffer sizes for unreliable network connections.
URL u = new URL("http://java.developer.com");
BufferedInputStream bis = new BufferedInputStream(u.openStream(), 256);
The PushbackInputStream class provides a pushback buffer so a 
program can "unread" bytes onto the stream. 
The next time data is read from the stream, the "unread" bytes are read.
 public void unread(int b) throws IOException
 public void unread(byte[] data, int offset, int length) throws IOException
 public void unread(byte[] data) throws IOException
By default the buffer is only one byte long, and trying to unread more than that 
throws an IOException. However you can change the default buffer 
size with the second constructor:
 public PushbackInputStream(InputStream in)
 public PushbackInputStream(InputStream in, int size)
 public void print(boolean b)
 public void print(int i)
 public void print(long l)
 public void print(float f)
 public void print(double d)
 public void print(char s[])
 public void print(String s)
 public void print(Object obj)
 public void println()
 public void println(boolean x)
 public void println(char x)
 public void println(int x)
 public void println(long x)
 public void println(float x)
 public void println(double x)
 public void println(char x[])
 public void println(String x)
 public void println(Object x)
This class traps all
IOExceptions. However you can test the error status with checkError(). This returns true if an error has occurred,
false otherwise.
 public boolean checkError()
Doesn't internationalize properly
Doesn't handle line breaks in a platform independent way.
DataInputStream and DataOutputStream 
 read and write primitive Java
data types and Strings in a machine-independent way.
IEEE 754 for floating point data
big-endian format for integer data
modified UTF-8 for Unicode data
DataOutputStream declares these methods:
 public DataOutputStream(OutputStream out)
 public synchronized void write(int b) throws IOException
 public synchronized void write(byte[] data, int offset, int length) 
  throws IOException
 public final void writeBoolean(boolean b) throws IOException
 public final void writeByte(int b) throws IOException
 public final void writeShort(int s) throws IOException
 public final void writeChar(int c) throws IOException
 public final void writeInt(int i) throws IOException
 public final void writeFloat(float f) throws IOException
 public final void writeDouble(double d) throws IOException
 public final void writeBytes(String s) throws IOException
 public final void writeChars(String s) throws IOException
 public final void writeUTF(String s) throws IOException
 public final int size()
 public void flush() throws IOException
DataInputStream has these methods:
 public DataInputStream(InputStream in)
 
 public final int read(byte[] input) throws IOException
 public final int read(byte[] input, int offset, int length) 
  throws IOException
 
 public final void readFully(byte[] input) throws IOException
 public final void readFully(byte[] input, int offset, int length) 
  throws IOException
  
 public final int skipBytes(int n) throws IOException
 public final boolean readBoolean() throws IOException
 public final byte readByte() throws IOException
 public final int readUnsignedByte() throws IOException
 public final short readShort() throws IOException
 public final int readUnsignedShort() throws IOException
 public final char readChar() throws IOException
 public final int readInt() throws IOException
 public final long readLong() throws IOException
 public final float readFloat() throws IOException
 public final double readDouble() throws IOException
 public final String readUTF() throws IOException
 public static final String readUTF(DataInput in) 
  throws IOException
 public final String readLine() throws IOException
Doesn't handle non-ASCII character sets well
Makes dangerous assumptions about line endings
import java.io.*;
import java.util.zip.*;
import com.macfaq.io.*;
public class GZipper {
  public final static String GZIP_SUFFIX = ".gz";
  public static void main(String[] args) {
    for (int i = 0; i < args.length; i++) {
      try {
        FileInputStream in = new FileInputStream(args[i]);      
        FileOutputStream fout = new FileOutputStream(args[i] + GZIP_SUFFIX);
        GZIPOutputStream out = new GZIPOutputStream(fout);
        byte[] buffer = new byte[256];
        while (true) {
          int bytesRead = in.read(buffer);
          if (bytesRead == -1) break;
          out.write(buffer, 0, bytesRead);
        }        
        out.close();
      }
      catch (IOException e) {
        System.err.println(e);     
      }
    }
  }
}
import java.io.*;
import java.util.zip.*;
import com.macfaq.io.*;
public class GUnzipper {
  public static void main(String[] args) {
    for (int i = 0; i < args.length; i++) {
      if (args[i].toLowerCase().endsWith(GZipper.GZIP_SUFFIX)) {
        try {
          FileInputStream fin = new FileInputStream(args[i]);      
          GZIPInputStream in = new GZIPInputStream(fin);
          FileOutputStream out = new FileOutputStream(
           args[i].substring(0, args[i].length()-3));
          byte[] buffer = new byte[256];
          while (true) {
            int bytesRead = in.read(buffer);
            if (bytesRead == -1) break;
            out.write(buffer, 0, bytesRead);
          }        
          out.close();
        }
        catch (IOException e) {
          System.err.println(e);     
        }
      }
      else {
        System.err.println(args[i] + " does not appear to be a gzipped file.");
      }
    }
  }
}
Must override:
public void write(int b) throws IOException
May override other methods as well for performance
package com.macfaq.io;
import java.io.*;
public class TeeOutputStream extends FilterOutputStream {
  private OutputStream out1;
  private OutputStream out2;
  public TeeOutputStream(OutputStream stream1, OutputStream stream2) {
    super(stream1);
    out1 = stream1;
    out2 = stream2;
  }
  public synchronized void write(int b) throws IOException {
    out1.write(b);
    out2.write(b);  
  }
  public synchronized void write(byte[] data, int offset, int length) 
   throws IOException {
    out1.write(data, offset, length);
    out2.write(data, offset, length);
  }
  public void flush() throws IOException {
    out1.flush();
    out2.flush();  
  }
  
  public void close() throws IOException {
    out1.close();
    out2.close();
  }
}
Must override:
public int read() throws IOException
May override other methods as well for performance
package com.macfaq.io;
import java.io.*;
public class PrintableInputStream extends FilterInputStream {
  public PrintableInputStream(InputStream in) {
    super(in);
  }
  public int read() throws IOException {
  
    int b = in.read();
    // printing, ASCII characters
    if (b >= 32 && b <= 126) return b;
    // carriage return, linefeed, tab, and end of file
    else if (b == 10 || b == 13 || b == 9 || b == -1) return b;
    // non-printing characters
    else return '?'; 
  }
  public int read(byte[] data, int offset, int length) throws IOException {
  
    int result = in.read(data, offset, length);
    for (int i = offset; i < offset+result; i++) {
      // do nothing with the printing characters
      // carriage return, linefeed, tab, and end of file
      if (data[i] == 10 || data[i] == 13 || data[i] == 9 || data[i] == -1) ;
      // non-printing characters
      else if (data[i] < 32 || data[i] > 126) data[i] = (byte) '?';
    }
    return result;
    
  }
}
Java uses Unicode
Unicode can be serialized in a variety of formats
UTF-8
UCS-2
UCS-4
Most files are in still some other encoding
ASCII
Latin-1
MacRoman
etc.
When you read and write text you must pay attention to character sets.
The 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.
public abstract class Reader extends Object
public abstract class Writer extends Object
The methods of the java.io.Writer class are
deliberately similar to the methods of the
java.io.OutputStream class. However rather than
working with bytes, they work with chars. 
The basic write() method writes a single two-byte
character with a value between 0 and 65535. The value is taken
from the two low-order bytes of the argument c. 
public void write(int c) throws IOException
You can also write an array of characters, a sub-array of characters, a String, or a substring.
 public void write(char[] text) throws IOException
 public abstract void write(char[] text, int offset, int length) throws IOException
 public void write(String s) throws IOException
 public void write(String s, int offset, int length) throws IOException
Like output streams, writers may be buffered. To force the write
to take place, call flush():
public abstract void flush() throws IOException
Finally the close() method closes the
Writer and releases any resources associated with
it.
    
 public abstract void close() throws IOException
The java.io.OutputStreamWriter class connects byte
streams and character streams. 
 
 public OutputStreamWriter(OutputStream out, String enc) 
  throws UnsupportedEncodingException
 public OutputStreamWriter(OutputStream out)
For example, if you wanted to write a file encoded in the Macintosh Symbol font, you might do this:
FileOutputStream fout = new FileOutputStream("symbol.txt");
OutputStreamWriter osw = new OutputStreamWriter(fout, "MacSymbol");
  
 The other methods just override methods from java.io.Writer, but behave
   identically from the perspective of the programmer.
 public void write(int c) throws IOException
 public void write(char c[], int offset, int length) throws IOException
 public void write(String s, int offset, int length) throws IOException
 public void flush() throws IOException
 public void close() throws IOException
The methods of the java.io.Reader class
are deliberately similar to the methods of the java.io.InputStream class. 
The basic read() method reads a single character 
(which may may take between one and four bytes depending on the character set) 
and returns the character as an int between 0 and 65535. 
 It returns -1 if the end of stream is seen. 
public int read() throws IOException
You can also read many characters into an array of chars.
 public int read(char[] text) throws IOException
 public abstract int read(char[] text, int offset, int length) 
  throws IOException
All the read() methods block until some input is
 available, an I/O error occurs, or the end of the stream is reached. 
You can skip a certain number of characters.
 public long skip(long n) throws IOException
The ready() method returns true if the reader is
ready to be read from, false if it isn't.
 public boolean ready() throws IOException
Readers may or may not support marking and resetting, like input streams.
 public boolean markSupported()
 public void mark(int readAheadLimit) throws IOException
 public void reset() throws IOException
 Finally the close() method closes the
Reader and releases any resources associated with
it.
    
 public abstract void close() throws IOException
The java.io.InputStreamReader class serves as a
bridge between byte streams and character streams: It reads
bytes from the input stream and translates them into characters
according to a specified character encoding.
The encoding can be set in the constructor, or you can accept the platform's default encoding.
 public InputStreamReader(InputStream in)
 public InputStreamReader(InputStream in, String encoding) 
  throws UnsupportedEncodingException
For example, if you wanted to read a file that had been encoded using the Macintosh Symbol font, you might do this:
FileInputStream fin = new FileInputStream("symbol.txt");
InputStreamReader reader = new InputStreamReader(fin, "MacSymbol");
The other methods just override methods from java.io.Reader, 
but behave identically from the perspective of the programmer:
 public int read() throws IOException
 public int read(char c[], int off, int length) throws IOException
 public boolean ready() throws IOException
 public void close() throws IOException
BufferedReader 
BufferedWriter 
CharArrayReader 
CharArrayWriter 
FileReader 
FileWriter 
FilterReader 
FilterWriter 
InputStreamReader 
LineNumberReader 
PipedReader 
PipedWriter 
PrintWriter 
PushbackReader 
StringReader 
StringWriter 
A subclass of
 java.io.Writer allows you to use the familiar
print() and println() methods
Can be chained to an OutputStreamWriter to handle non-default character
    sets properly.
Automatic flushing is performed only when println() is invoked, not every time a newline character is seen.
println() is still dangerous
PrintStream is unofficially deprecated
 public PrintWriter(Writer out)
 public PrintWriter(Writer out, boolean autoFlush)
 public PrintWriter(OutputStream out)
 public PrintWriter(OutputStream out, boolean autoFlush)
 public void flush()
 public void close()
 public boolean checkError()
 protected void setError()
 public void write(int c)
 public void write(char buf[], int offset, int length)
 public void write(char buf[])
 public void write(String s,
 public void write(String s)
 public void print(boolean b)
 public void print(char c)
 public void print(int i)
 public void print(long l)
 public void print(float f)
 public void print(double d)
 public void print(char s[])
 public void print(String s)
 public void print(Object obj)
 public void println()
 public void println(boolean x)
 public void println(char x)
 public void println(int x)
 public void println(long x)
 public void println(float x)
 public void println(double x)
 public void println(char x[])
 public void println(String x)
 public void println(Object x)
A subclass of
 java.io.Reader that you chain to another Reader class to 
buffer characters.
Constructor can specify buffer size
  public BufferedReader(Reader in, int bufferSize)
  public BufferedReader(Reader in)
 Also notable for its readLine() method 
that allows you to read text a line at a time.
 public String readLine() throws IOException
readLine() is still evil!
BufferedReaders do support marking and resetting, at least 
up to the
length of the buffer.
// Implement the Unix cat utility in java
import java.io.*;
class cat  {
  public static void main (String args[]) {
  
    String thisLine;
 
   //Loop across the arguments
   for (int i=0; i < args.length; i++) {
 
     //Open the file for reading
     try {
       BufferedReader br = new BufferedReader(new FileReader(args[i]));
       while ((thisLine = br.readLine()) != null) { // while loop begins here
         System.out.println(thisLine);
       } // end while 
     } // end try
     catch (IOException e) {
       System.err.println("Error: " + e);
     }
  } // end for
  
} // end main
}
Very much like FilterInputStream and FilterOutputStream
You must override the three-args read and write methods:
public int read(char[] text, int offset, int length) throws IOException 
public void write(char[] text, int offset, int length) throws IOException 
C:
scanf("%d", &x);
C++:
cin >> x;
Pascal:
READLN (X);
Fortran:
      READ 2, X
    2 FORMAT (F5.1)
C:
printf("%.2d", salary);
C++:
cout.precision(2);
cout << salary;Fortran:
      PRINT 20, SALARY
   20 FORMAT(F10.2)
I/O is not at all the same thing as converting a number to a string
First convert the number to a string
Then output the string
Much more flexible
java.util.Locale objects encapsulate differences between 
different cultures, languages, and countries
Predefined locales:
Locale.SIMPLIFIED_CHINESE
Locale.CHINA
Locale.PRC
Locale.TRADITIONAL_CHINESE
Locale.TAIWAN
Locale.CANADA
Locale.UK
Locale.US
Locale.FRANCE 
Locale.CANADA_FRENCH
Locale.GERMANY
Locale.ITALY
Locale.JAPAN
Locale.KOREA
Locale.ENGLISH
Locale.FRENCH
Locale.GERMAN
Locale.ITALIAN
Locale.JAPANESE
Locale.KOREAN
Locale.CHINESE
Number formats are locale specific
Number formats specify
maximum and minimum integer width
maximum and minimum fraction width (precision, number of decimal places)
whether or not digits are grouped (e.g. 2,109,356 vs. 2109356)
what character digits are grouped with
decimal separator
NumberFormat myFormat = NumberFormat.getInstance();
NumberFormat canadaFormat = NumberFormat.getInstance(Locale.CANADA);
Locale turkey = new Locale("tr", "");
NumberFormat turkishFormat = NumberFormat.getInstance(turkey);
Locale swissItalian = new Locale("it", "CH");
NumberFormat swissItalianFormat = NumberFormat.getInstance(swissItalian);
A NumberFormat object converts integers and floating 
point numbers using one of NumberFormat's five overloaded 
format() methods:
public final String format(long number)
public final String format(double number)
public abstract StringBuffer format(long number, StringBuffer toAppendTo, 
 FieldPosition pos)
public abstract StringBuffer format(double number, StringBuffer toAppendTo, 
 FieldPosition pos)
public final StringBuffer format(Object number, StringBuffer toAppendTo, 
 FieldPosition pos)
import java.text.*;
public class FormatTest {
  public static void main(String[] args) {
    NumberFormat nf = NumberFormat.getInstance();
    for (double x = Math.PI; x < 100000; x *= 10) {
      String formattedNumber = nf.format(x);
      System.out.println(formattedNumber + "\t" + x);
    }
  }
}
3.141        3.14159265358979
31.415      31.4159265358979
314.159     314.159265358979
3,141.592       3141.5926535897897
31,415.926  31415.926535897896
3,141        3.14159265358979
31,415      31.4159265358979
314,159     314.159265358979
3 141,592       3141.5926535897897
31 415,926  31415.926535897896
You specify the minimum and maximum of each type you want in each number using these four methods:
public void setMaximumIntegerDigits(int newValue)
public void setMinimumIntegerDigits(int newValue)
public void setMaximumFractionDigits(int newValue)
public void setMinimumFractionDigits(int newValue)
For example, to specify that myFormat should format 
numbers with at least 10 digits before the decimal point and at most  
3 digits after, you would type:
myFormat.setMinimumIntegerDigits(10);
myFormat.setMaximumFractionDigits(3);
import java.text.*;
public class PrettyTable {
  public static void main(String[] args) {
  
    System.out.println("Degrees Radians Grads");
    NumberFormat myFormat = NumberFormat.getInstance();
    myFormat.setMinimumIntegerDigits(3);
    myFormat.setMaximumFractionDigits(2);
    myFormat.setMinimumFractionDigits(2);
    for (double degrees = 0.0; degrees < 360.0; degrees++) {
      String radianString = myFormat.format(Math.PI * degrees / 180.0);
      String gradString = myFormat.format(400 * degrees / 360);
      String degreeString = myFormat.format(degrees);
      System.out.println(degreeString + "  " + radianString 
       + "  " + gradString);
    }
    
  }
}
Output:
300.00  005.23  333.33
301.00  005.25  334.44
302.00  005.27  335.55
303.00  005.28  336.66
304.00  005.30  337.77
305.00  005.32  338.88
306.00  005.34  340.00
307.00  005.35  341.11
308.00  005.37  342.22
309.00  005.39  343.33
310.00  005.41  344.44
311.00  005.42  345.55
312.00  005.44  346.66
313.00  005.46  347.77
314.00  005.48  348.88
Most number formats support grouping and some use it by default.
Ask whether a particular NumberFormat
groups with the isGroupingUsed() method:
public boolean isGroupingUsed()
You can turn grouping on or off for a number format with the 
setGroupingUsed() method.
public void setGroupingUsed(boolean groupNumbers)
If you know you're going to be working with money, you can request a
currency formatter with the static 
NumberFormat.getCurrencyInstance() method:
public static final NumberFormat getCurrencyInstance()
public static NumberFormat getCurrencyInstance(Locale inLocale)
import java.text.*;
import java.util.*;
public class MinimumWage {
  public static void main(String[] args) {
  
    NumberFormat dollarFormat = NumberFormat.getCurrencyInstance(Locale.ENGLISH);
    double minimumWage = 5.15;
    
    System.out.println("The minimum wage is " 
     + dollarFormat.format(minimumWage));
    System.out.println("A worker earning minimum wage and working for forty");
    System.out.println("hours a week, 52 weeks a year, would earn " 
     + dollarFormat.format(40*52*minimumWage));
    
  }
}
This program prints
The minimum wage is $5.15
A worker earning minimum wage and working for forty 
hours a week, 52 weeks a year, would earn $10,712.00
Number formats are also responsible for converting strings to binary numbers.
 Number formats provide more flexible conversions than 
you can achieve with the methods in the type wrapper classes like 
Integer.parseInt().
 For instance, a percent format parse() method 
can interpret 57% as 0.57 instead of 57. 
A currency format can read (12.45) as -12.45.
Understand grouping
public Number parse(String text) throws ParseException
public abstract Number parse(String text, ParsePosition parsePosition)
public final Object parseObject(String source, ParsePosition parsePosition)
import java.text.*;
import java.io.*;
public class RootFinder {
  public static void main(String[] args) {
  
    Number input = null;
    
    try {
      BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
      NumberFormat nf = NumberFormat.getInstance();
      while (true) {
        System.out.println("Enter a number (-1 to quit): ");
        String s = br.readLine();
        try {
          input = nf.parse(s);
        }
        catch (ParseException e) {
          System.out.println(s + " is not a number I understand.");
          continue;
        }
        double d = input.doubleValue();
        if (d < 0) break;
        double root = Math.sqrt(d);
        System.out.println("The square root of " + s + " is " + root);
      }
    }
    catch (IOException e) {
      System.err.println(e);  
    }
    
  }
}
% java RootFinder Enter a number (-1 to quit): 87 The square root of 87 is 9.327379053088816 Enter a number (-1 to quit): 65.4 The square root of 65.4 is 8.087026647662292 Enter a number (-1 to quit): 3.151592 The square root of 3.151592 is 1.7752723734683644 Enter a number (-1 to quit): 2,345,678 The square root of 2,345,678 is 1531.5606419596973 Enter a number (-1 to quit): 2.998E+8 The square root of 2.998E+8 is 1.7314733610425546 Enter a number (-1 to quit): 299800000 The square root of 299800000 is 17314.733610425545 Enter a number (-1 to quit): 0.0 The square root of 0.0 is 0.0 Enter a number (-1 to quit): four four is not a number I understand. Enter a number (-1 to quit): 4 The square root of 4 is 2.0 Enter a number (-1 to quit): Enter a number (-1 to quit): (12) (12) is not a number I understand. -1
Scientific and engineering notation like 2.998E10, 6.022E23, 931.494013E6, 1.67262158E-27
Only supported in Java 1.3 and later; and then only by affixing E0
  to the DecimalFormatSymbols pattern
In Java 1.2 and earlier, you can roll your own by 
  subclassing NumberFormat
IBM alphaWorks NumberFormat class at 
  http://www.alphaworks.ibm.com/tech/numberformat
Memory mapped I/O
Asynchronous I/O
Direct access to character converter classes
Better support for regular expressions
USB API

Java I/O
Elliotte Rusty Harold
O'Reilly & Associates, 1999
ISBN: 01-56592-485-1
This presentation: http://www.ibiblio.org/javafaq/slides/intljava/2001ny/javaio/