【IO】java IO 总结

来源:互联网 发布:java培训机构 知乎 编辑:程序博客网 时间:2024/04/19 07:37

1. 前言

IO 在 Java SE 的 jdk  中 单独占据了一个 包 ,其重要性可见一斑 ,而且 IO 流的操作 也经常让人 摸不着头脑,今特 总结,以备后续翻阅。

本次 测试 皆 基于 jdk1.8 , log  基于 slf4j + simple 包

gradle 依赖:

    testCompile group: 'junit', name: 'junit', version: '4.11'    compile group: 'org.slf4j', name: 'slf4j-api', version: '1.7.22'    compile group: 'org.slf4j', name: 'slf4j-simple', version: '1.7.22'


2. 总览

  IO 包下 接口 ,类 ,异常 如下分类

接口

Closeable  :   用于关闭数据对象, 释放对象所持有的资源DataInput:用于从二进制流中读取相应字节,并且重新构造为 java 的 基础类型DataOutput : 与 DataInput 相反,用于将 java 基础类型转换为 二进制,并写出到二进制流Externalizable:FileFilter:功能接口FilenameFilter:功能接口Flushable :用于将任何 缓冲了的 输出 写出到 流中ObjectInput : 继承上面的 DataInput ,用于读取对象,数组和字符串ObjectInputValidation:ObjectOutput : 继承 DataOutput,用于写对象ObjectStreamConstants:Serializable : 用于类的 序列化 ,空接口

类:

BufferedInputStream : 用于从字节输入流中读取字节, 增加缓冲区,内部为数组,默认 8192 字节BufferedOutputStream :写出字节到内部的输出流(output stream),增加缓冲区,先写到缓冲区再写到目的流BufferedReader :从字符-输入流中读取文本,增加缓冲区BufferedWriter:写文本 到 字符输出流ByteArrayInputStream :包含一个内部的缓冲区,这个缓冲区包含了从流中读取的字节。ByteArrayOutputStream:数据被写到 字节数组中保存,当写入数据时,缓冲区会自动增长,数据可以使用 toByteArray() 和 toString() 取得。CharArrayReader :同 ByteArrayInputStream,只是这里处理字符CharArrayWriter:同 ByteArrayOutputStream,只是这里处理字符Console : 始于 1.6DataInputStream :从输入流中读取 java 基础类型DataOutputStream :写入 java 基础类型到 输出流File :文件相关FileDescriptorFileInputStream :从文件中获取 输入字节流FileOutputStream:写数据到 一个文件的 输出流FilePermissionFileReader:读字符文件的方便类,默认编码,默认字节缓冲FileWriter:写字符文件的方便类FilterInputStream:装饰类FilterOutputStream:装饰类FilterReaderFilterWriterInputStream:所有代表字节输入流的父类InputStreamReader :字节流转换为字符流的桥梁LineNumberInputStreamLineNumberReaderObjectInputStreamObjectInputStream.GetFieldObjectOutputStreamObjectOutputStream.PutFieldObjectStreamClassObjectStreamFieldOutputStreamOutputStreamWriterPipedInputStreamPipedOutputStreamPipedReaderPipedWriterPrintStreamPrintWriterPushbackInputStreamPushbackReaderRandomAccessFileReaderSequenceInputStreamSerializablePermissionStreamTokenizerStringBufferInputStreamStringReaderStringWriterWriter

异常类

按照数据结构分类:

    字节流

    字符流

按照方向分类:

    输入流

    输出流


3.分析

3.1 输入 | 输出  字节流

输入流 UML 图 (列出了每个类的构造函数):


输出流 UML 图


InputStream

所有 输入字节流的顶层父类 。

方法: 

read() 方法 : 从 输入流 中读取 数据的下一个 字节。返回  0-255 的 整型。 如果没有数据则 返回  -1;此为抽象方法,需要子类实现。read(byte b[]):  调用下面的方法, read(b, 0, b.length);read(byte b[], int off, int len):从 输入流中 off 位置开始 读取 len 长度的字节 保存到 字节数组 b 中


方法分析:

read() 方法 需要子类 具体实现;

read(byte b[],int off,int len) : 内部实现方法为 ,使用循环,调用单字节的读方法,将读到的 int 类型强转为 byte 保存到 字节数组 b 中。



OutputStream

所有 输出字节流的顶层父类 。

方法:

