首先要说明啥是输入流和输出流,输入流是将数据从文件、标准输入设别或其他外部设备输入加载到内存,而输出流是将内存中的数据保存到文件中或传输到输出设备。然后在Java的IO流里面分字节流和字符流两大类。在字节流里最常用到的就是FileInputStream(FileOutputStream)和BufferedInputStream(BufferedOutputStream)两大类,当然字节流里所有的子类都是从InputStream和OutputStream两个抽象类继承过来的。
在InputStream里read()方法是抽象的,需要由具体的子类来实现。InputStream中read()具体的源码是
public abstract int read() throws IOException; //从输入流中读取数据的下一个字节。返回 0 到 255 范围内的 int 字节值。 //如果因为已经到达流末尾而没有可用的字节,则返回值 -1。 //在输入数据可用、检测到流末尾或者抛出异常前,此方法一直阻塞。 //返回:下一个数据字节;如果到达流的末尾,则返回 -1。
先说输入流中标准输入设备,在Java中System.in对应于标准输入,主要接受键盘的输入。但有一点很重要,非常值得注意。从标准输入中读取数据,并不是每键入一个字符就形成输入流,而是当键入回车符之后才开始将一整行字符作为输入流。比如说这段回显的程序
public static void echo(InputStream is){ while(true){ try { int i=is.read(); System.out.print(i); if(i==-1){ break; } char c=(char)i; System.out.print(c); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } public static void main(String[] args) { // TODO Auto-generated method stub echo(System.in); }
在这段程序中,由于InputStream对象接受的是标准输入设备的输入流,因此is.read()方法并不会键入一个字符,就马上读入一个字符然后在控制台上打印出来。在没有敲回车前,此方法会一直阻塞在这里,程序不会继续往下进行。当然在一段输入流的末位会看到13和10,这就是对应的回车和换行。
在InputStream中还有两个很重要的方法——read(byte[] b)和read(byte[] b,int off,int len)。 read(byte[] b)方法是从输入流中读入一定数量的字节,并将其存储在缓冲区数组b中。而read(byte[] b,int off,int len)将输入流中最多len个数据字节读入byte数组。其实这两个重载的read()方法是有联系的。
public int read(byte b[]) throws IOException { return read(b, 0, b.length); } //从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中。 //以整数形式返回实际读取的字节数。从源码中看到出来含有一个参数的read()方法其实调用的就是含有三个参数的read()方法。只是后两个参数表示从数组0号元素开始读完整个数组的长度。而read(byte[] b,int off,int len)的源码是(截取部分)
public int read(byte b[], int off, int len) throws IOException { int c = read(); if (c == -1) { return -1; } b[off] = (byte)c; int i = 1; try { for (; i < len ; i++) { c = read(); if (c == -1) { break; } b[off + i] = (byte)c; } } catch (IOException ee) { } return i; } //读入缓冲区的总字节数;如果因为已到达流末尾而不再有数据可用,则返回 -1。从程序中可以看的出来,从输入流中读入的数据会存放在一个临时数组中,从而提高了输入的效率。