<?xml version="1.0"?>
<!DOCTYPE presentation [
  <!NOTATION TXT SYSTEM "text/plain">
  <!ENTITY song.css SYSTEM "song.css" NDATA TXT>
  <!ENTITY song.css SYSTEM "song.css" NDATA TXT>
]>
<presentation>

  <title>Java I/O</title>
  <date>March 22, 2000</date>
  <host>SD2000 West</host>
  <copyright>2000 Elliotte Rusty Harold</copyright>
  <description>
    A slide from Elliotte Rusty Harold's presentation 
    Java I/O at SD2000 West, March 22, 2000
  </description>
  <last_modified>March 17, 2000</last_modified>
  <author_name>Elliotte Rusty Harold</author_name>
  <author_url>http://www.macfaq.com/personal.html</author_url>
  <author_email>elharo@metalab.unc.edu</author_email>
  <abstract>
    Elliotte Rusty Harold's presentation 
  Java I/O at SD2000 West, March 22, 2000
  </abstract>
  <description>
  Elliotte Rusty Harold's presentation 
  Java I/O at SD2000 West, March 22, 2000
  </description>

<slide>
  <title>Java I/O</title>
  
  <h2 align="center">Elliotte Rusty Harold</h2>
  <h2 align="center">SD2000 West March 22, 2000</h2>
  <h3 align="center">elharo@metalab.unc.edu</h3>
  <h3 align="center">http://metalab.unc.edu/javafaq/</h3>

  <description>
    A slide from Elliotte Rusty Harold's presentation 
    Java I/O at SD2000 West, March 22, 2000
  </description>
  <last_modified>January 22, 2000</last_modified>
</slide>

<slide>
  <title>How I Got Interested in Java I/O</title>
  <note>
  <ol>
  <li>seems like a pretty boring, straight-forward topic</li>
  <li>I wrote JNP and discovered that a lot of questions were 
  about I/O
  </li>
  <li>Decided to write a quick 2
  month, 200 page book about inpout and output in Java</li>
  <li>6 months and 600 pages later...</li>
  </ol>
  </note>
  
  <p>I really wanted to show people two things:</p>
  <ol>
  <li>How to read a line of text from the console</li>
  <li>How to do without <code>printf()</code></li>
  </ol>

 
  
  <description>
    A slide from Elliotte Rusty Harold's presentation 
    Java I/O at SD2000 West, March 22, 2000
  </description>
  <last_modified>January 22, 2000</last_modified>
</slide>

<slide>
  <title>The Main Theme:</title>
 
  
  <h2>Input and output in Java isn't weaker than 
  input and output in C; just different.</h2>

  <h2>How is it different?</h2>
  <ul>
  <li>Formatting numbers as strings and parsing numbers out of
  strings are separate operations.</li>
  <li>Text is not assumed to be ASCII.</li>
  <li>The console is assumed to be
   the least important target for I/O</li>
  <li>Threads are used to perform non-blocking I/O</li>  
  <li>and more...</li>  
  </ul>
 
  
  <description>
    A slide from Elliotte Rusty Harold's presentation 
    Java I/O at SD2000 West, March 22, 2000
  </description>
  <last_modified>January 28, 2000</last_modified>
</slide>

<slide>
  <title>What is a stream?</title>
  
  <ul>
    <li>A sequence of bytes, generally external to the program</li>
  </ul>
  
  <description>
    A slide from Elliotte Rusty Harold's presentation 
    Java I/O at SD2000 West, March 22, 2000
  </description>
  <last_modified>March 17, 2000</last_modified>
</slide>

<slide>
  <title>Where do streams come from</title>
  
  <ul>
  <li>The console: <code>System.in</code>, <code>System.out</code>, <code>System.err</code></li>
  <li>Files</li>
  <li>Network Connections</li>
  <li>Java programs: byte array streams, piped streams, etc.</li>
  <li><em>not</em> GUIs</li>
  </ul>
  
  <description>
    A slide from Elliotte Rusty Harold's presentation 
    Java I/O at SD2000 West, March 22, 2000
  </description>
  <last_modified>March 17, 2000</last_modified>
</slide>

 
 
<slide>
  <title>The Base Classes</title>
  
  <ul>
  <li><code>java.io.InputStream</code></li>
  <li><code>java.io.OutputStream</code></li>
  <li><code>java.io.Reader</code></li>
  <li><code>java.io.Writer</code></li>
  </ul>
  
  <description>
    A slide from Elliotte Rusty Harold's presentation 
    Java I/O at SD2000 West, March 22, 2000
  </description>
  <last_modified>March 17, 2000</last_modified>
</slide>
 
<slide>
  <title>The Stream Subclasses</title>
  
  <ul>
  <li>From <code>java.io</code>:
  <code><ul>
	<li>BufferedInputStream </li>
	<li>BufferedOutputStream </li>
      	<li>ByteArrayInputStream </li>
      	<li>ByteArrayOutputStream </li>
      	<li>DataInputStream </li>
      	<li>DataOutputStream </li>
      	<li>FileInputStream </li>
      	<li>FileOutputStream </li>
      	<li>FilterInputStream </li>
      	<li>FilterOutputStream </li>
      	<li>ObjectInputStream </li>
      	<li>ObjectOutputStream </li>
      	<li>PipedInputStream </li>
      	<li>PipedOutputStream </li>
      	<li>PrintStream </li>
      	<li>PushbackInputStream </li>
      	<li>SequenceInputStream </li>
      	<li>LineNumberInputStream </li>
      	<li>StringBufferInputStream</li>
</ul></code>


<note>last two deprecated</note>

</li>

  <li>From <code>java.util.zip</code>:
  <code><ul>
	<li>CheckedInputStream </li>
	<li>CheckedOutputStream </li>
      	<li>InflaterInputStream </li>
      	<li>DeflaterOutputStream </li>
      	<li>GZIPInputStream </li>
      	<li>GZIPOutputStream </li>
      	<li>ZipInputStream </li>
      	<li>ZipOutputStream </li>
</ul></code></li>

  <li>From <code>java.util.jar</code>:
  <code><ul>
	<li>JarInputStream </li>
	<li>JarOutputStream </li>
</ul></code></li>

  <li>From <code>java.security</code>:
  <code><ul>
	<li>DigestInputStream </li>
	<li>DigestOutputStream </li>
</ul></code></li>

  <li>From <code>javax.crypto</code>:
  <code><ul>
	<li>CipherInputStream </li>
	<li>CipherOutputStream </li>
</ul></code></li>

<li>Undocumented classes in the sun packages like
<code>sun.net.TelnetInputStream</code> and 
<code>sun.net.TelnetOutputStream</code></li>

<li>Third party subclasses and subclasses you write yourself</li>

</ul>


  <description>
    A slide from Elliotte Rusty Harold's presentation 
    Java I/O at SD2000 West, March 22, 2000
  </description>
  <last_modified>January 28, 2000</last_modified>
</slide>

<slide>
  <title>Numeric data</title>
  
  <ul>
  <li>Streams read and write bytes</li>
  <li>The <code>byte</code> data type is signed</li>
  <li>Java always promotes bytes to ints before working
  on them individually.</li>
  <li> Many of the methods in the stream classes 
   return or accept as arguments 
  ints in the range of an unsigned byte (0-255). </li>
  </ul>
  
  <description>
    A slide from Elliotte Rusty Harold's presentation 
    Java I/O at SD2000 West, March 22, 2000
  </description>
  <last_modified>January 28, 2000</last_modified>
</slide>

 
 
