java中的IO流总结(一)

在java API中,可以从其中读入一个字节序列的对象叫做输入流,而可以向其中写入一个字节序列的对象叫做输出流,这些字节序列的来源和目的地可以是文件,而且通常是文件,但是也可以是网络连接,甚至还可以是内存块,而抽象类InputStream和OutputStream构成了输入/输出(I/O)类层次结构的基础。
因为面向字节的流不便于处理以Unicode形式存储的信息(Unicode中每个字符都使用了多个字节来表示),所以从抽象类Reader和Writer中继承出来了一个专门用于处理Unicode字符的单独的类层次结构,这些类拥有的读写操作都是基于两字节的Unicode码元的,而不是基於单字节的字符。

顺便提一下,UTF-8编码方式是一种8位的Unicode字符集,长度是可变的,一个字符可能是1个字节,2个字节,3个字节或者4个字节,中文一般是3个字节。
UCS-2编码是固定长度为16位的Unicode字符集,每个字符都占2个字节。
UTF-16编码也是一种16位的编码字符集,在UTF-16中,字符要么是2字节,要么是4字节。

一、流
输入流和输出流的层次结构

                                    图1-1 输入流和输出流的层次结构

这里写图片描述

                                    图1-2 Reader和Writer的层次结构

上图1-1和1-2就是java的流家族,包含各种流类型
我们把流家族中的成员按照他们的使用方法来进行划分,这样就形成了处理字节和字符的两个单独的层次结构:
(1)对於单个的字节或字节数组,我们可以使用InputStream和OutputStream,他们可以可以读写单个的字节或字节数组
(2)对于字符串和数字:我们就需要功能更为强大的子类,例如DataInputStream和DataOutputStream可以以二进制的格式读取所有的基本java类型
(3)其他流:ZipInputStream和ZipOutputStream可以以常见的ZIP压缩格式读取文件

java.io.InputStream:
       abstract int read() :从数据中读入一个字节,并返回该字节,这个read方法在碰到流的结尾时返回-1。
       int read(byte[] b) :读入一个字节数组,并返回实际读入的字节数,或者在碰到流的结尾时返回-1;这个read方法最多读入b.length个字节
       int read(byte[] b, int off, int len) :读入一个字节数组,这个read方法返回实际读入的字节数,或者在碰到流的结尾时返回-1;

        参数:b:   数据读入的数组
             off:   第一个读入字节应该被放置的位置在b中的偏移量 
             len:   读入字节的最大数量

       long skip(long n):在输入流中跳过n个字节,返回实际跳过的字节数(如果碰到流的结尾,则可能小于n)
       int available( ):返回在不阻塞的情况下可获取的字节数
        void close( ):关闭这个输入流
       void mark( int readlimit):在输入流的当前位置打一个标记(并非所有的流都支持这个特性),如果从输入流中已经读入的字节多余readlimit个,则这个流允许忽略这个标记
       void reset( ):返回到最后一个标记,随后对read的调用将重新读入这些字节,如果当前没有任何标记,则这个流不被重置。
       boolean markSupported( ):如果这个流支持打标记,则返回true

java.io.OutputStream:
       abstract void write(int n):写出一个字节数据
       void write(byte[] b)
       void write(byte[] b,int off, int len):写出所有字节或者某个范围的字节到数组b

参数:b:   数据写出的数组
     off:   第一个写出字节在b中的偏移量 
     len:   写出字节的最大数量

       void close( ):冲刷并关闭输出流
       void flush( ):冲刷输出流,也就是将所有缓冲的数据发送到目的地

首先,read和write方法在执行时都将阻塞,直至字节确实被读入或者写出,当完成对流的读写时,应该调用close方法关闭它,这个调用会释放掉十分有限的操作系统资源,如果一个应用程序打开了过多的流而没有关闭,系统的资源将被耗尽,关闭一个输出流的同时还会冲刷用于该输出流的缓冲区:所有被临时置于缓冲区中,以便使用更大的包的形式传递的字符在关闭输出流时都将被送出,但是如果不关闭文件,那么写出字节的最后一个包可能将永远也得不到传递,所以就需要我们的flush方法来认为的冲刷这些输出
我们再来看一张图
这里写图片描述
这里有四个附加接口:Closeable、Flushable、Readable和Appendable。
Closeable和Flushable非常简单,分别拥有下面的两个方法:
       void close( ) throws IOException
       void flush( )
InputStream、OutPutStream、Reader和Writer都实现了Closeable接口
而OutputStream和Writer还实现了Flushable接口

Readable接口只有一个方法:int read(CharBuffer cb)
CharBuffer 类拥有按顺序和随机的进行读写的方法,它表示内存中的一个缓冲区或者一个内存映像的文件

Appendable接口有两个用于添加单个字符和字符序列的方法:
Appendable append(char c)
Appendable append(CharSequence s)
CharSequence 接口描述了一个char值序列的基本属性,String,CharBuffer,StringBuilder和StringBuffer都实现了它
在流类家族中,只有Writer类实现了Appendable 接口

