InputStream
1.概要
InputStream位于java.io,它也是一个抽象类,是java类库中的基础输入类。它提供了将数据读取为原始字节所需的基本方法。
Public abstract int read() throws IOException; Public int read(byte b[])throws IOException Public int read(byte b[],int off,int len) 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()
2.子类简介
InputStream也有个庞大的家族。典型的如从文件中读取数据的FileInputStream,从字节数组中读取的ByteArrayInputStream,从网络连接中读取的TelnetInputStream。
TelnetInputStream隐藏于sun.net,FileInputStream位于java.io,在其他package如java.net中也有各种子类。
3.方法
read
一个无参read方法是基础,其他2个read方法围绕着它来实现,它被声明为abstract,具体实现开放给子类去实现。
从类库的层级上描述,它与输出抽象基类OutputStream的write方法类似,它在子类中可能有各种覆盖版本,但它本身的方法签名int read()的含义应被遵守:int read()表示单字节读取,返回的int表示0~255之间的一个无符号字节。
int值存储为字节数组时会产生一个-128~127之间的有符合字节,使用时可以int i=b>=0?b:256+b进行转换;int read()及基于它实现的另2个read都用返回值-1表示流的结束。
设计上讲,int read()的子类实现可以在方法内有特定的实现,所以无所谓阻塞或非阻塞,但在标准库的主要实现上,子类的int read()通常是经JNI进行IO操作,所以说该read方法会等待并阻塞后面的代码,直到读取到1个字节的数据。因该read通常包含着IO操作,应尽量放到独立的线程中。
Write/Read有很多细节,另开篇记录讨论,这里主要说明InputStream类。
skip是跳过某些数据,避免无谓的读取。这在随机访问的文件操作时有用,但在网络操作中用处不大。网络连接是顺序的且耗时的,相比较起来读取数据的成本反而是很小的,skip跳过某些数据意义不大。
available
单字节读取效率不高,所以有2个从流中读取多字节的read方法:
Public int read(byte b[])throws IOException Public int read(byte b[],int off,int len) throws IOException
这2个方法都尝试从流中读取多字节数据并填充到byte b[]数组,第2个方法还能指明填充b[]中的从off位置起始连续len字节的数据。
但填充不一定都会成功,可能失败也可能不完全成功,如期待读取1024字节但只读取到512字节,在网络中尤其如此,可能剩余的512字节还在传输中。所以需要循环判断直到读取到期待的数据:
Int finish=0; Int size=1024; Byte[] input=new byte[size]; While(finish<size) { Finish+=in.read(input,finish,size-finish); }
这个循环能的等待后续的数据,但它也阻塞了后面的程序,而且后续的数据可能永远不会到达。所以需要判断流是否结束,流使用-1标识结束,传输完成、发生异常、对方端中止等情况都是可能的原因。
While(finish<size) { Int flag=in.read(input,finish,size-finish); If(flag==-1) break; Finish+=flag; }
available方法不用等待剩余的数据,它测试当前有多少实际可用的数据,保证了能直接读取到可用数据并返回,这样可以避免因等待而导致的阻塞,实际上数据可能更多点,但至少能保证available建议的数据。
Int ava=in.available();//预测 Int input=new byte[ava]; Int finish=in.read(input,0,ava);//实际读取
close
与输出一样,IO向的处理需要释放相关的资源,使用类似的释放模式。继续读取一个关闭后的流可能导致一个IOException,但有些流可能仍然允许处理这个对象,如获取消息摘要时需要先读取数据并关闭流之后才可以。
mark, reset,markSupported3个方法通常是组合使用的,以实现数据的重复读取。mark方法是在流中打上一个标记,调用reset方法时会把流重置到这个位置,接下来的读取从这个位置开始。一个流任何时刻只有一个mark,新标记会清除之前的标记。markSupported用于测试当前流是否支持mark标记功能,返回true时支持标记功能。当流不支持标记功能时,调用mark没什么效果,之后调用reset时将引发IOException。
mark(int readlimit),它在不同子类中的实现上有许多不同的细节。按最典型的BufferedInputStream与ByteArrayInputStream举例,形参有readlimit与readAheadlimit区别,后者可能会让人误解为在某前置位置打上mark标记,但一般,mark方法的标记位就是当前位置。
在BufferedInputStream中,有默认8192字节大小的内部buf,buf中数据读取完后会从底层流中加载新一批的数据。readlimit设置的是期望能复用的数据大小,基于buf实现,调用mark方法时标记buf中的当前位置,之后的readlimit大小区间都是期望复用的。当设置的readlimit小于buf容量时,buf被读取完加载一批新数据时,保留区间前移,mark标记置0,复读数据在前,新数据加载到尾部,复读时就从0位开始,若mark置0后继续读取直到读完buf,buf此时要加载新数据,会丢弃之前的所有数据,从0开始覆盖,即mark后继续读取buf大小的数据会导致mark失效。
当readlimit值大于buf容量时,为实现复用目的,buf直接扩展为readlimit大小,此时扩展后的整个buf都是复用区,若继续读取超出readlimit,导致buf加载新数据,同样会覆盖之前的数据,所以读取超出readlimit时此时导致mark失效。

浙公网安备 33010602011771号