<slide>
  <title>The console</title>
  
  <note>show console,
  run WriteHello
  discuss problems that arise thorugh learning about I/O through console
  </note>
  
  <ul>
    <li>The default destination for output written to 
    <code>System.out</code> or <code>System.err</code>, 
    and the default source of input for <code>System.in</code>.</li>
    <li>Converts written bytes to characters</li>
    <li>Cannot be put into raw mode</li>
    <li>May or may not exist in an applet</li>
  </ul>
  
<pre><code>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);

  }
  
}</code>
</pre>
  
  <description>
    A slide from Elliotte Rusty Harold's presentation 
    Java I/O at SD2000 West, March 22, 2000
  </description>
  <last_modified>March 17, 2000</last_modified>
</slide>


 
<slide>
  <title>Security Issues</title>
  
  <p>I/O is extremely limited in applets:</p>
  
  <ul>
  <li>An applet cannot read a file.</li>
  <li>An applet cannot write a file.</li>
  <li>An applet cannot delete a file.</li>
  <li>An applet cannot  determine whether a file exists.</li>
  <li>An applet cannot  make a network connection to most  hosts.</li>
 <li>An applet cannot  accept an incoming connection from an arbitrary host.</li>
  </ul>
  
  <description>
    A slide from Elliotte Rusty Harold's presentation 
    Java I/O at SD2000 West, March 22, 2000
  </description>
  <last_modified>January 28, 2000</last_modified>
</slide>

 
 
<slide>
  <title>Character data</title>
  
  <ul>
  <li>Java uses Unicode</li>
  <li>char != byte</li>
  <li>Most programs and operating systems don't use Unicode</li>
  <li>When reading or writing text the text must be converted. </li>
  <li>Therefore, reading and writing bytes is not the same as reading and writing text</li>
  </ul>
  
  <description>
    A slide from Elliotte Rusty Harold's presentation 
    Java I/O at SD2000 West, March 22, 2000
  </description>
  <last_modified>January 28, 2000</last_modified>
</slide>
<slide>
  <title>The OutputStream class </title>

<ul>
<li> An <code>OutputStream</code> sends raw bytes of data to a 
target such as the console or a network server. </li> 
<li><code>OutputStream</code> is an abstract class. 



<p><code> public abstract class OutputStream extends Object
</code></p>
</li>
<li>Many methods in the 
class library are only specified to return <code>OutputStream</code> rather than 
the more specific subclass so you tend to 
use polymorphism.</li>
<li>Many of the methods of 
<code>OutputStream</code> are generally useful. These are:

<pre><code> 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
</code></pre>
</li>
<li> The <code>write()</code> methods send raw bytes of data to whomever is 
 listening to this stream. </li>
</ul>
  
  <description>
    A slide from Elliotte Rusty Harold's presentation 
    Java I/O at SD2000 West, March 22, 2000
  </description>
  <last_modified>March 17, 2000</last_modified>
</slide>

 
 
<slide>
  <title>Writing Bytes to Output Streams</title>
  
<pre><code>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);
    }

  }

}</code>
</pre>
  
  <description>
    A slide from Elliotte Rusty Harold's presentation 
    Java I/O at SD2000 West, March 22, 2000
  </description>
  <last_modified>January 22, 2000</last_modified>
</slide>
 
 
<slide>
  <title>Flushing Output Streams</title>
  
  <ul>
<li>  Sometimes output streams are buffered by the operating system for performance.  
<note>  In other words, rather than writing each byte as it's written the bytes are 
  accumulated in a buffer ranging from several bytes to several thousand bytes. 
  Then, when the buffer fills up, all the data is written at once.</note></li>
  
  <li> The 
  <code>flush()</code> method forces the data to be written whether or not the 
  buffer is full.</li>

<li>This is <b>not</b> the same as the buffering performed by a 
<code>BufferedOutputStream</code>. That buffering is handled by the Java 
runtime. This buffering is at the native OS level. 
However, a call to <code> flush()</code> should empty both buffers</li>
  </ul>
  
  <description>
    A slide from Elliotte Rusty Harold's presentation 
    Java I/O at SD2000 West, March 22, 2000
  </description>
  <last_modified>March 17, 2000</last_modified>
</slide>

<slide>
  <title>Closing Output Streams</title>


<p>The <code>close()</code> method closes the stream and  releases any resources 
associated with the stream. Once the stream is closed attempts to write to it 
throw <code>IOException</code>s.</p>
  
  <description>
    A slide from Elliotte Rusty Harold's presentation 
    Java I/O at SD2000 West, March 22, 2000
  </description>
  <last_modified>January 28, 2000</last_modified>
</slide>

 
 
<slide>
  <title>Subclassing <code>OutputStream</code></title>
  
  <ul>
  <li>You must implement 
  
<p><code>public abstract void write(int b) throws IOException
</code></p>
  </li>
  
  <li>Expected semantics:
  <ol>
  <li>b is an int between 0 and 255</li>
  <li>If b is not between 0 and 255, then the three high order 
  bytes of the int are thrown away.</li>
  </ol>
  </li>
  <li>You may override other methods for performance.</li>
  </ul>
  
<pre><code>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) {

  }

}</code>
</pre>
  
  
  <description>
    A slide from Elliotte Rusty Harold's presentation 
    Java I/O at SD2000 West, March 22, 2000
  </description>
  <last_modified>March 17, 2000</last_modified>
</slide>


 
<slide>
  <title>The InputStream Class</title>
  
<ul><li><code>java.io.InputStream </code>is an abstract class that
contains the basic methods for reading raw bytes of data from a
stream.</li> 

<li>Although <code>InputStream</code> is abstract,
many methods are only specified to return
an <code>InputStream</code>, so you'll often have to deal
directly with only the methods declared in this class. </li>
</ul>

<pre><code> 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()
 </code></pre>

<note>Notice that almost all these methods can throw an
<code>IOException</code>. This is true of pretty much anything
to do with input and output. The only exception is the
<code>PrintStream</code> class which eats all exceptions.
</note>

  
  <description>
    A slide from Elliotte Rusty Harold's presentation 
    Java I/O at SD2000 West, March 22, 2000
  </description>
  <last_modified>March 17, 2000</last_modified>
</slide>

 
 
<slide>
  <title>The <code>read()</code> method</title>
  
<pre><code> public abstract int read() throws IOException
</code></pre>  

<ul>
 <li>Reads a single unsigned byte of data</li>
<li>Returns an int value of between 0 and 255. </li>
<li>Returns -1 on end of stream</li>
<li>May block</li>
</ul>

  
  <description>
    A slide from Elliotte Rusty Harold's presentation 
    Java I/O at SD2000 West, March 22, 2000
  </description>
  <last_modified>January 28, 2000</last_modified>
</slide>

<slide>
  <title>Example of the <code>read()</code> method</title>
  
  
<note>Here's a simple program that echoes back what the user types at the command line. The byte is
cast to its equivalent in the ISO Latin-1 character set before being printed. This program does
not properly handle Unicode. In general, input and output streams do not properly handle
Unicode data. Therefore you should use them only for raw data and use the java.io.Reader and
java.io.Writer classes for text data, especially non-ASCII data.
</note>
<note> Note that as a general rule on most platforms characters
are only sent to System.in a line at a time, not as each character
is typed. This allows the user to backspace over mistakes and 
correct them.  Java does not allow you to put the console into
"raw" mode.  </note>

<pre><code>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();  
  
  }

}</code></pre>
  
  <description>
    A slide from Elliotte Rusty Harold's presentation 
    Java I/O at SD2000 West, March 22, 2000
  </description>
  <last_modified>January 28, 2000</last_modified>
