Java IO流浅析

Java IO流浅析

困而学,学而知

最近看了很多关于Java I/O相关的知识,但是乏于学习得不系统,所以成效甚微,但总归也是有些许收获。无论如何,既然有收获,也需得有总结。

为什么需要I/0流

  1. 当我们的程序需要从硬盘,网络,或其他应用程序中读取或写入数据时候,数据传输量可能很大,而我们的内存或带宽有限,无法一次性读取获取写入大量数据。
  2. 而流(Stream)可以实现一点一点的逐步传输数据。
  3. 想想我们是怎样下载一个大文件的, 下载软件(例如x雷)并不会占用你内存很大的空间, 而只是在内存划分一个缓冲区, 一点一点地下载到自己的内存(缓冲区满了再写到硬盘),。

IO流的分类

先来说说**,具体什么是流?在Java中,可以从其中读入一个字节序列的对象称作输入流**,而可以向其中写入一个字节序列的对象成为输出流。这些字节序列的来源地和目的地可以是文件、网络连接甚至是内存块。抽象类InputStream和OutputStream构成了输入/输出类层次结构的基础。

InputStream和OutputStream是面向字节的,而Reader和Writer是面向字符的。

我们来按照不同的方式给IO流分类。

按照数据来源或操作的对象区分

如果按照数据来源或者操作的对象来看的话,可以分为:

  • 文件(file):FileInputStream、FileOutputStream 、FileReader、FileWriter
  • 数组
    • 字节数组: ByteArrayInputStream、ByteArrayOutputStream
    • 字符数组: CharArrayReader、CharArrayWriter
  • 管道操作: PipedInputStream、PipedOutputStream、PipedReader、PipedWriter
  • 基本数据操作: DataInputStream、DataOutputStream
  • 缓冲操作: BufferedInputStream、BufferedOutputStream、BufferedReader、BufferedWriter
  • 打印: PrintStream、PrintWriter
  • 对象序列化和反序列化: ObjectInputStream、ObjectOutputStream
  • 转换: InputStreamReader、OutputStreamWriter
  • 字符串(Java8 已废弃): StringBufferInputStream、StringBufferOutputStream、StringReader、StringWriter

数据源节点也可以再进行二次处理,使数据更加容易使用,所以还可以划分成节点流和处理流。

按照数据来源或操作的对象区分

上图中出现了两个新概念:处理流和节点流。

节点流

节点流:直接与数据源相连,读入或读出。 直接使用节点流,读写不方便,为了更快的读写文件,才有了处理流。

处理流

处理流和节点流一块使用,在节点流的基础上,再套接一层,套接在节点流上的就是处理流。如BufferedReader.处理流的构造方法总是要带一个其他的流对象做参数。一个流对象经过其他流的多次包装,称为流的链接。

按照数据流的方向区分

按照处理数据流的方向来区分的话,可以分为输入流和输出流。

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

按照数据的方向来区分IO流很好理解。如果要操作文件,肯定要先读取文件,执行操作之后,然后再输出文件。这也就对应了流的输入和输出。

还有一点就是,流的来源地和目的地可以是文件、网络连接甚至是内存块。

按照数据流的类型来区分

按照处理数据流的类型来区分的话,可以分为字符流和字节流。

  • 字节流(8个字节):InputStream、OutputStream
  • 字符流(16个字节):Reader、Writer

按照数据的类型来区分

字节流和字符流的一个问题

关于字节流和字符流,有一个问题就是既然有了字节流了,为什么还需要字符流?

我在Java核心技术卷中找到了如下的解释:

因为面向字节的流不便于处理以Unicode形式存储的信息(Unicode中每个字符都是用了多个字节来表示),所以从抽象类Reader和Writer中继承出来了一个专门用来处理Unicode字符的单独的类层次结构。这些类拥有的读入和写出操作都是基于两字节的Unicode码元的,而不是基于单字节的字符,也就是面向字符的。

也就是说字符流的出现是为了处理多个字节的问题。虽然在处理字符的时候很方便,但是也带来了一些问题,比如说编码问题。字符流在处理这些字符数据的时候,会涉及到字符的编码和解码。这不是本文讨论的话题,后面有机会再说说。F

关于这个答案,网上各所纷纭,我只是找了一个比较被广泛认同的书籍的中的一段话,如果有疑问可以评论大家一起讨论。

Java IO流的一些API

Java 关于IO的API有很多,如果要在这一篇文章中写出来,整篇文章就会很长了。并且网上有很多类似的文章,各位看官可以google或baidu一下。

缓冲流

自己之前也不是怎么会用缓冲流(比如说:BufferedInputStream、BufferedOutputStream),也一直在想,既然不用缓冲流就可以正确完成IO操作,那为什么还要用缓冲流呢?

当然了,答案其实是很简单的,肯定是为了性能。那缓冲流又是怎么实现更好的性能的呢?

试想一下,如果我们不用缓冲流,我们在读数据的时候,不是从来源地读一个字节,就会向目的地写一个字节呀。在磁盘中使用IO操作费时费力的事,要比在内存中操作慢很多。而使用缓冲流之后,我们可以先从来源地读取出一批量的字节,然后保存到缓冲区中(内存),等缓冲区达到一定数量之后,再一次性的写入目的地。这样子能减少磁盘的IO操作,更好的提高性能。

需要注意的是,正是我们使用了缓冲流,所以在使用缓冲流(BufferedOutputStream)之后,一定要记得调用close()flush()方法,强行将剩余数据写出。

缓冲流使用了装饰器模式。比如说BufferedInputStreamBufferedOutputStream这两个类分别是FilterInputStreamFilterOutputStream的子类,作为装饰器子类,使用它们可以防止每次读取/发送数据时进行实际的写操作,代表着使用缓冲区。

本文总结

本文对Java IO的基本知识做了一个基本的分析,对IO的分类的做了介绍,结合网上的一些文章,做了一些总结。也有对应的导图可供参考,也解释了字节流和字符流之间的关系。最后我们还说了为什么需要缓冲流。文章还是对Java IO基础的一个浅析,如果要系统深入的学习,还需要去查阅相关书籍。

参考文章

java I/O流详解

看完这个,Java IO从此不在难

Java IO流学习总结三:缓冲流-BufferedInputStream、BufferedOutputStream

Copyright: 采用 知识共享署名4.0 国际许可协议进行许可

Links: https://baozi.fun/2019/12/14/java-io-simple-analysis

Buy me a cup of coffee ☕.