write(int b):将 b 写到 输出流中;参数 b 为 整型,一个 int 有 4个 字节,每个字节有8 位;              这里会将 b 的 低8位 按顺序 写到 输出流中, 而 b 的 高 24 位 将被 忽略 。此为抽象方法,需要子类实现write(byte b[]): 调用了下面的方法  write(b, 0, b.length)write(byte b[], int off, int len):将 数组 b 中 从 off 位置开始 写入 len 个 字节到该 输出流中;

flush():  空方法,没有做任何实现。



方法分析:

write(int b) 为 抽象方法,需要子类实现;

而 write(byte b[]) 和 write(byte b[], int off, int len) 相同, 该方法 的实现是 循环 字节数组 b ,然后调用单字节的写入方法 write(int b) .



所有方法:                                                           

            


序列化流

ObjectInputStream

用于 输入流中 基本数据类型 和 对象 的 反序列化,即 读出 其中的 基本数据类型 和 对象。 和 ObjectOutputStream 搭配使用

感觉应该也是提供了一个功能 ,可以在流中 传递 对象 和 基本数据类型的 功能。


构造函数:需要提供一个  介质,可以是文件流 FileInputStream

ObjectInputStream(InputStream in): 使用 输入流构造一个 ObjectInputStream,如果输入流中保存着序列化的对象则可以使用 readObject() 读取

方法:

不仅 对 InputStream 类中的 read 方法 进行了 实现和重写(override),另外还提供了其他的方法,如 readObject(),readByte(),readBoolean() 等对java 基本数据类型的读取;


方法分析:

该输入流 提供了多个 内部类 来实现 这些 读方法,

BlockDataInputStream:InputStream 子类,两种模式

           default mode: 数据不会被 缓冲

           block mode: 数据在读取的同时 ,会缓冲到 内置的 字节数组中

PeekInputStream :InputStream 子类, 支持 单个字节 peek 操作


read() 方法 会先调用 BlockDataInputStream 的 read() 方法,然后再调用 PeekInputStream 的read()方法,最后调用其 构造函数传入的 InputStream 流的 read() 方法;

read(byte b[],int off,int len) 的流程 同 read(),最后调用 传入的输入流的 read(byte b[],int off,int len)方法;


ObjectOutputStream

将 基本数据类 和 对象的序列 写到 输出流(OutputStream),需要先序列化;和 ObjectInputStream 搭配使用


构造函数: 需要提供一个 介质,可以是文件流

ObjectOutputStream(OutputStream out) :使用输出流构造一个 ObjectOutputStream ,可以向 输出流中 写入序列化的对象数据 writeObject()


方法:

不仅对 父类 的 write 方法 进行了实现 和 重写(override),还提供了其他的写 基本数据类型的方法


方法分析:

提供了内部类 来重写 数据的写出操作:

BlockOutputStream:


write(int b): 调用了 BlockOutputStream 的 write()方法 ,然后 将 数据 保存在 BlockOutputStream 的 内置 字节数组中,容量 1024字节

write(byte [] buf,int off,int len):内部调用了 BlockOutputStream 的  write(byte[] b, int off, int len, boolean copy) 方法,这里会执行while 循环并分情况处理,直至把 buf 中 的数据写完,以达到效率最优:

               如果 当前 内置 字节数组 b 中 已经写 满,则 直接调用 构造函数传入的 输出流  的 写操作 ;并将 pos 置为 0,再次循环;

               如果 要读取的 数据的 buf 长度 大于 内置 字节数组 的最大长度,则直接调用 构造函数传入的 输出流  的 写操作,将 0-b.length 长度的数据写到输出流中,再次循环;

               否则,将 buf 中的数据 保存到 内置 数组 b 中,再次循环;

flush(): 内部调用了 BlockOutputStream 的 close()方法,内部 再次执行将 内置字节数组 b 中的 数据写入到 构造函数传入的 输出流中,然后执行  构造函数传入的 输出流   的  close()方法;