</slide>

<slide>
  <title>Reading several bytes at once</title>


<note>
The basic <code>read()</code> method reads a byte at a time. 
This is less than perfectly efficient.  The following two 
overloaded variants read multiple bytes into an array of bytes.
</note>
<ul>
<li>It's more efficient to read multiple bytes at a time:
<pre><code> public int read(byte[] data) throws IOException
 public int read(byte[] data, int offset, int length) throws IOException
 </code></pre>

<note>>
The first method tries to read enough bytes to fill the array
<code>b</code>. The second method reads <code>length</code>
bytes from the input stream and stores them into the array
<code>b</code> starting at position <code>offset</code>.
</note>
</li>
<li>
These methods block until there is some data available. Then
they read as many bytes as they can into <code>b</code>, or
until they've read <code>length</code> bytes.
</li>

<li>Each returns the number of bytes actually read
or -1 on end of stream.
<note> You
should not assume that the array will be filled or that
<code>length</code> bytes will actually have been read. 
</note>
</li>
</ul>

  <description>
    A slide from Elliotte Rusty Harold's presentation 
    Java I/O at SD2000 West, March 22, 2000
  </description>
  <last_modified>March 17, 2000</last_modified>
</slide>

 
 
<slide>
  <title>Counting the available bytes</title>

<p>The <code>available() </code>method tests how many bytes are
ready to be read from the stream without blocking.</p>

<P><code> public int available() throws IOException
</code></P>

<note>For example, the following program is a more efficient version
of <code>Echo</code> that uses <code>available()</code> to test
how many bytes are ready to be read, creates an array of exactly
that size, reads the bytes into the array, then converts the
array to a <code>String</code> and prints the
<code>String</code>.
</note>

<pre><code>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 &gt; 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); 
    }
  
  }

}
</code></pre>


  <description>
    A slide from Elliotte Rusty Harold's presentation 
    Java I/O at SD2000 West, March 22, 2000
  </description>
  <last_modified>January 28, 2000</last_modified>
</slide>


 
<slide>
  <title>Skipping bytes</title>
  
  <ul>
<li>The <code>skip()</code> method
reads a specified number of bytes and throws them away. 

<P><code> public int skip(long n) throws IOException
</code></P>
</li>
<li>An example:

<note>You might use this, for example, if you want to quickly move past a standard header or prefix to some data. 
For example, in the following code fragment <code>skip()</code> is used
to move past some padding included in Java byte code to make sure that a jump 
table entry for a <code>switch</code> statement is aligned on a four byte boundary:</note>

<pre><code>      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 &lt; npairs; i++) {
           int newPosition = position + pad + 12 + i*8;
           result += "\n" + newPosition + "    " 
            + dis.readInt() + " " + dis.readInt();
         }
</code></pre>
</li>

<li>
The complete program is included in Chapter 5 of my book, 
<CITE><A HREF="http://metalab.unc.edu/books/javasecrets.html">Java 
Secrets</A></CITE>, IDG Books, 1997.)
</li>
</ul>

  <description>
    A slide from Elliotte Rusty Harold's presentation 
    Java I/O at SD2000 West, March 22, 2000
  </description>
  <last_modified>March 17, 2000</last_modified>
</slide>

<slide>
  <title>Marking and Resetting</title>

<note><p>
It's often useful to be able to read a few bytes, and then back
up and reread them. For example, in the design of a Java
compiler you don't know for sure whether you've read &lt;,
&lt;&lt;, or &lt;&lt;=  until you've read one two many
characters. It would be useful to be able to back up and reread
the token once you know which token you've read.  Compiler
design and other parsing problems provide many more examples,
but this need occurs elsewhere as well. 
</p>

<p>
Some but not all input streams allow you to mark a particular
position in the stream, and then return to it. There are three
methods that allow this:
</p></note>

<pre><code> public synchronized void mark(int readlimit)
 public synchronized void reset() throws IOException
 public boolean markSupported()
</code></pre>

<ul>
<li> <code>markSupported()</code> method returns
<code>true</code> if this stream supports marking and
<code>false</code> if it doesn't.</li>

<li>The
<code>mark()</code> method places a bookmark in the stream which
you can return to later using the<code> reset() </code>method.</li>
<li>There can be only one <code>mark() </code>in the stream at any
given time. Marking a second location erases the first mark.</li>
<li> If
marking is not supported, these methods throw
<code>IOException</code>s.</li>
</ul>

  <description>
    A slide from Elliotte Rusty Harold's presentation 
    Java I/O at SD2000 West, March 22, 2000
  </description>
  <last_modified>January 28, 2000</last_modified>
</slide>

 
 
<slide>
  <title>Closing Input Streams</title>
 
<pre><code> public void close() throws IOException 
</code></pre>
  
  <description>
    A slide from Elliotte Rusty Harold's presentation 
    Java I/O at SD2000 West, March 22, 2000
  </description>
  <last_modified>January 28, 2000</last_modified>
</slide>
 <slide>
  <title>Subclassing <code>InputStream</code></title>
  <ul>
    <li>You must implement
    
<pre><code>    public abstract int read() throws IOException
</code></pre>    
</li>
<li>
You may override others as well</li>
  </ul>
  
  <description>
    A slide from Elliotte Rusty Harold's presentation 
    Java I/O at SD2000 West, March 22, 2000
  </description>
  <last_modified>January 28, 2000</last_modified>
</slide>



 
<slide>
  <title>An <code>InputStream</code> subclass</title>
  
<pre><code>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 &lt; 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;
  
  }
  
}
</code></pre>
  
  
  <description>
    A slide from Elliotte Rusty Harold's presentation 
    Java I/O at SD2000 West, March 22, 2000
  </description>
  <last_modified>January 28, 2000</last_modified>
</slide>

 
 
<slide>
  <title>Writing Files</title>
  

<p>The <code>java.io.FileOutputStream</code> class represents an <code>OutputStream</code> that
writes bytes to a file. It has the following public methods:</p>

<pre><code> 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
</code></pre>

<note>Except for the constructors and <code>getFD()</code>, these methods merely override the methods of the same name in <code>java.io.OutputStream</code>.
You use them exactly like you use those methods, only the output is written into a file.
</note>  

  <description>
    A slide from Elliotte Rusty Harold's presentation 
    Java I/O at SD2000 West, March 22, 2000
  </description>
  <last_modified>January 28, 2000</last_modified>
</slide>

<slide>
  <title>Writing Files; An Example</title>
  
<p>This example reads user input from <code>System.in</code> and writes it into 
the files specified on the command line. </p>

<pre><code>import java.io.*;


public class MultiType {

  public static void main(String[] args) {

    FileOutputStream[] fos = new FileOutputStream[args.length];

    for (int i = 0; i &lt; 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 &gt; 0) {
          byte[] b = new byte[n];
          int result = System.in.read(b);
          if (result == -1) break;
          for (int i = 0; i &lt; 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 &lt; args.length; i++) {
      try {
        fos[i].close(); 
       }
       catch (IOException e) {
         System.err.println(e); 
       }
    }


  } // end main
  
}</code></pre>
  
  
  <description>
    A slide from Elliotte Rusty Harold's presentation 
    Java I/O at SD2000 West, March 22, 2000
  </description>
  <last_modified>January 28, 2000</last_modified>
</slide>

<slide>
  <title>Appending Data to Files Streams</title>
  
<p>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 <code>true</code> 
as the second argument to the <code>FileOutputStream()</code> constructor.
For example,</p>

<p><code>FileOutputStream fos = new FileOutputStream("16.html", true);</code></p>


<note>The following example reads user input from
<code>System.in</code> and appends it to the files specified on
the command line. 
</note>
<pre><code>import java.io.*;


public class Append {

