I/O输入/输出

作者: 起个名忒难 | 来源:发表于2017-05-29 22:37 被阅读46次
    引言

    对程序语言设计者来说,设计一个令人满意的I/O(输入输出)系统,是件极艰巨的任务,摘自《Thinking in Java 》

    概述

    JAVA程序通过流来完成输入/输出。流是生产或消费信息的抽象,流通过Java的输入/输出系统与物理设备连接。JAVA的I/O通过java.io包下的类和接口来支持,主要是5个类和一个接口。

    • File:用于文件或目录的描述信息,例如生成新目录,修改文件名,删除文件,判断文件路径等
    • InputStream:抽象类,所有输入流的父类,定义了所有输入流具有的共同特征。
    • OutputStream:抽象类,基于字节的输出操作,所有输出流的父类,定义了输出流具有的共同特征。
    • Reader:抽象类,基于字符的输入类。
    • Writer:抽象类,基于字符的输出类。
    • Serializable:将Java对象转换成平台无关的二进制数据
    • RandomAccessFile:包装了一个随机访问的文件,实现了DataInput/DataOutput接口。
    File类

    一个File类的对象,表示了磁盘上的文件或目录,提供了与平台无关的方法来对磁盘上的文件或者目录进行操作,直接处理文件或目录,但是不能访问文件本身。

    在java.io包中提供了60多了类流:

    从功能上分为:

    • 输入流:从外部流向内存
    • 输出流:从内存流向外部

    从流的角色上分为:

    • 节点流:可以从/向一个特定的IO设备(磁盘,网络)读/写数据的流,直接连接物理设备或socket
    • 处理流:对一个已经存在的流(节点流)进行连接或者封装。

    从流的结构上分为:

    • 字节流 :InputStream OutputStream
    • 字符流 : Reader Writer

    在最底层,所有的输入输出都是以字节为单位的,基于字符的流只是为处理字符提供方便有效的方法。它们的操作方式几乎完全一样,只是操作的数据单元不同,字节流操作的数据单元是字符,字符流操作的数据单元是字符。

    类流全家福

    I/O流类.png

    从上面的图片中,可以看出无论是字节流和字符流都提供了对文件,数组,字符串,管道,对象,音频等内容的输入/输出流支持。

    对输入/输出流基类的简单介绍

    InputSteam 三个基本的读方法:
    abstract int read(): 读取一个字节数据,并返回读到数据的末尾,如果返回-1 , 表示都到了输入流的末尾。

    int read(byte[] b ): 将数据读入一个字节数组,同时返回实际读取的字节数,如果返回-1 ,表示读到了输入流的末尾。

    int read(byte[] b , int off , int len):将数据读入一个字节数组,同时返回实际读取的字节数,如果返回-1 ,表示读到了输入流的末尾,off指定在数组b中存放数据的起始位置,len指定读取的最大字节数。

    可能会有一个疑问,为什么上面的第一个方法是抽象,而其余两个read方法是具体的? 通过源码找到了想要的答案。第二个方法依靠了第三个方法,而第三个方法依靠了第一个方法实现,换句话说只有第一个read方法是与具体的I/O设备相关的,需要InputStream的子类来实现。源码如下:

    public abstract int read() throws IOException;
    
    public int read(byte b[]) throws IOException {
            return read(b, 0, b.length);  //调用第三个方法实现
        }
    
    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;   //没有字节返回-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;  //返回读取的字节数组长度
        }
    

    其它方法
    long skip(long n ):输入流中跳过n个字节,并返回实际跳过的字节数
    void close():关闭流
    。。。

    OutputStream 主要方法
    void write(byte[]] b ):将参数b中的字节写到输出流
    void write(byte[] b , int off ,int len):将参数b的从偏移量off开始len个字节写到输出流
    abstract viud write(int b ):先将int转换成byte类型,把低字节写入到输出流中
    void flush():将缓冲区中的字节全部输出,并清空缓存区
    void close():关闭输出流并释放与流相关的系统资源

    Reader里包含的3个读取方法:
    int read():
    int read(char[] c):
    int read(char[] c ,int off , int len) :

    Write包含的方法
    void write(char[] c)
    void write(String str)
    void write(String str, int off, int len)
    abstract flush()
    void close()

    根据上面图片做一个简单的总结:

    • FileInputStream和FileOutputStream 节点流,用于对文件读取或往文件中写入字节流。如果在构造FileOutputStream时,文件已经存在,则覆盖整个文件。
    • BufferedInputStream和BufferedOutputStream过滤流,需要使用已经存在的节点流来构造,提供了读写的效率。
    • DataInputStream和DataOutputStream 过滤流,需要使用已经存在的节点流来构造,提供了读写Java中基本数据类型的功能。
    • PipedInputStream和PipedOutputStream 管道流,用于线程间的通信,一个线程的PipedInputStream对象从另一个线程的PipedOutputStream 对象读取输入,只要管道流有用,必须提供同时构造管道输入流和管道输出流。
    • InputStreamReader和OutputStreamWriter类,用于处理字符流的基本类,用来在字节流和字符流之间搭一座桥。将字节流和字符流进行转换。
    • FileReader和FileWriter 可以创建一个可以读/写文件的类。
    • BufferedReader:通过缓冲输入提高性能。
    • SequenceInputStream:把多个InputStream合并为一个InputStream

    I/O流的链接示意图:

    流的链接示意图.png

    常用类
    FileInputStream , FileOutputStream , ByteArrayInputStream, ByteArrayOutputStream ,BufferedReader,BufferedWriter, ,InputStreamReader ,InputStreamWriter,FileReader ,FileWriter

    如何选择I/O流

    确定是输入还是输出

    输入:输入流 InputStream Reader
    输出:输出流 OutputStream Writer

    明确操作的数据对象是否是纯文本

    是:字符流 Reader,Writer
    否:字节流 InputStream,OutputStream

    明确具体的设备。

    文件:
    读:FileInputStream,, FileReader,
    写:FileOutputStream,FileWriter

    数组:
    byte[ ]:ByteArrayInputStream, ByteArrayOutputStream
    char[ ]:CharArrayReader, CharArrayWriter

    String:
    StringBufferInputStream(已过时,因为其只能用于String的每个字符都是8位的字符串), StringReader, StringWriter

    Socket流
    键盘:用System.in(是一个InputStream对象)读取,用System.out(是一个OutoutStream对象)打印

    是否需要转换流

    是,就使用转换流,从Stream转化为Reader、Writer:InputStreamReader,OutputStreamWriter

    是否需要缓冲提高效率

    是就加上Buffered:BufferedInputStream, BufferedOuputStream, BufferedReader, BufferedWrite


    少年听雨歌楼上,红烛昏罗帐。  
    壮年听雨客舟中,江阔云低,断雁叫西风。
    感谢支持!
                                            ---起个名忒难
    

    相关文章

      网友评论

        本文标题: I/O输入/输出

        本文链接:https://www.haomeiwen.com/subject/lidlfxtx.html