测试:

    @Test    public void ObjectInputStream()throws Exception {        //创建序列化对象        User user = new User();        user.setName("xlch");        user.setEmail("xlch@163.com");        user.setCreateTime(Date.from(LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant()));        try(FileOutputStream fileOutputStream = new FileOutputStream("D:\\test.txt");            ObjectOutputStream out = new ObjectOutputStream(fileOutputStream);            FileInputStream fileInputStream = new FileInputStream("D:\\test.txt");            ObjectInputStream in = new ObjectInputStream(fileInputStream);) { //目录下的文件并不存在, FileOutputStream 会先创建            //写入序列化对象到 文件流中            out.writeObject(user);            //从文件流中写出序列化对象            User user1 = (User) in.readObject();            logger.info("user name is {}",user1.getName());            logger.info("user createTime is {}",user1.getCreateTime());        }    }





字节数组 流 

ByteArrayInputStream

 包含一个内置的 缓冲区 ,这个缓冲区保存着字节,这些字节可以读取出来 。有一个内置的 计数器 跟踪 下一个字节,用于支持 read() 方法。关闭流对方法调用无影响。

简单的说 : 将  存储内容的字节数组 构造成  输入流,保存的是 字节数组中的内容 。然后利用其中的方法可以 读出 字节数组中的数据

构造函数:内部包含 字节数组 这个 介质 ,但是需要传递数据给这个 介质

ByteArrayInputStream(byte buf[]); //使用 buf  作为 缓冲数组 创建一个 ByteArrayInputStream 对象。初始位置 pos 为 0 ,初始长度为 buf 的长度ByteArrayInputStream(byte buf[], int offset, int length)  // 初始位置为 offset ,初始长度为 length

方法:

read() : 从输入流中(介质为 字节数组)读取 下一个字节 ,返回该字节 的 低八位 整型类型,如果返回 -1 说明无数据可读。read(byte b[], int off, int len) :从 b 的 off 位置开始存储, 读取的长度 为 len



ByteArrayOutputStream

构造一个空的 输出流,用于将 外部 字节数据 写到 内置的 字节数组缓冲区中 。内置的 缓冲区 会 随着 数据的写入 而 自动增长 . 其中 的 数据可以使用  toByteArray() 方法 和 toString() 方法 取回 。同样关闭流 对方法的调用无影响 。 相当于一个 容器 ,暂时保存读取到的字节数据。经常会用到


构造函数: 内部包含一个可保存数据的 字节数组的介质 ,不需要提供

ByteArrayOutputStream()  // 构造一个默认容量为 32 字节的 输出流ByteArrayOutputStream(int size) // 指定内置缓冲区容量大小的 输出流

方法:

write(int b): 写一个字节到 内置的 buffer 缓冲区 中writeTo(OutputStream out) : 将 所有数据 写到 另一个 输出流中 ,其实调用了 out.write(buf,0,count)flush():冲刷 输出流,迫使任何 已缓存的 输出字节 写出 。唯一的联系是 :调用这个方法代表  之前由 输出流的实现类 写出的 任意的字节 已经被 缓冲,则这些数据必须立即被写到目的地。如果这些流的 目的地 是由内部操作系统 提供的一个抽象,比如 一个文件 ,那么 flush 只能保证之前写的数据传递到 操纵系统中等待写,而不会保证直接 写到物理介质上,如 磁盘。

    只有 BufferedOutputStream,BufferedWriter,ObjectOutputStream 以及 Printxxxx 相关的类 需要调用 flush  ,而其他的 输出流 对 flush() 方法没有实现 。摘自stackoverflow



所有方法:

  

测试:

package com.ycit.io;import org.junit.Test;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import java.io.ByteArrayInputStream;import java.io.ByteArrayOutputStream;import java.io.IOException;/** * Created by xlch on 2017/1/11. */public class InputStreamTest {    private static final Logger logger = LoggerFactory.getLogger(InputStreamTest.class);    /**     * 一个字节一个字节的读     */    @Test    public void byteArrayInputStreamTest()throws Exception {        byte [] bytes = new byte[]{'a','b'};        ByteArrayInputStream in = new ByteArrayInputStream(bytes);        int size = in.available();        logger.info("the available size == {}", size); // 2        int binary = in.read();        while (binary != -1) {            logger.info("the binary is {}",binary); //分别 输出 97 98            binary = in.read();        }        in.close();    }    /**     * 一次读取多个数据     */    @Test    public void readUseBuffer() throws IOException {        byte [] data = new byte[]{'a','b','c'};        try(ByteArrayInputStream in = new ByteArrayInputStream(data)){            byte [] buffer = new byte[1024];            int len = in.read(buffer,2,5);            while (len != -1) {                logger.info("read data size is {}", len); //3                logger.info("buffer[0] is {}", buffer[0]); //0                logger.info("buffer[2] is {}", buffer[2]); //97 即 a 的 Ascii                logger.info("buffer[3] is {}", buffer[3]); //98                logger.info("binary data is {}", new String(buffer));//abc                len = in.read(buffer);            }        }catch (IOException e){            e.printStackTrace();        }    }    @Test    public void byteArrayOutputStream() throws Exception {        ByteArrayOutputStream out = new ByteArrayOutputStream();        out.write('a');        logger.info("out convert string is {}", new String(out.toByteArray(), "utf-8")); // a        logger.info("out convert string is {}", out.toString()); // a        out.write(new byte[]{'b','c','d'}, 1, 2);        logger.info("out convert string is {}", out.toString()); // acd        //将 ByteArrayInputStream 中的数据 写到  ByteArrayOutputStream 中        byte [] bytes = new byte[]{'a','b'};        ByteArrayInputStream in = new ByteArrayInputStream(bytes);        byte [] buffer = new byte[1024];        int len = in.read(buffer);        while (len != -1) {            out.write(buffer);            len = in.read(buffer);        }        logger.info("out convert string is {}", new String(out.toByteArray(), "utf-8")); // acdab    }}



文件流


FileInputStream

从 一个文件 获取 输入字节 ,文件可读性依赖于 主机环境。 用于读取 原始数据 流 ,比如 图像 数据 。读取字符流 考虑使用 FileReader .

和 ByteArrayInputStream 类似 ,只不过 获取数据来源不同,一个是 字节数组 ,一个是文件。而且难易程度不同,文件的读取和系统硬件相关,需要使用 native 方法读取。

简单的说:由 文件 构造一个 输入流 , 流中的数据 就是 文件中的数据 ,可以 读取 流中的 数据,相当于间接读取了 文件的数据 。

构造函数: 不存在介质 ,需要 提供一个包含数据的 文件 介质,可从中读取

FileInputStream(String name) :提供 文件路径,实际调用下面的构造函数FileInputStream(File file):直接 提供 File 对象,内部使用 FileDescriptor 代表 该文件的连接FileInputStream(FileDescriptor fdObj):提供文件描述符对象,该对象充当 通向 代表 一个 打开的文件、打开的 套接字、或者其他的源 的一个不透明句柄 ,                    FileDescriptor 主要用来 创建 FileInputStream 或者 FileOutputStream 并且 包含之 。                    应用不应该创建 他们自己的文件 描述符

方法:

read() : 从输入流中(即文件中) 读取一个字节read(byte b[]) : 从输入流中读取  b.length 个 字节read(byte b[], int off, int len)



方法分析:

所有的 read() 方法 的内部实现 都是 调用 内部的  native 方法 ,native 方法 是 通过 底层 的 C/C++ 实现的


FileOutputStream


用于 将 数据 写到一个文件中 或者 写到一个 FileDescriptor 中。

简单的说: 同样是由 文件 构造的 输出流,目的是 向 文件中写数据

构造函数:不存在 介质,需要提供一个文件介质,用于保存即将写入的数据

FileOutputStream(String name):使用文件路径,实际上调用下面的构造函数,第二个参数默认为 falseFileOutputStream(String name, boolean append) : 使用文件路径,同时指定 是覆盖还是 在后面添加FileOutputStream(File file)FileOutputStream(File file, boolean append)FileOutputStream(FileDescriptor fdObj)
方法:

write(int b) :  将 b 写入到 文件 输出流中 ,即写入到文件中write(byte b[]):将 b.length 个 字节 写入到 文件 输出流中write(byte b[], int off, int len)


方法分析:

所有的 read() 方法 的内部实现 同样都是 调用 内部的  native 方法 ,native 方法 是 通过 底层 的 C/C++ 实现的;

flush():并没有对 父类 的 flush 方法 进行重写(override);


所有方法:

NOTE: FileInputStream 指定 文件后 ,如果文件不存在,或者指定的是一个目录,或者其他的原因不能打开 则 抛出 FileNotFoundException 异常

               FileOutputStream 指定文件后,如果文件存在,但是是一个目录,或者 文件不存在 但是不能创建,或者因为其他原因不能打开,则抛出 FileNotFoundException

也就是说 ,FileOutputStream 在发现文件不存在后可以 自动创建文件 ,而 FileInputStream 在 发现文件不存在后则 直接 抛出异常 !

  /**     * 一个字节一个字节的读     * @throws Exception     */    @Test    public void FileInputStreamTest()throws Exception {        String sourcePath = "E:\\study\\jdk\\io\\InputStream.PNG";        String destinationPath = "E:\\study\\jdk\\io\\11.PNG";        try(FileInputStream in = new FileInputStream(sourcePath); FileOutputStream out = new FileOutputStream(destinationPath)){ // 这个时候 流中已经包含了这张图片            int binary = 0;            while ((binary = in.read()) != -1) {                out.write(binary);            }        }catch (Exception e) {            e.printStackTrace();        }finally {            logger.info("流已关闭");        }    }    /**     * 一次读取多个,并不是缓冲     * @throws Exception     */    @Test    public void useBuffer()throws Exception {        String sourcePath = "E:\\study\\jdk\\io\\InputStream.PNG";        String destinationPath = "E:\\study\\jdk\\io\\11.PNG";        try(FileInputStream in = new FileInputStream(sourcePath); FileOutputStream out = new FileOutputStream(destinationPath)){ // 这个时候 流中已经包含了这张图片            byte [] buffer = new byte[32];            int len = 0;            while ((len = in.read(buffer)) != -1) {                out.write(buffer);            }        }catch (Exception e) {            e.printStackTrace();        }finally {            logger.info("流已关闭");        }    }


PipedInputStream

管道输入流 应该和 管道输出流  相连 , 管道输入流 提供 字节数据 ,这些数据被 写到 管道输出流中 。通常 和 多线程有关

测试:略


PipedOutputStream

测试:略



SequenceInputStream

用于 批量处理 多个 输入流

构造函数:

SequenceInputStream(Enumeration<? extends InputStream> e) SequenceInputStream(InputStream s1, InputStream s2) :内部同样是使用 Vevtor 转为 Enumeration



FilterInputStream

包含一些其他的 输入流的 实例 ,通常用于转化数据  或者 提供额外的功能 (所谓的 装饰模式中的 Decorator 角色,参看案例),重写(override)了 InputStream 的所有方法.


FilterOutputStream

同上,扮演 装饰模式 中的 Decorator 角色。



功能性 流

BufferedInputStream    

用来 给 另一个 输入流 提供 缓冲 输入的能力。先缓冲一般部分数据到内置的缓冲区数组中,然后从 缓冲区数组中读取字节。  参看 源码分析

当对象创建时,内置的 缓冲区 数组 也被创建,当从 流中读取 字节或者 跳过字节时,内置 的 缓冲区数组如果有必要的话 会 重新从 提供的输入流中重新装填 数据。

构造函数:

BufferedInputStream(InputStream in):调用下面的构造函数,默认缓冲区容量为 8192字节BufferedInputStream(InputStream in, int size): 为 in 输入流 增加 缓冲功能,缓冲区数组大小为 size


方法分析:

主要是 利用 内置 字节数组 缓冲区 buf 来 缓冲数据

read(int b):从内置字节数组缓冲区中读取一个字节,如果缓冲区没有可读数据,则 调用 fill()方法填充;

read(byte b[], int off, int len):如果 len 长度 大于 内置字节数组缓冲区的长度,则 直接调用 构造函数传入的 输入流 参数 调用 read 方法;

                         


BufferedOutputStream

将要写的字节 先写到 缓冲区数组中,然后一起写到 目的 流中。

构造函数:

BufferedOutputStream(OutputStream out)  :调用下面的构造函数,默认初始容量 8192字节BufferedOutputStream(OutputStream out, int size):指定内置缓冲区数组的初始容量


方法分析:



测试:

    /**     * 文件 copy  使用缓冲     * @throws Exception     */    @Test    public void userBuffered() throws Exception {        String sourcePath = "E:\\study\\jdk\\io\\InputStream.PNG";        String destinationPath = "E:\\study\\jdk\\io\\11.PNG";        try(BufferedInputStream in = new BufferedInputStream(new FileInputStream(sourcePath));            BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(destinationPath));){            byte[] buffer = new byte[1024];            int len = 0;            while ((len = in.read(buffer)) != -1) {                out.write(buffer);                out.flush();            }        }    }

DataInputStream

提供一个功能,用来 读取 java 中基本数据类型的功能 ,输入流的原型 只能读取 字节。和 DataOutputStream 对接

构造函数:

DataInputStream(InputStream in)

DataOutputStream

提供一个功能,用来直接写入 java 的基本数据类型;和 DataInputStream 对接

构造函数:

DataOutputStream(OutputStream out)

测试:

    @Test    public void dataInputStreamTest() {        try(FileOutputStream out = new FileOutputStream("D:\\test.txt");DataOutputStream dataOut = new DataOutputStream(out);//文件不存在,先调用 FileOutputStream 会创建文件            FileInputStream in = new FileInputStream("D:\\test.txt");DataInputStream dataIn = new DataInputStream(in);){//文件不存在,如果先调用 FileInputStream 会 抛出 FileNotFoundException            dataOut.writeDouble(0.12);            Double d = dataIn.readDouble();            logger.info("the double is {}", d);//0.12        }catch (Exception e) {            e.printStackTrace();        }    }


PrintStream

提供一个功能,可以向 输出流中 方便的写 各种数据值 ,不在局限于 byte 类型;

System.out.println 中 的 System.out 返回的 即是  PrintStream 对象

构造函数:

PrintStream(OutputStream out):调用下面的构造函数 ,boolean 默认为 falsePrintStream(OutputStream out, boolean autoFlush) : autoFlush 是否自动刷新PrintStream(OutputStream out, boolean autoFlush, String encoding):PrintStream(String fileName): 指定一个文件路径,如果不存在会自动创建PrintStream(String fileName, String csn):指定编码方式PrintStream(File file)PrintStream(File file, String csn):指定编码方式


测试:

    @Test    public void printStream() {        String path = "D:\\test.txt";        try(PrintStream out = new PrintStream(path);){            out.print(true);        //向 文件中写入 boolean 值            out.println("新年快乐");  //向文件中写入 字符串,并且 换行 ,相当于执行了写操作和 newLine()            out.flush();        }catch (Exception e) {            e.printStackTrace();        }    }



3.2 输入 | 输出 字符流

输入字符流 UML


输出字符流 UML


Reader

读数据到 字符流中

方法:

read(java.nio.CharBuffer target) :1.5 后添加的方法,读字符串到 字符缓冲区read():读取  单个字符,实际上也是调用了read(char cbuf[], int off, int len) 方法),返回 低 16位read(char cbuf[]) :调用下面的构造函数,read(cbuf, 0, cbuf.length);read(char cbuf[], int off, int len):从 cbuf 的 off 位置开始存储,读取 len  个 字符到 cbuf 数组 中;返回读取字符个数该方法为抽象方法,需要子类实现



方法分析:

都是依赖于 read(char cbuf[], int off, int len)  抽象方法的实现;

read(): 内部构造一个空的 字符数组 ,然后调用  read(char cbuf[], int off, int len)  方法读取;

Writer

写数据到 字符流中

方法:


write(int c) : 写 单独的字符 。c 的 低 16位 字节被写入到 字符流中,c 的 高 16位 被忽略。write(char cbuf[]):调用下面的构造函数,write(cbuf, 0, cbuf.length):写字符数组到 流中。write(char cbuf[], int off, int len):从 cbuf 的 off 位置开始, len 的长度 写到 流中。抽象方法write(String str): 写一个字符串 到 流中,调用下面的构造函数,write(str,0,str.length())write(String str, int off, int len):将 str 的 off位置,到len 的长度 的 字符串 写到流中append(CharSequence csq): 字符序列 csq 添加到 输出流的后端,返回这个输出流,内部使用的是 write(csq.toString())方法append(CharSequence csq, int start, int end):内部调用: write(cs.subSequence(start, end).toString());Writer append(char c):内部调用 write(c)

flush() :抽象方法


方法分析:

都调用了  write(char cbuf[], int off, int len) 抽象方法;



所有方法图:



StringReader

源是 字符串的 字符流。通过字符串构造对象,提供数据

类似于 字节流的 ByteArrayInputStream

构造函数:

StringReader(String s) :内部含有一个字符串对象保存 s

StringWriter

将需要输出的数据 保存在 内部的 StringBuffer 中 ,后续可以通过 toString() 和 getBuffer().toString() 方法 获取字符串。

类似于 字节流的 ByteArrayOutputStream ,可以充当 字符的容器

构造函数:

StringWriter() : 未指定内部 StringBuffer 容量StringWriter(int initialSize):指定内部StringBuffer 容量为 initialSize


分析:write  写方法 都是 直接 append 到 内置的  StringBuffer 中 , flush 方法 没有具体实现


测试:

 /**     * 单字节的从输入流中读出,并写入到 输出流中保存     */    @Test    public void stringReaderTest() {        String data = "hello world~~";        try(StringReader in = new StringReader(data);            StringWriter out = new StringWriter();){            int len = 0;            while ((len = in.read()) != -1) {                out.write(len);            }            logger.info("the data is {}", out.toString());//hello world~~            logger.info("the data is {}", out.getBuffer().toString());//hello world~~        }catch (Exception e) {        }    }    /**     * 一次读取多个字符     */    @Test    public void readMore() {        String data = "新年快乐~~";        char [] buf = new char[1024];        try(StringReader in = new StringReader(data);){            int len = 0;            while ((len = in.read(buf)) != -1) {                logger.info("character is {}", new String(buf));//新年快乐~~            }        }catch (Exception e) {            e.printStackTrace();        }    }


CharArrayReader

源为 字符 数组 ,类似于 上面的 StringReader

CharArrayWriter

源为 字符数组 ,类似与 StringWriter

分析: write 写方法 都是直接 写到 字符数组 中 ,flush 方法 没有具体 实现

测试:


转换流

InputStreamReader

读取 字节数据,并使用指定的编码方式 把 读取的字节数据 解码成 字符串。提升效率 可以考虑使用 缓冲输入流(BufferedReader)。

字节流 → 字符流 。

BufferedReader in = new BufferedReader(new InputStreamReader(System.in));

构造函数:

参数为 字节输入流 和 编码方式

InputStreamReader(InputStream in):使用默认的编码方式 创建一个 对象InputStreamReader(InputStream in, String charsetName):指定编码方式InputStreamReader(InputStream in, Charset cs):指定编码方式InputStreamReader(InputStream in, CharsetDecoder dec)


方法分析:

内部使用 java.nio.cs 包中的 类 StreamDecoder  实现读取 单个字节和 多个字节




OutputStreamWriter

使用 指定的 编码方式 将被写入的字符   编码成 字节。 内置 了  StreamEncoder 类

字符流 → 字节流 ,提高性能 可以使用 缓冲流

  Writer out = new BufferedWriter(new OutputStreamWriter(System.out));

构造函数:参数为 字节输出流 和 编码方式

OutputStreamWriter(OutputStream out) :使用默认的编码方式OutputStreamWriter(OutputStream out, String charsetName)OutputStreamWriter(OutputStream out, Charset cs)OutputStreamWriter(OutputStream out, CharsetEncoder enc)


方法分析:

内部使用 java.nio.cs 包中的 类 StreamEncoder  实现写 单个字节和 多个字节 到 目标流中


测试:

    @Test    public void inputStreamReaderTest()throws Exception {        String path = "D:\\test.txt";        byte [] buffer = new byte[1024];        try(FileOutputStream out = new FileOutputStream(path);            OutputStreamWriter outputStreamWriter = new OutputStreamWriter(out,"utf-8");            FileInputStream fileInputStream = new FileInputStream(path);) {            outputStreamWriter.write("新年快乐~~");// 将字符串写到了字节流 FileOutputStream 中,即所谓的 字符 → 字节            outputStreamWriter.flush();//此时如果不 冲洗 则 下面读不到。            fileInputStream.read(buffer);            logger.info("result is {}", new String(buffer, "utf-8"));//新年快乐~~        } catch (FileNotFoundException e) {            e.printStackTrace();        }    }    /**     *  copy 文件     */    @Test    public void useTogether() {        String path = "D:\\test.txt";        String copyPath = "D:\\copy.txt";        char buffer[] = new char[1024];        try(FileInputStream inputStream = new FileInputStream(path);Reader in = new InputStreamReader(inputStream, "utf-8");//保持和文件保存时的 编码方式相同            FileOutputStream outputStream = new FileOutputStream(copyPath, false);Writer out = new OutputStreamWriter(outputStream, "utf-8")) {            int len  = 0;            while ((len = in.read(buffer)) != -1) {                out.write(buffer);                logger.info("the buffer string is {}", new String(buffer));            }        } catch (IOException e) {            e.printStackTrace();        }    }

FileReader

读取字符文件 的方便类 , 相当于 构造 InputStreamReader 的两个参数 InputStream 为 FileInputStream ,编码方式 使用 默认的编码方式 ,生成的类定义为 FileReader

InputStreamReader in = new InputStreamReader(new FileInputStream(path));等同于FileReader in = new FileReader(path);
如果需要需要自定义 ,则使用 InputStreamReader

InputStreamReader 的 子类

构造函数:

FileReader(String fileName)FileReader(File file)FileReader(FileDescriptor fd)


FileWriter

类同 FileReader




BufferedReader

功能性流,提供缓冲的功能

构造函数:

BufferedReader(Reader in): 使用默认容量的 缓冲区字符数组 创建一个缓冲了的字符 输入流,默认为 8192 ,调用下面的构造函数BufferedReader(Reader in, int sz):内置的字符数组容量为 sz

方法:

readLine(boolean ignoreLF) : 读取文本的一行,"\r","\n" 或者回车 代表 一行的末端。参数表示下一个"\n" 是否被跳过



BufferedWriter

功能性流,提供缓冲的功能,内置了 一个字符数组  char cb[]

BufferedWriter(Writer out):使用默认容量的输出缓冲 创建一个 缓冲字符的输入流BufferedWriter(Writer out, int sz):容量为 sz

方法:

newLine():另起一行


分析:

BufferedWriter 类中的  写方法(write(int b))是将 要写的数据 写入到内置的 字符数组 cb 中 ,当达到 cb 规定的长度后(默认 8192,可自定义)调用flushbuffer() ,将 cb 中数据一起写入到 目的流 out 中; write(byte cbuf[], int off, int len):如果请求的 buffer  长度 没有超出内置 字符数组 cb 的 长度,则会先将 cbuf 中的数据保存到 cb 中 ,等cb  达到 cbuf 长度 ,再调用flushbuffer();如果请求的 buffer  长度 超过了 cb 的 长度 ,会先清洗(flushbuffer())  再 直接 将 cbuf 写入到 目的流 out 中。


测试:

    @Test    public void bufferedTest() {        String path = "D:\\test.txt";        String copyPath = "D:\\copy.txt";        char buffer[] = new char[1024];        try(Reader in = new BufferedReader(new FileReader(path));Writer out = new BufferedWriter(new FileWriter(copyPath))) {            int len  = 0;            while ((len = in.read(buffer)) != -1) {                out.write(buffer);                logger.info("the buffer string is {}", new String(buffer));            }        }catch (IOException e) {            e.printStackTrace();        }    }


PrintWriter

PrintStream 用于处理 字节输出流 , PrintWriter 既可以处理 字节输出流 , 也可以处理 字符输出流 。即 构造函数增加了  Writer 参数。


4. 总结


个人觉得要想 灵活使用,死记硬背那些 口诀是不够的,关键是要掌握 每个流的 构造函数,其次闻其类名知 其意,而同一类流 提供的方法基本一致。

构造函数 可以让我们快速的构造一个 流来使用 ,而通过构造方法 也可以确定其 主要用途。

个人理解,流 相当于 把 存有数据的 载体(介质) 流化,这个流化的目的是 提供 一套 对 载体 操纵数据的方法(读写)。

输入流


输入流用于提供数据,提供 读出操作,可以单个的读 出( read() ),也可以一次读取多个 read(<...>);

InputStream  Reader 之类,输入流的构造 需要 提供 已存在的 数据存储载体(介质),以便从载体中读取(read())数据,如字节输入流  有的需要提供 一个 字节数组;有的需要提供一个已存在的文件的文件路径或者文件对象;亦或是直接提供 已创建的 输入流 构造另一个输入流,这类流还是基于 上面提到的 流 。

明白了这些就可以轻松 构造出 输入流 对象 ;

其次

闻其名知其意,如 ObjectInputStream ,观其构造函数,传入的是另一个输入流,这个输入流中保存着对象。字面上理解就是 与对象相关的字节输入流,应该可以想到 其功能是 从构造其对象的  输入流的载体中读取 序列化的对象 ,这个过程应该叫做 反序列化。


输出流

输出流用于 保存数据,提供写入操作,可以单个的写入(write()),也可以一次写入多个(write( <...> ));

OutputStream Writer 之类,输出流 需要指定数据保存的介质,如 内置的 缓冲区(可以不用指定),文件路径等。


字节流

读 和 写 的 操作 是以· 字节 为 单位 (其中的 write( int b) 是 为了 和 read() 返回的 int 类型对应),如

read();                                       // 读一个字节,返回 int ,读取字节的 低八位read(byte [] buffer);                    //读多个字节,返回读取个数read(byte [] buffer, int off, int len);  //读多个字节,返回读取个数write(int b);                       // 写一个字节,需要写入字节的低八位write(byte [] buffer);             // 写多个字节write(byte [] buffer, int off, int len);//写多个字节




字符流

读 和 写的 操作 是以 字符 为单位 (其中的 write( int b) 是 为了 和 read() 返回的 int 类型对应),如

read();      //读一个字符,返回 int, 低 16位read(char cbuf[]);  //读多个字符,返回个数read(char cbuf[], int off, int len); //读取多个,返回个数write(int c); // 写一个字符, c 代表 字符的 低16位write(char cbuf[]);  //写多个字符write(char cbuf[], int off ,int len); //写多个字符write(String) // 写 一个字符串。。 


0 0
原创粉丝点击