  public static void main(String[] args) {

    FileOutputStream[] fos = new FileOutputStream[args.length];

    for (int i = 0; i &lt; 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 &gt; 0) {
          byte[] b = new byte[n];
          int result = System.in.read(b);
          if (result == -1) break;
          for (int i = 0; i &lt; 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 &lt; args.length; i++) {
      try {
        fos[i].close(); 
      }
      catch (IOException e) {
        System.err.println(e); 
      }
    } // end for


  } // end main
  
}</code></pre>
  
  <description>
    A slide from Elliotte Rusty Harold's presentation 
    Java I/O at SD2000 West, March 22, 2000
  </description>
  <last_modified>January 28, 2000</last_modified>
</slide>

 
<slide>
  <title>Reading Files</title>
  
<p>The <code>FileInputStream</code> class represents an 
<code>InputStream</code> that reads bytes from a file. It has the following public
methods:</p>

<pre><code> 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
 </code></pre>

<note> Except for the constructors and getFD(), these methods
merely override the methods of the same name in
java.io.InputStream. You use them exactly like you use those
methods, only you'll end up reading data from a file. </note>

  <description>
    A slide from Elliotte Rusty Harold's presentation 
    Java I/O at SD2000 West, March 22, 2000
  </description>
  <last_modified>January 28, 2000</last_modified>
</slide>

<!--
<slide>
  <title>Reading Files Example</title>
  ????

  <description>
    A slide from Elliotte Rusty Harold's presentation 
    Java I/O at SD2000 West, March 22, 2000
  </description>
  <last_modified>January 28, 2000</last_modified>
</slide>
-->

 
<slide>
  <title>Filter Streams</title>
  
<p>
The <code>java.io.FilterInputStream</code> and
<code>java.io.FilterOutputStream</code> classes are concrete
subclasses of <code>InputStream</code> and
<code>OutputStream</code> that somehow modify data read from an
underlying stream. You rarely use these classes directly, but
their subclasses are extremely important, especially
<code>DataInputStream</code> and <code>DataOutputStream</code>.
</p>


<p>
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
<code>DataOutputStream</code> from a
<code>FileOutputStream</code> you might do this:
</p>

<pre><code>FileOutputStream fos = new FileOutputStream("ln.txt");
DataOutputStream dos = new DataOutputStream(fos);
</code></pre>

<p>It's not uncommon to combine these into one line like this:</p>

<pre><code>DataOutputStream dos = new DataOutputStream(new FileOutputStream("ln.txt"));
</code></pre>
  
  
  <description>
    A slide from Elliotte Rusty Harold's presentation 
    Java I/O at SD2000 West, March 22, 2000
  </description>
  <last_modified>January 28, 2000</last_modified>
</slide>
 
<slide>
  <title>The Filter Stream Classes</title>

 
<ul>
<li><code>BufferedInputStream</code> and <code>BufferedOutputStream</code>
<note>These classes buffer reads and writes
by reading data first into a buffer (an internal array of bytes).
Thus  an application
can read bytes from the stream without necessarily calling the underlying native method. The data is read from or written into the buffer
in blocks; subsequent accesses go straight to the buffer.  
</note>
</li>
<li><code>DataInputStream</code> and
      <code>DataOutputStream</code> 
<note>These classes read and write primitive Java data types and Strings 
in a machine-independent way. (Big-endian for integer types, IEEE-754 for 
floats and doubles, UTF-8 for Unicode)
</note>      
      </li>
      
<li><code>PrintStream</code> 
   
<note>The print stream class is implemented by System.out and System.err.
It allows very simple printing of both primitive values,
objects, and string literals.  It uses the platform's default character encoding to
convert characters into bytes. This class traps all
IOExceptions. This class is primarily intended for debugging.
</note>   
</li>    


<li><code>PushbackInputStream</code> 

<note>This  input stream has a one byte pushback buffer so a program can unread 
the last character read. The next time data is read from the stream, the 
"unread" character is re-read. </note>
</li>


<li><code>GZIPInputStream</code> and <code>GZIPOutputStream</code>

<note>From the <code>java.util.zip</code> package, these classes
provide compression and decompression services </note></li>


<li><code>DigestInputStream</code> and <code>DigestOutputStream</code>

<note>
From the <code>java.util.security</code> package, these classes 
can calculate a message digest for a stream using a strong hash function
like SHA.</note>
</li>

<li><code>CipherInputStream</code> and <code>CipherOutputStream</code>


<note>From the <code>javax.crypto</code> package in the <a
href="http://java.sun.com/products/jce/index.html">Java
Cryptography Extension</a> (JCE), a standard extension to Java,
these classes can calculate encrypt or decrypt streams using a
variety of algorithms like DES, RSA, Blowfish, and more.
</note></li>


<li><code>ObjectInputStream</code> and <code>ObjectOutputStream</code>


<note>Subclasses of <code>DataInputStream</code> and
<code>DataOutputStream</code> that can also serialize and
deserialize Java objects to and from raw bytes. Used by remote
method invocation (RMI) and JavaBeans.
</note>
</li>
<li>You can also create your own subclasses of
<code>FilterInputStream</code> and
<code>FilterOutputStream</code> that perform custom
filtering.</li>
 </ul>


  <description>
    A slide from Elliotte Rusty Harold's presentation 
    Java I/O at SD2000 West, March 22, 2000
  </description>
  <last_modified>January 22, 2000</last_modified>
</slide>
 
<slide>
  <title>Buffered Streams</title>

<ul>
<li>The <code>java.io.BufferedInputStream</code> and
<code>java.io.BufferedOutputStream</code> 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.  </li>

<li>The only real difference to the
client between a regular
stream and a buffered stream are the constructors: 

<pre><code> public BufferedInputStream(InputStream in)
 public BufferedInputStream(InputStream in, int size)
 public BufferedOutputStream(OutputStream out)
 public BufferedOutputStream(OutputStream out, int size)
</code></pre>
<note>The <code>size</code> argument is the number of bytes in the
buffer. If a size isn't specified, a 512 byte buffer is used.
</note>

</li>



<li>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.

<pre><code>URL u = new URL("http://java.developer.com");
BufferedInputStream bis = new BufferedInputStream(u.openStream(), 256);
</code></pre>
</li>
</ul>

  <description>
    A slide from Elliotte Rusty Harold's presentation 
    Java I/O at SD2000 West, March 22, 2000
  </description>
  <last_modified>March 17, 2000</last_modified>
</slide>

<slide>
  <title>PushbackInputStream</title>

<ul>
<li>
The <code>PushbackInputStream</code> class provides a pushback buffer so a 
program can "unread" bytes onto the stream. <note>These may be bytes the program 
has read from the stream or they may be bytes that come from somewhere 
else.</note></li> 

<li>The next time data is read from the stream, the "unread" 
bytes are read. 

<pre><code> 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
</code></pre>
</li>

<li>
By default the buffer is only one byte long, and trying to unread more than that 
throws an <code>IOException</code>. However you can change the default buffer 
size with the second constructor:

<pre><code> public PushbackInputStream(InputStream in)
 public PushbackInputStream(InputStream in, int size)
</code></pre>
</li>
</ul>

<note>Although both <code>PushbackInputStream</code> 
and <code>BufferedInputStream</code> use buffers, only
a <code>PushbackInputStream</code> allows unreading and only 
a <code>BufferedInputStream</code> allows marking and resetting.
In a <code>PushbackInputStream</code> <code>markSupported()</code> returns false.  

<p>The <code>read()</code> and <code>available()</code> methods work 
exactly as with normal input streams. 
However, they first attempt to read from the pushback buffer.</p>

</note>

