IO类演变的历史-字节流与字符流

栏目:IO与序列化 作者:admin 日期:2015-07-07 评论:0 点击: 2,198 次

我们很有必要了解IO类的演变历史,如果缺乏历史的眼光,那么我们对什么时候该使用那些类,以及什么时候不该使用那些类而感到迷惑。
JDK1.0的时候,所有与输入相关的类都继承于InputStream,所有与输出相关的类都继承于OutputStream。
JDK1.1的时候,增加了面向字符的IO类,包括Reader和Writer。
JDK1.4的时候,增加了NIO,实际上旧的IO已经使用重新使用NIO实现过了,即使没有显示的使用NIO编码,我们也能从中受益的。

字节流

字节流,顾名思义,以字节为单位进行IO操作。字节流最常用的方法是read和write方法。
read方法是从流中读取字节并递增文件指针到一下个位置。在字节流的读取过程中,我们要明白一个重要的概念:文件指针。文件指针指示了流中的当前位置。当文件指针到达文件的末尾时候,读取操作返回-1给调用者。read方法不带任何参数,每次只能读取一个字节。read方法的返回值是读取的字节,并转化为int类型返回。如下所示:
FileInputStream reader =new FileInputStream("filename");//以字节流格式打开文件
int result =reader.read();
再此有个疑问:为什么result为-1表示已经读取到文件的末尾了?
因为read()方法内部将读取到的所有字节高位补0转为int返回,这样做所有的数据都会是正数,所以此时就可以用-1表示流末尾了。
read方法还有其他的形式:
int read(byte[] b),读出的数据被存储在字节数组中,返回读取的字节数,如果提前到达文件的末尾,返回值可能小于数组的长度。
int read(byte[] b, int off, int len),第一个参数指定数据被存储的字节数组,第二个参数off指定读取的第一个字节将存储在字节数组的偏移量,第三个参数len指定要读取的字节数。
注意:上面三种形式的读取都是直接从文件流里读取的,每次读取只能是一个一个字节的读取。需要说明的是每次读取单个字节或512个字节,需要的io数量都是相同的。为了提高效率,人们设计出了缓存的读取方式:
FileInputStream reader =new FileInputStream("filename");
BufferedInputStream bs = new BufferedInputStream(reader);
bs.read();
虽然上面仍然读取的是一个字节数据,但是因为缓存的存在,效率已经大大提高了。底层的实现是这样的:BufferedInputStream的实现中有一个用于存储数据的内部缓冲区数组:protected volatile byte[] buf。这个缓冲区数组的作用在于对源进行数据块访问,而不是一字节一字节的访问,也就是进行一次I/O将一块数据存到缓冲区中,再从缓冲区中read,当缓冲区为空时再重新读新的数据块。

字符流

字符流是操作字符文件的。一般情况下我们的使用方式是:
FileReader reader =new FileReader("filename");//以字符格式打开文件
BufferedReader buffer = new BufferedReader(reader);//建立缓存
buffer.readLine();//读取缓存中的一行

小结

虽然IO类的类很多,但是与我们实际开发密切相关的类,其实没有几个。我们一定要区分出字节类和字符类,另外还有对缓存的内部实现(以块的形式去读取文件)有个更深刻的认识。

IO类演变的历史-字节流与字符流:等您坐沙发呢!

发表评论