InputStream
闲话:java.io下的类数量繁多,记得第一次试图尝试研究一下java io源码时,无从下手,最近在接触了一些设计模式后在重新梳理研究一下Java I/O, 以此记录.
-
描述
InputStream: This abstract class is the superclass of all classes representing an input stream of
bytes输入源 对应的具体类 构造方法参数及说明 字节数组 ByteArrayInputStream 一个byte[]数组 String StringBufferedInputStream 以弃用 文件 FileInputStream String, File, FileDescriptor, 指从一个文件中读取信息 管道 PipedInputStream 可以想象成实际中的管道,你把一些东西从管子一头放入,而他们将从另一头出来 其他输入流的组合序列 SequenceInputStream 用来表示逻辑的串联流(指任意其他InputStream) 其他数据源 网络等 抽象装饰器 FilterInputStream 所有具体装饰器的父类 -
InputStream
日常生活中当我们需要水时,往往有许多途径可以获得;如自来水(即从水管中),瓶装水(各种饮料),小溪,水库,等等,而InputStream则相当于所有可以获得水资源的途径,我们都可以从这些途径中取得水资源(即InputStream中的read()方法).而从每个途径中获取水资源的方法尽不相同,所以这个read()方法是抽象的,需要子类去具体实现(如自来水从水龙头出水, 矿泉水从瓶口出水,小溪可以沿途取水等等)
package java.io; public abstract class InputStream implements Closeable { private static final int MAX_SKIP_BUFFER_SIZE = 2048; //这是所有子类必须实现的方法,如果在学Java时习惯了System.in, System.out //时,一开始时很难进入到一个抽象的思维中的(对我来说),所以一开始并没有理解这个 //read(). //目前理解整理一下: //InputStream其实现类代表一个可以读的*数据载体*, 当需要从中读入一些信息(如字节,字符)时 //调用在其上的read()方法,这个方法返回一个int类型的数据 // byte[] buf = new byte[]{0x11, 0x23}; // ByteArrayInputStream in = new ByteArrayInputStream(buf); // System.out.println(in.read()); // 其输出结果回返回17(0x11) //而在上面这个buf就是作为一种数据载体,被抽象成为一个数据流,并从其中读取一个字节 public abstract int read() throws IOException; /** * 传入一个字节数组b,将尽可能读到的字节缓冲到b中 * 这个函数最终会调用read(b, 0, b.length)作为一般的情况来使用 */ public int read(byte b[]) throws IOException { return read(b, 0, b.length); } /** * 这个方法蛮清晰的, 分几种情况判断 * 1.b为null,抛出NullPointerException * 2.off<0, len<0, len>b.length属于取值范围错误,并抛出IndexOutOfBoundesException * 3.len == 0, 直接返回0作为一种正确示意(不同于-1) * 接下来就是从给定的数据载体中逐个读取字节,即每个具体的实现类表达的一种抽象流: * 如ByteArrayInputStream表示从给定的byte[]中取得数据,直至到达末尾 * FileInputStream表示从文件中读取数据, * 每个具体的实现类实现相应的read()抽象方法 * 注意:这个方法会阻塞直到可以读取数据 */ public int read(byte b[], int off, int len) throws IOException { if (b == null) { throw new NullPointerException(); } else if (off < 0 || len < 0 || len > b.length - off) { throw new IndexOutOfBoundsException(); } else if (len == 0) { return 0; } 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; } /** * 从输入流中跳过并丢弃给定长度n的字节 * 有多种特殊情况导致skip停止 * 1. n <= 0;(直接返回0, 不跳过任何字节, 并且默认方法不处理负数,由部分实现类处理相应逻辑) * 2. 在跳过n字节之前到达输入流末尾(返回跳过的的字节数量) * 内部创建一个byte[]数组用来保存跳过的数据直到跳过n字节或到达输入流末尾 */ public long skip(long n) throws IOException { long remaining = n; int nr; if (n <= 0) { return 0; } int size = (int)Math.min(MAX_SKIP_BUFFER_SIZE, remaining); byte[] skipBuffer = new byte[size]; while (remaining > 0) { nr = read(skipBuffer, 0, (int)Math.min(size, remaining)); if (nr < 0) { break; } remaining -= nr; } return n - remaining; } public int available() throws IOException { return 0; } public void close() throws IOException {} public synchronized void mark(int readlimit) {} public synchronized void reset() throws IOException { throw new IOException("mark/reset not supported"); } public boolean markSupported() { return false; } } -
FileInputStream
可以初略想象称这是一个矿泉水,你传给他的文件就是你想要储存在瓶里的水资源,当你要喝水时,则可以打开瓶盖喝水:即调用相应的read()方法,喝完水时盖紧瓶盖:即调用close().
简单使用
public static void main(String[] args) throws IOException { FileInputStream in = new FileInputStream("test"); while(in.available() != 0) System.out.print(in.read() + " "); System.out.println(); DataInputStream dataIn = new DataInputStream(new FileInputStream("test")); while(dataIn.available() != 0) System.out.print(dataIn.readLine()); in.close(); dataIn.close(); } /** * output: * 105 116 32 105 115 32 97 32 115 105 109 112 108 101 32 102 105 108 101 41 * it is a simple file.分析: test是我在当前目录创建的一个简单文本文件, 里面包含了’it is a simple file.’,当我将他传给FileInputStream时便可以对该文件进行读操作了,不过我们可以注意到,单纯的一个FileInputStream从中读到的数据时原始的字节数据,即这些字符相对应的ASCII码值,所以下面用一个DataInputStream来装饰它,使他能够拥有更多的功能,获得你想要的结果;
综上: FileInputStream其本身也是一个抽象表示,我们很少直接使用它,当传递一个文件名或一个File对象时,FileInputStream的负责链接到这个真实的文件,我们只管利用它提供的接口进行读即可, 而他本身提供的接口有限且往往不符合我们的期待,所以Java I/O设计者采用装饰者模式,动态的扩展其上的功能达到我们想要的功能.
- ByteArrayInputStream
这个类很简单, 可直接阅读源代码, 构造函数传入一个byte[]数组, 另外在这个类上面调用close是无任何实际效应的.为什么在下方解释
简单使用
public static void main(String[] args) throws IOException { String source = "hello world"; //通过String的getBytes()返回一个byte[]数组并构造一个ByteArrayInputStream ByteArrayInputStream in = new ByteArrayInputStream(source.getBytes()); int c; while ((c = in.read()) != -1) System.out.print((char) c); in.close();//close无效 System.out.println(); //将内部指针重设至初始位置 in.reset(); System.out.println((char)in.read()); //给mark传入的参数是无意义的 // public void mark(int readAheadLimit){ // mark = pos; //} in.mark(3); in.reset(); System.out.println((char)in.read()); } /** * output: * hello world * h * e */分析: ByteArrayInputStream这个类就是代表在一个字节数组上进入读操作,其源码也非常简单易懂,之说以在其上调用close()无效果, 因为这个InputStream并不涉及像File, Pipe,等与操作系统息息相关的操作,就是一个简单的从给定的byte[]数组中读取数据,而一个byte[]数组就没有关闭的概念了,就像我们可以反复从一个数组里通过下标取得数据一样
- SequenceInputStream
组个多个流为单个流,这个类也很简单,其够着函数有两个 |构造函数|说明| |-|-| |SequenceInputStream(Enumeration<T extends InputStream> e)| |SequenceInputStream(InputStream s1, InputStream s2)|其最终也是创建一个含有两个InputStream的Enumeration
简单使用
public static void main(String[] args) throws IOException { FileInputStream fileIn = new FileInputStream("test"); ByteArrayInputStream baIn = new ByteArrayInputStream("byte array".getBytes()); SequenceInputStream sIn = new SequenceInputStream(fileIn, baIn); int c; while((c = sIn.read()) != -1) System.out.print((char)c); fileIn.close(); baIn.close(); sIn.close(); } /** * output: * it is a simple file.byte array