  <description>
    A slide from Elliotte Rusty Harold's presentation 
    Java I/O at SD2000 West, March 22, 2000
  </description>
  <last_modified>March 17, 2000</last_modified>
</slide>

 
 
<slide>
  <title>PrintStream</title>

<note>The main use of the class is the exceptionally overloaded 
<code>print()</code> and <code>println()</code> methods. They differ in that 
<code>println()</code> adds an end-of-line character to whatever it prints while 
<code>print() </code>does not.
</note>
<pre><code> 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)
</code></pre>

<note>
<code>PrintStream</code> is primarily intended for debugging.
Otherwise it's unofficially deprecated in Java 1.1.
You should use the <code>PrintWriter</code> class instead.   
</note>    

  
  <description>
    A slide from Elliotte Rusty Harold's presentation 
    Java I/O at SD2000 West, March 22, 2000
  </description>
  <last_modified>January 28, 2000</last_modified>
</slide>

<slide>
  <title>PrintStream is Evil</title>

<ul>
<li>
This class traps all
<code>IOException</code>s. However you can test the error status with <code>checkError()</code>. This returns <code>true</code> if an error has occurred,
<code>false</code> otherwise.

<p><code> public boolean checkError()</code></p>

</li>
<li>Doesn't internationalize properly</li>
<li>Doesn't handle line breaks in a platform independent way.</li>

</ul>

  
  <description>
    A slide from Elliotte Rusty Harold's presentation 
    Java I/O at SD2000 West, March 22, 2000
  </description>
  <last_modified>January 28, 2000</last_modified>
</slide>


<slide>
  <title>Data Streams</title>

<ul>
<li><code>DataInputStream</code> and <code>DataOutputStream</code> 
 read and write primitive Java
data types and Strings in a machine-independent way.</li> 
<li>IEEE 754 for floating point data</li>
<li>big-endian format for integer data</li>
<li>modified UTF-8 for Unicode data</li>
</ul>
  
  <description>
    A slide from Elliotte Rusty Harold's presentation 
    Java I/O at SD2000 West, March 22, 2000
  </description>
  <last_modified>January 28, 2000</last_modified>
</slide>
<slide>
  <title>DataOutputStream</title>

<p>
DataOutputStream declares these methods:
</p>

<pre><code> 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
</code></pre> 
  
  <description>
    A slide from Elliotte Rusty Harold's presentation 
    Java I/O at SD2000 West, March 22, 2000
  </description>
  <last_modified>January 29, 2000</last_modified>
</slide>
 
<slide>
  <title>DataInputStream</title>
<p>
DataInputStream has these methods:
</p>

<pre><code> 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
</code></pre>

<note>The readFully() methods read repeatedly from the underlying stream until the byte array is
filled. This contrasts with the regular read() methods which only read as many bytes as are
available at the moment.
</note>
  
  <description>
    A slide from Elliotte Rusty Harold's presentation 
    Java I/O at SD2000 West, March 22, 2000
  </description>
  <last_modified>January 29, 2000</last_modified>
</slide>

 
<slide>
  <title><code>readLine()</code> is Evil!</title>

<pre><code> public final String readLine() throws IOException
</code></pre>

<ul>
<li>Doesn't handle non-ASCII character sets well</li>
<li>Makes dangerous assumptions about line endings</li>
</ul>
  
  <description>
    A slide from Elliotte Rusty Harold's presentation 
    Java I/O at SD2000 West, March 22, 2000
  </description>
  <last_modified>January 29, 2000</last_modified>
</slide>
 
 
<slide>
  <title>GZIPOutputStream</title>
 
 <note>This one really shows the power of streams</note>
  
<pre><code>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 &lt; 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);     
      }
    }

  }

}
</code></pre>  
  
  <description>
    A slide from Elliotte Rusty Harold's presentation 
    Java I/O at SD2000 West, March 22, 2000
  </description>
  <last_modified>March 17, 2000</last_modified>
</slide>


 
<slide>
  <title>GZIPInputStream</title>

<pre><code>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 &lt; 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.");
      }
    }

  }

}
</code></pre>
  
  <description>
    A slide from Elliotte Rusty Harold's presentation 
    Java I/O at SD2000 West, March 22, 2000
  </description>
  <last_modified>March 17, 2000</last_modified>
</slide>

<slide>
  <title>Subclassing FilterOutputStream</title>

  <ul>
    <li>Must override:
    
    <p><code>public void write(int b) throws IOException</code></p>
    </li>
    <li>May override other methods as well for performance</li>
  </ul>  
  
<pre><code>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();
  }

}
</code></pre>
  
  <description>
    A slide from Elliotte Rusty Harold's presentation 
    Java I/O at SD2000 West, March 22, 2000
  </description>
  <last_modified>March 17, 2000</last_modified>
</slide>


<slide>
  <title>Subclassing FilterInputStream</title>
  
  <ul>
    <li>Must override:
    
    <p><code>public int read() throws IOException</code></p>
    </li>
    <li>May override other methods as well for performance</li>
  </ul>

<pre><code>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 &gt;= 32 &amp;&amp; b &lt;= 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 &lt; 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] &lt; 32 || data[i] &gt; 126) data[i] = (byte) '?';
    }
    return result;
    
  }

}
</code></pre>

  <description>
    A slide from Elliotte Rusty Harold's presentation 
    Java I/O at SD2000 West, March 22, 2000
  </description>
  <last_modified>March 17, 2000</last_modified>
</slide> 
 
 
<slide>
  <title>Character Sets</title>
  
  <ul>
  <li>Java uses Unicode</li>
  <li>Unicode can be serialized in a variety of formats
  <ul>
  <li>UTF-8</li>
  <li>UCS-2</li>
  <li>UCS-4</li>
  </ul></li>
  <li>Most files are in still some other encoding
  <ul>
  <li>ASCII</li> 
  <li>Latin-1</li> 
  <li>MacRoman</li> 
  <li>etc.</li> 
 </ul></li> 
 <li>When you read and write text you <strong>must</strong> pay attention to
 character sets. </li>
 </ul>
 
 <note>
   Early computer systems and programming languages (pre-1995) mostly 
   assumed the world looked like English, but it doesn't!
 </note>
 
  <description>
    A slide from Elliotte Rusty Harold's presentation 
    Java I/O at SD2000 West, March 22, 2000
  </description>
  <last_modified>January 29, 2000</last_modified>
</slide>
 
<slide>
  <title>Readers and Writers</title>  
 
<p>
The <code>java.io.Reader</code> and <code>java.io.Writer</code>
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.
</p>

<pre><code>public abstract class Reader extends Object
public abstract class Writer extends Object
</code></pre>

  <description>
    A slide from Elliotte Rusty Harold's presentation 
    Java I/O at SD2000 West, March 22, 2000
  </description>
  <last_modified>January 29, 2000</last_modified>
</slide>
  
<slide>
  <title>The Writer class</title>
 
 <ul> 
<li>The methods of the <code>java.io.Writer</code> class are
deliberately similar to the methods of the
<code>java.io.OutputStream</code> class. However rather than
working with bytes, they work with chars. </li>