java.io.Closeable:
        void close( ):关闭这个Closeable,可能胡抛出IOException

java.io.Flushable:
        void flush( ):冲刷这个Flushable

java.lang.Readable:
        int read(CharBuffer cb ):尝试着向cb读入其可持有数量的char值,返回读入的char值得数量,或者当从这个Readable中无法在获取更多的值时返回-1。

java.lang.Appendable
        Appendable append(char c)
        Appendable append(CharSequence s)
向这个Appendable中追加给定的码元或者给定的字符序列中的所有码元,返回this

java.lang.CharSequence
        char charAt(int index):返回给定索引处的码元
        int length( ):返回在这个序列中的码元的数量
        CharSequence subSequence(int startIndex, int endIndex ):返回由存储在startIndex到endIndex -1处的所有码元构成的CharSequence
        String toString( ):返回这个序列中所有码元构成的字符串

二、组合流过滤器:
FileInputStream和FileOutputStream可以提供一个附着在磁盘上的输入流和输出流,而我们只需要向它的构造器提供文件名或者文件的完整路径。例如

FileInputStream fin = new FileInputStream("employee.dat");

这句代码可以用户目录下的employee.dat文件
与抽象类InputStream 和OutputStream一样,这些类只能在字节级别上进行读写,也就是说,我们只能从fin对象读取字节或字节数组。

byte b = (byte)fin.read();

如果我们只有DataInputStream,我们就只能读入数值类型:

DataInputStream din = ...;
double s = din.readDouble();

FileInputStream 中没有任何读入数值类型的方法,DataInputStream 中没有任何从文件获取数据的方法。

这是由于java使用了一种灵巧的机制来分离这两种职责,某些流可以从文件和其他外部的位置上获取字节(例如FileInputStream 和openStream),而某些流可以将字节组装到更有用的数据类型中(例如DataInputStream 和PrintWriter),我们必须对二者进行结合。

例如,我们想从文件中读入数字,需要创建一个FileInputStream ,然后将其传递给DataInputStream 构造器:

FileInputStream  fin = new FileInputStream("employee.dat");
DataInputStream  din = new DataInputStream(fin);
double s = din.readDouble();

又例如:流在默认的情况下是不能被缓冲区缓存的,也就是说每个对read的调用都会请求操作系统在分发一个字节。相比之下,我们可以请求一个数据块存并将其放到缓冲区中,这样每次读取字节都是在缓存中读取,会显得更加高效,下面就是使用缓冲机制的构造器序列:

DataInputStream din = new DataInputStream (
    new BufferedInputStream(
         new FileInputStream("employee.dat")));

再例如:当多个流链接在一起的时候,我们需要跟踪各个中介流,例如,当读入输入时,我们经常会要浏览下一个字节,以便于判断是不是我们想要的值:

PushbackInputStream pbin = new PushbackInputStream(
    new BufferedInputStream(
         new FileInputStream("employee.dat")));

现在我们可以预读下一个字节

int b = pbin.read();

如果下一个字节并不是我们所期望的,我们可以将其推回到流中

if(b != '<') pbin.unread(b);

但是对于读入和推回是应用于可回推输入流(PushbackInputStream)
如果你想能够预先浏览并且还可以读入数字,那就需要再结合DataInputStream 。

java的流类库相比其他的编程语言有一点麻烦,其他语言诸如缓冲机制和预览等细节都是自动处理的,但是java必须将多个流过滤器组合起来,不过呢,这种混合并匹配过滤器类以构建真正有用的流序列能力,将带来极大的灵活性。

java.io.FileInputStream
        FileInputStream(String name)
        FileInputStream(File file)
使用由name字符串或者file对象指定路径名的文件,创建一个新的文件输入流,非绝对路径名将按照相对于VM启动时所设置的相对目录解析

java.io.FileOutputStream
        FileOutputStream(String name)
        FileOutputStream(String name,boolean append)
        FileOutputStream(File file)
        FileOutputStream(File file,boolean append)
使用由name字符串或者file对象指定路径名的文件创建一个新的输出流,如果append为true,那么数据将被添加到文件尾部,而具有相同名字的已有文件不会删除;否则,这个方法删除所有相同名字的已有文件

java.io.BufferedInputStream
        BufferedInputStream(InputStream in)
创建一个带缓冲区的流,带缓冲区的输入流在从流中读入字符时,不会每次都对设备访问,而是从缓存中读取。当缓冲区为空时,会向缓冲区中读入一个新的数据块

java.io.OutputStream
        BufferedInputStream(OutputStream out)
创建一个带缓冲区的流,带缓冲区的输出流在收集要写出的字符时,不会每次都对设备访问。当缓冲区填满或当流被冲刷时,数据就被写出。

java.io.PushbackInputStream
        PushbackInputStream(InputStream in)
        PushbackInputStream(InputStream in,int size)
构建一个可以预览一个字节或者具有指定大小的回推缓冲区的流
        void unread(int b)
回推一个字节,它可以在下次调用read方法是被再次获取
参数 :b 要再次读入的字节

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章