<li>The basic <code>write()</code> 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 <code>c</code>. 


<p><code>public void write(int c) throws IOException</code></p>

</li>
<li>You can also write an array of characters, a sub-array of characters,
a String, or a substring.

<pre><code> 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
</code></pre>
  <note>It's the array method that's the fundamental
  abstract method here. This is a subtle difference
  that's important when subclassing. 
  </note>
</li>


<li>Like output streams, writers may be buffered. To force the write
to take place, call <code>flush()</code>:

<p><code>public abstract void flush() throws IOException
</code></p>
</li>

<li>Finally the <code>close()</code> method closes the
<code>Writer</code> and releases any resources associated with
it.
    
<p><code> public abstract void close() throws IOException</code></p>
  </li>
  </ul>
  
  <description>
    A slide from Elliotte Rusty Harold's presentation 
    Java I/O at SD2000 West, March 22, 2000
  </description>
  <last_modified>March 17, 2000</last_modified>
</slide>

 
 
<slide>
  <title>OutputStreamWriter</title>
  
  <ul>
<li>
The <code>java.io.OutputStreamWriter</code> class connects byte
streams and character streams. 
<note>
It writes bytes onto the
underlying output stream after translating characters according
to a specified character encoding. 
The encoding can be set in the constructor, or you can accept
the platform's default encoding.</note> 

<pre><code> public OutputStreamWriter(OutputStream out, String enc) 
  throws UnsupportedEncodingException
 public OutputStreamWriter(OutputStream out)
</code></pre>
</li>

<li>For example, if you wanted to write a file encoded in the
Macintosh Symbol font, you might do this:

<pre><code>FileOutputStream fout = new FileOutputStream("symbol.txt");
OutputStreamWriter osw = new OutputStreamWriter(fout, "MacSymbol");
</code></pre>
</li>
 <li>  
 The other methods just override methods from <code>java.io.Writer</code>, but behave
   identically from the perspective of the programmer.

<pre><code> 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
</code></pre>
</li>
</ul>

  <description>
    A slide from Elliotte Rusty Harold's presentation 
    Java I/O at SD2000 West, March 22, 2000
  </description>
  <last_modified>March 17, 2000</last_modified>
</slide>


<slide>
  <title>The Reader class</title>
  
  <ul>
<li>The methods of the <code>java.io.Reader</code> class
are deliberately similar to the methods of the <code>java.io.InputStream </code>class. 
</li>
<li>
The basic<code> read()</code> 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. 

<p><code>public int read() throws IOException
</code></p>
</li>
<li>You can also read many characters into an array of chars.
<note>These methods return the number of bytes successfully read 
or -1 if the end of stream occurs.</note>

<pre><code> public int read(char[] text) throws IOException
 public abstract int read(char[] text, int offset, int length) 
  throws IOException
</code></pre>
</li>

<li>All the <code>read()</code> methods block until some input is
 available, an I/O error occurs, or the end of the stream is reached. 
</li>

<li>You can skip a certain number of characters. 
<note>This method also
blocks until some characters are available. It returns the
number of characters skipped or -1 if the end of stream is
reached.</note>
 
<pre><code> public long skip(long n) throws IOException
</code></pre>
</li>

<li>The <code>ready()</code> method returns true if the reader is
ready to be read from, false if it isn't.

<p><code> public boolean ready() throws IOException
</code></p>
</li>
<li>Readers may or may not support marking and resetting, like input streams. 

<pre><code> public boolean markSupported()
 public void mark(int readAheadLimit) throws IOException
 public void reset() throws IOException
</code></pre>
</li>

<li> Finally the <code>close()</code> method closes the
<code>Reader</code> and releases any resources associated with
it.
    
<p><code> public abstract void close() throws IOException
</code></p>
</li>
</ul>

  <description>
    A slide from Elliotte Rusty Harold's presentation 
    Java I/O at SD2000 West, March 22, 2000
  </description>
  <last_modified>March 17, 2000</last_modified>
</slide>

 
 
<slide>
  <title>InputStreamReader</title>

<ul>
<li>The <code>java.io.InputStreamReader</code> 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.</li>
<li>The encoding can be set in the constructor, or you can accept
the platform's default encoding. 

<pre><code> public InputStreamReader(InputStream in)
 public InputStreamReader(InputStream in, String encoding) 
  throws UnsupportedEncodingException
</code></pre>
</li>
<li>For example, if you wanted to read a file that had been
encoded using the Macintosh Symbol font, you might do this:

<pre><code>FileInputStream fin = new FileInputStream("symbol.txt");
InputStreamReader reader = new InputStreamReader(fin, "MacSymbol");
</code></pre>
</li>

<li>The other methods just override methods from <code>java.io.Reader</code>, 
but behave identically from the perspective of the programmer:

<pre><code> 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
</code></pre>
</li>
</ul>

  <description>
    A slide from Elliotte Rusty Harold's presentation 
    Java I/O at SD2000 West, March 22, 2000
  </description>
  <last_modified>March 17, 2000</last_modified>
</slide>

 
 
<slide>
  <title>Other Readers and Writers</title>

<ul>
	<li><code>BufferedReader </code></li>
      	<li><code>BufferedWriter </code></li>
      	<li><code>CharArrayReader </code></li>
      	<li><code>CharArrayWriter </code></li>
      	<li><code>FileReader </code></li>
      	<li><code>FileWriter </code></li>
      	<li><code>FilterReader </code></li>
      	<li><code>FilterWriter </code></li>
      	<li><code>InputStreamReader </code></li>
      	<li><code>LineNumberReader </code></li>
      	<li><code>PipedReader </code></li>
      	<li><code>PipedWriter </code></li>
      	<li><code>PrintWriter </code></li>
      	<li><code>PushbackReader </code></li>
      	<li><code>StringReader </code></li>
      	<li><code>StringWriter </code></li>
</ul>
  
  <description>
    A slide from Elliotte Rusty Harold's presentation 
    Java I/O at SD2000 West, March 22, 2000
  </description>
  <last_modified>March 17, 2000</last_modified>
</slide>

<slide>
  <title>PrintWriter</title>
  
  <ul>
  <li>A subclass of
<code> java.io.Writer</code> allows you to use the familiar
<code>print()</code> and <code>println()</code> methods</li>
    <li>Can be chained to an <code>OutputStreamWriter</code> to handle non-default character
    sets properly.</li>
    <li>Automatic flushing is performed only when <code>println()</code> is invoked, not every time a newline character is seen.</li>
    <li><code>println()</code> is still dangerous</li>
    <li><code>PrintStream</code> is unofficially deprecated</li>
  </ul>

<pre><code> 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)
</code></pre>
  
  <description>
    A slide from Elliotte Rusty Harold's presentation 
    Java I/O at SD2000 West, March 22, 2000
  </description>
  <last_modified>March 17, 2000</last_modified>
</slide>

<slide>
  <title>BufferedReader</title>

<note>Each time you read from an unbuffered <code>Reader</code>, there's a 
matching read from the underlying input stream. Therefore it's a good idea to 
wrap a <code>BufferedReader</code> around each <code>Reader</code> whose 
<code>read()</code> operations are expensive, such as a <code>FileReader</code>. 
For example, </note>

<ul>
<li>A subclass of
<code> java.io.Reader</code> that you chain to another <code>Reader</code> class to 
buffer characters.</li>
<li>Constructor can specify buffer size
<pre><code>  public BufferedReader(Reader in, int bufferSize)
  public BufferedReader(Reader in)
</code></pre>
</li>
<li> Also notable for its <code>readLine()</code> method 
that allows you to read text a line at a time.

<pre><code> public String readLine() throws IOException
</code></pre>

</li>
<li><code>readLine()</code> is still evil!</li>
<li>
<code>BufferedReader</code>s do support marking and resetting, at least 
up to the
length of the buffer.</li>
</ul> 


  <description>
    A slide from Elliotte Rusty Harold's presentation 
    Java I/O at SD2000 West, March 22, 2000
  </description>
  <last_modified>January 29, 2000</last_modified>
</slide>


<slide>
  <title>BufferedReader readLine() example</title>

<pre><code>// 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 &lt; 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

}</code>
</pre>

  <description>
    A slide from Elliotte Rusty Harold's presentation 
    Java I/O at SD2000 West, March 22, 2000
  </description>
  <last_modified>January 29, 2000</last_modified>
</slide>

 <slide>
  <title>Filter Readers and Writers</title>
  
  <ul>
    <li>Very much like FilterInputStream and FilterOutputStream</li>
    <li>You must override the three-args read and write methods:
<p><code>public int read(char[] text, int offset, int length) throws IOException </code></p>   
<p><code>public void write(char[] text, int offset, int length) throws IOException </code></p>   
    </li>
  </ul>
  
  <description>
    A slide from Elliotte Rusty Harold's presentation 
    Java I/O at SD2000 West, March 22, 2000
  </description>
  <last_modified>March 17, 2000</last_modified>
</slide>
 
<slide>
  <title>Formatted I/O</title>
  
  
  
  
  <description>
    A slide from Elliotte Rusty Harold's presentation 
    Java I/O at SD2000 West, March 22, 2000
  </description>
  <last_modified>January 29, 2000</last_modified>
</slide>


<slide>
  <title>The Old Way</title>
 
<note>conflates I/O with number forqatting</note> 
 
<h2>Input</h2>  
<ul>
<li>C: <p><code>scanf("%d", &amp;x);</code></p></li>

<li>C++: <p><code>cin &gt;&gt; x;</code></p></li>

<li>Pascal: <p><code>READLN (X);</code></p></li>

<li>Fortran: 

<pre><code>      READ 2, X
    2 FORMAT (F5.1)
</code>
</pre></li>
</ul>

<h2>Output</h2>  
<ul>
<li>C: <p><code>printf("%.2d", salary);</code></p></li>

<li>C++: 
<pre><code>cout.precision(2);
cout &lt;&lt; salary;</code></pre></li>

<li>Fortran: 

<pre><code>      PRINT 20, SALARY
   20 FORMAT(F10.2)
</code>
</pre></li>

</ul>  
  
  
  <description>
    A slide from Elliotte Rusty Harold's presentation 
    Java I/O at SD2000 West, March 22, 2000
  </description>
  <last_modified>March 17, 2000</last_modified>
</slide>

 
 
<slide>
  <title>The New Way</title>

<ul>
  <li>I/O is not at all the same thing as converting a number to a string</li>
  <ol>
<li>First convert the number to a string</li>
<li>Then output the string</li>
</ol>
<li>Much more flexible</li>
</ul>
  
  <description>
    A slide from Elliotte Rusty Harold's presentation 
    Java I/O at SD2000 West, March 22, 2000
  </description>
  <last_modified>March 17, 2000</last_modified>
</slide>


 
<slide>
  <title>Locales</title>

<ul>
<li><code>java.util.Locale</code> objects encapsulate differences between 
different cultures, languages, and countries</li>
<li>Predefined locales:
<pre><code>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
</code></pre></li>
</ul>
  
  <description>
    A slide from Elliotte Rusty Harold's presentation 
    Java I/O at SD2000 West, March 22, 2000
  </description>
  <last_modified>January 29, 2000</last_modified>
</slide>

 
 
<slide>
  <title>Number Format</title>
  
  <ul>
  <li>Number formats are locale specific</li>
  <li>Number formats specify
  <ul>
  <li>maximum and minimum integer width</li>
<li>maximum and minimum fraction width (precision, number of decimal places)</li>
<li>whether or not  digits are grouped (e.g. 2,109,356 vs. 2109356)</li>
<li>what character digits are grouped with</li>
<li>decimal separator</li>
  </ul>
  </li>
  </ul>
<pre><code>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);
</code></pre>

  <description>
    A slide from Elliotte Rusty Harold's presentation 
    Java I/O at SD2000 West, March 22, 2000
  </description>
  <last_modified>January 29, 2000</last_modified>
</slide>
<slide>
  <title>Formatting Numbers</title>

<ul>
<li>A <code>NumberFormat</code> object converts integers and floating 
point numbers using one of <code>NumberFormat</code>'s five overloaded 
<code>format()</code> methods:

<pre><code>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)
</code></pre></li>

 
</ul>

  
  <description>
    A slide from Elliotte Rusty Harold's presentation 
    Java I/O at SD2000 West, March 22, 2000
  </description>
  <last_modified>March 17, 2000</last_modified>
</slide>

<slide>
  <title> Number Format example</title>

<pre><code>
import java.text.*;


public class FormatTest {

  public static void main(String[] args) {

    NumberFormat nf = NumberFormat.getInstance();
    for (double x = Math.PI; x &lt; 100000; x *= 10) {
      String formattedNumber = nf.format(x);
      System.out.println(formattedNumber + "\t" + x);
    }

  }

}</code>
</pre>

<hr/>
U.S. English system results:

<pre><code>3.141		3.14159265358979
31.415		31.4159265358979
314.159		314.159265358979
3,141.592		3141.5926535897897
31,415.926	31415.926535897896</code>
</pre>

<hr/>
<note>
The formatted numbers don't use a ridiculous number of decimal places, and group 
the integer part with commas when it becomes large. Of course the exact 
formatting depends on the default locale. For instance when I changed the locale 
to  French I encountered this result:</note>
French results:

<pre><code>3,141		3.14159265358979
31,415		31.4159265358979
314,159		314.159265358979
3 141,592		3141.5926535897897
31 415,926	31415.926535897896</code>
</pre>

<note>The French locale uses a decimal comma instead of a decimal point, and 
separates every three digits in the integer part with a space. This may be 
confusing to an American, but seems perfectly normal to a Parisian. One of the 
advantage of number formats is that by using the default number format for the 
system., much of your program is automatically localized. No extra code is 
required to do the right thing on French systems, on Canadian systems, on 
Japanese systems, and so on.
</note>

  
  <description>
    A slide from Elliotte Rusty Harold's presentation 
    Java I/O at SD2000 West, March 22, 2000
  </description>
  <last_modified>January 29, 2000</last_modified>
</slide>

  
 
<slide>
  <title>Specifying Precision</title>

<p>You specify the minimum and maximum of each type you want in each number 
using these four methods:</p>
<pre><code>public void setMaximumIntegerDigits(int newValue)
public void setMinimumIntegerDigits(int newValue)
public void setMaximumFractionDigits(int newValue)
public void setMinimumFractionDigits(int newValue)
</code></pre>

<p>For example, to specify that <code>myFormat</code> should format 
numbers with at least 10 digits before the decimal point and at most  
3 digits after, you would type:</p>

<pre><code>myFormat.setMinimumIntegerDigits(10);
myFormat.setMaximumFractionDigits(3);
</code></pre>
  
  <description>
    A slide from Elliotte Rusty Harold's presentation 
    Java I/O at SD2000 West, March 22, 2000
  </description>
  <last_modified>January 29, 2000</last_modified>
</slide>

 
<slide>
  <title> Precision Example</title>

<pre><code>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 &lt; 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);
    }
    
  }

}
</code></pre>

<hr/>
<p>Output:</p>

<pre><code>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
</code></pre>
  
  <description>
    A slide from Elliotte Rusty Harold's presentation 
    Java I/O at SD2000 West, March 22, 2000
  </description>
  <last_modified>January 29, 2000</last_modified>
</slide>


 
<slide>
  <title>How big is 299792500?</title>
  
  <description>
    A slide from Elliotte Rusty Harold's presentation 
    Java I/O at SD2000 West, March 22, 2000
  </description>
  <last_modified>January 29, 2000</last_modified>
</slide>

 
<slide>
  <title>Grouping</title>
  
<ul>
<li>Most number formats support grouping and some use it by default. </li>

<li>Ask whether a particular <code>NumberFormat</code>
groups with the <code>isGroupingUsed()</code> method:
<pre><code>public boolean isGroupingUsed()
</code></pre></li>

<li>You can turn grouping on or off for a number format with the 
<code>setGroupingUsed()</code> method.
<pre><code>public void setGroupingUsed(boolean groupNumbers)
</code></pre></li>
</ul>  
  
  <description>
    A slide from Elliotte Rusty Harold's presentation 
    Java I/O at SD2000 West, March 22, 2000
  </description>
  <last_modified>January 29, 2000</last_modified>
</slide>

 
 
<slide>
  <title>Currency Formats</title>

<p>
If you know you're going to be working with money, you can request a
currency formatter with the static 
<code>NumberFormat.getCurrencyInstance()</code> method:
</p>
<pre><code>public static final NumberFormat getCurrencyInstance()
public static NumberFormat getCurrencyInstance(Locale inLocale)
</code></pre>


<pre><code>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));
    
  }

}
</code></pre>

<hr/>
Output:
<pre><code>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
</code></pre>

<note>Notice how nicely the numbers are formatted. Nowhere did I add dollar signs, say that I wanted exactly two numbers after the decimal point, or that I wanted to separate the thousands with commas. The NumberFormat class took care of that.
There are limits to how far this goes. Currency formats may change the currency sign in different locales, but they won't convert the values (between U.S. and Canadian dollars or between U.S. dollars and British pounds, for example). Since conversion rates float from day to day and minute to minute that's a bit much to ask of a static class. If you wanted to do this, you need to provide some source of the conversion rate information, either from user input or pulled off the network.  
</note>
  
  <description>
    A slide from Elliotte Rusty Harold's presentation 
    Java I/O at SD2000 West, March 22, 2000
  </description>
  <last_modified>January 29, 2000</last_modified>
</slide>
<slide>
  <title>Percent Formats</title>
  
  <description>
    A slide from Elliotte Rusty Harold's presentation 
    Java I/O at SD2000 West, March 22, 2000
  </description>
  <last_modified>January 29, 2000</last_modified>
</slide>

 
 
<slide>
  <title>Parsing Input</title>
<ul>

<li>Number formats are also responsible for 
converting strings to binary numbers.</li>
<li> Number formats provide more flexible conversions than 
you can achieve with the methods in the type wrapper classes like 
<code>Integer.parseInt()</code>.
<ul>
<li> For instance, a percent format <code>parse()</code> method 
can interpret 57% as 0.57 instead of 57. </li>
<li>A currency format can read (12.45) as -12.45.</li>
<li>Understand grouping</li>
</ul>
</li>
<li>
<pre><code>public Number parse(String text) throws ParseException
public abstract Number parse(String text, ParsePosition parsePosition)
public final Object parseObject(String source, ParsePosition parsePosition)
</code></pre>
  </li>
  </ul>
  <description>
    A slide from Elliotte Rusty Harold's presentation 
    Java I/O at SD2000 West, March 22, 2000
  </description>
  <last_modified>March 17, 2000</last_modified>
</slide>


 
<slide>
  <title>CS 101 Homework</title>
  
<pre><code>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 &lt; 0) break;
        double root = Math.sqrt(d);
        System.out.println("The square root of " + s + " is " + root);
      }
    }
    catch (IOException e) {
      System.err.println(e);  
    }
    
  }

}
</code></pre>
<hr/>
Output:
<pre><tt>% 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
</tt></pre>

<note>These results tell you a few things about the default number format on the platform where I ran it (U.S. English Windows NT, JDK 1.2rc1).  First of all it doesn't understand exponential notation. The square root of 2.998E+8 is not 1.7314733610425546. It's 1.7314733610425546E+4. The number format parsed right up to the first character it didn't recognize (E) and stopped. Thus you got the square root of 2.998 instead. You can also see that this number format doesn't understand negative numbers represented by parentheses or words like "four". 
On the other hand, it can parse numbers with thousands separators like 2,345,678. This is more than the standard I/O libraries in most other languages can do. With the appropriate, non-default number format Java could parse (12), four, and 2.998E+8 as well.
</note>  
  
  
  <description>
    A slide from Elliotte Rusty Harold's presentation 
    Java I/O at SD2000 West, March 22, 2000
  </description>
  <last_modified>January 29, 2000</last_modified>
</slide>

 
 
<slide>
  <title>Exponentials</title>
  
  <ul>
  <li>No default support for exponentials</li>
  <li>You can roll your own by subclassing NumberFormat</li>
  <li>IBM alphaWorks <code>NumberFormat</code> class at 
  <a href="http://www.alphaworks.ibm.com/tech/numberformat">http://www.alphaworks.ibm.com/tech/numberformat</a></li>
  </ul>
  
  <description>
    A slide from Elliotte Rusty Harold's presentation 
    Java I/O at SD2000 West, March 22, 2000
  </description>
  <last_modified>January 29, 2000</last_modified>
</slide>

<slide>
  <title>Streams are not Thread Safe</title>
  
  <description>
    A slide from Elliotte Rusty Harold's presentation 
    Java I/O at SD2000 West, March 22, 2000
  </description>
  <last_modified>January 29, 2000</last_modified>
</slide>

<slide>
  <title>Future Improvements and Additions</title>
  
  <ul>
  <li>Memory mapped I/O</li>
  <li>Asynchronous I/O</li>
  <li>Direct access to character converter classes</li>
  <li>Better support for regular expressions</li>
  </ul>
  
  <description>
    A slide from Elliotte Rusty Harold's presentation 
    Java I/O at SD2000 West, March 22, 2000
  </description>
  <last_modified>January 29, 2000</last_modified>
</slide>

<slide>
<title>To Learn More</title>


  <img src="javaio_cover.gif" width="181" height="237" align="left"/>
  <ul>
  <li>Java I/O</li>
  <li>Elliotte Rusty Harold</li>
  <li>O'Reilly &amp; Associates, 1999</li>
  <li>ISBN: 01-56592-485-1 </li>
  </ul>
  <br clear="all"/>
  
  <ul>
  <li>This presentation: 
  <a href="http://metalab.unc.edu/javafaq/slides/sd2000west/javaio/">http://metalab.unc.edu/javafaq/slides/sd2000west/javaio/</a></li>
  </ul>

  <description>
    A slide from Elliotte Rusty Harold's presentation 
    Java I/O at SD2000 West, March 22, 2000
  </description>
  <last_modified>January 29, 2000</last_modified>
</slide>


  <slide>
    <title>Questions?</title>
  
  
  <description>
    A slide from Elliotte Rusty Harold's presentation 
    Java I/O at SD2000 West, March 22, 2000
  </description>
  <last_modified>January 22, 2000</last_modified>
</slide>

</presentation>
