JavaI/O深入学习之输入和输出

 更新时间:2019年06月18日 09:30:53   作者:木瓜芒果  
这篇文章主要介绍了JavaI/O深入学习之输入和输出,Java类库中的I/O类分成输入和输出两部分,可以在JDK文档里的类层次结构中查看到。,需要的朋友可以参考下

前言

编程语言的I/O类库中常使用流这个抽象概念,它代表任何有能力产出数据的数据源对象或者是有能力接收数据的接收端对象。“流”屏蔽了实际的I/O设备中处理数据的细节。

在文章:<<Java I/O深入学习之File和RandomAccessFile>>中,我们讲到RandomAccessFile可以写入和读取文件,具备I/O功能,但是其只能针对文件,而I/O还涉及到很多其他场景比如网络、读取内存中的字符串等,所以Java类库中提供了一系列的类库来对其进行支持,也就是本文要总结学习的。

Java类库中的I/O类分成输入和输出两部分,可以在JDK文档里的类层次结构中查看到。通过继承,任何自Inputstream或Reader派生而来的类都含有名为read()的基本方法,用于读取单个字节或者字节数组。同样,任何自OutputStream或Writer派生而来的类都含有名为write()的基本方法,用于写单个字节或者字节数组。

但是,我们通常不会用到这些方法,它们之所以存在是因为别的类可以使用它们,以便提供更有用的接口。因此,我们很少使用单一的类来创建流对象,而是通过叠合多个对象来提供所期望的功能(这是装饰器设计模式的应用,也有专门写文总结过:装饰器模式)。实际上,Java中“流”类库让人迷惑的主要原因就在于:创建单一的结果流,却需要创建多个对象。

I/O需要应对的场景往往是多样化的,Java类库的设计者则是通过创建大量的类来解决这个难题,区区一篇文章难以详述,本文也只是尽力对传统I/O类库所涉及到的类提供一个总览,在把握整个脉络的前提下才能更好的理解并应用I/O类库来解决实际编程问题。如需涉及到细节,还是需要参考JDK文档。

1. InputStream/OutputStream

Java 1.0中,类库的设计者首先限定与输入有关的所有类都应该从InputStream继承,而与输出有关的所有类都应该从OutputStream继承。

1.1 InputStream

InputStream的作用是用来表示那些从不同数据源产生输入的类。这些数据源包括:

  • 字节数组;
  • String对象;
  • 文件;
  • “管道”,工作方式与实际管道相似,即从一端输入,从另一端输出;
  • 一个由其他种类的流组成的序列,以便我们可以将它们收集合并到一个流内;
  • 其他数据源,如Internet连接等;

每一种数据源都有相应的InputStream子类,作为基础构件:

  • ByteArrayInputStream,允许将内存的缓冲区当作InputStream使用;
  • StringBufferInputStream,将String转换成InputStream;
  • FileInputStream,用于从文件中读取信息;
  • PipedInputStream,产生用于写入相关PipedOutputStream的数据。实现“管道化”概念;
  • SequenceInputStream,将两个或多个InputStream对象转换成单一InputStream;

1.2 OutputStream

OutputStream的作用是表示那些可以输出到不同数据源的类,其具体的子类决定了输出所要去往的目标:字节数组、文件或管道,同样是作为基础构件:

  • ByteArrayOutputStream,在内存中创建缓冲区。所有送往“流”的数据都要放置在此缓冲区;
  • FileOutputStream,用于将信息写至文件;
  • PipedOutputStream,任何写入其中的信息都会自动作为相关PipedInputStream的输出,实现“管道化”概念;

1.3 装饰器

除了有上面的基础构件,还有两个子类:FilterInputStream/FilterOutputStream,也是InputStream和OutputStream的子类,它们为“装饰器”(decorator)类提供基类,其中,“装饰器”类可以把属性或有用的接口与基础构件连接在一起。因为上面提到的InputStream/OutputStream是单字节为单位来操作的,而真实的I/O场景远不止于此,所以就通过“装饰”(其原理是类之间的组合)的方式来扩展其功能。

我自己梳理了一下InputStream/OutputStream流继承层次结构,结合下面的解释来看可以对字节流体系有一个更清晰的认识:

1.3.1 FilterInputStream

FilterInputStream类主要有如下子类,也就是具体装饰器:

  • DataInputStream;
  • BufferedInputStream;
  • LineNumberInputStream;

其提供的装饰功能主要在两个方面:

  • 读取不同的基本类型数据以及String对象,比如DataInputStream;
  • 在内部修改InputStream的行为方式:是否缓冲、是否保留它所读过的行,如BuffereInputStream、LineNumberInputStream;

1.3.2 FilterOutputStream

与FilterInputStream类似,FilterOutputStream主要是完成写入的功能,主要有如下装饰器:

  • DataOutputStream,与DataInputStream搭配使用,因此可以按照可移植方式向流中写入基本类型数据(int、char、long);
  • PrintStream,用于产生格式化输出。其中DataOutputStream处理数据的存储,PrintStream处理显示;
  • BufferedOutputStream,使用它以避免每次发送数据时都要进行实际的写操作。代表“使用缓冲区”。可以调用flush()清空缓冲区;

2. Writer/Reader

InputStream和OutputStream是提供面向字节形式的I/O,但是InputStream/OutputStream流继承层次结构仅支持8位字节流,并且不能很好地处理16位的Unicode字符。由于Unicode用于字符国际化(Java本身的char也是16位的Unicode),所以添加Reader/Writer继承层次结构就是为了在所有的I/O操作中都支持Unicode。

几乎所有原始的Java I/O流类都有相应的Reader和Writer类来提供天然的Unicode操作,我们可以对比一下:

我们发现大体上,这两个不同的继承层次结构中的接口即使不能完全相同,但是也是非常相似的。

对于InputStream和OutputStream来说,我们会使用FilterInputStream和FilterOutputStream的装饰器子类来修改“流”以满足特殊需要。Reader/Writer的类继承层次结构继续沿用相同的思想,但是又并不完全采用上面说到的装饰器模式。如下是自己梳理的Reader/Writer继承层次结构:

与前面的I/O继承层次结构图相对比可以发现,尽管BufferedOutputStream是FilterOutputStream的子类,但是BufferedWriter并不是FilterWriter的子类(FilterWriter是抽象类,但是没有任何子类,仅仅是作为一个占位符)。

2.1 适配器

有时我们必须把来自于“字节”层次结构中的类和“字符”层次结构中的类结合起来使用。为了实现这个目的,要用到“适配器”(adapter)类:InputStreamReader可以把InputStream转换为Reader,而OutputStreamWriter可以把OutputStream转换为Writer。

3. 总结

  • I/O需要应对的场景往往是多样化的,Java类库的设计者通过创建大量的类来解决这个难题,在实际使用中,通过装饰器模式避免“类爆炸”,但类的数量还是不少,这也是Java中“流”类库让人迷惑的主要原因;
  • InputStream和OutputStream是提供面向字节形式的I/O,而Reader和Writer则提供兼容Unicode与面向字符的IO功能;
  • 如果需要把字节流和字符流结合起来使用,可以使用适配器进行转换,InputStreamReader可以把InputStream转换为Reader,而OutputStreamWriter可以把OutputStream转换为Writer;

本文主要是梳理了传统I/O流的类继承层次结构,包括字节流(InputStream/OutputStream)和字符流(Writer/Reader),并没有一开始就一头扎进I/O类库的海洋中,主要是希望通过这种方式能够对整个I/O体系有一个清晰的认识,这对于进一步的学习可以有更明确的指导作用,下文会针对一些I/O的的典型使用方式进行总结。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

相关文章

  • Java如何利用状态模式(state pattern)替代if else

    Java如何利用状态模式(state pattern)替代if else

    这篇文章主要给大家介绍了关于Java如何利用状态模式(state pattern)替代if else的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-11-11
  • 浅谈Java自定义注解相关知识

    浅谈Java自定义注解相关知识

    今天带大家来学习Java注解的相关知识,文中对自定义注解作了非常详细的介绍,对正在学习Java的小伙伴们很有帮助,需要的朋友可以参考下
    2021-05-05
  • SpringBoot下实现session保持方式

    SpringBoot下实现session保持方式

    这篇文章主要介绍了SpringBoot下实现session保持方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-03-03
  • Java Web程序实现返回JSON字符串的方法总结

    Java Web程序实现返回JSON字符串的方法总结

    Java Web服务器端只要把Java对象数据转成JSON字符串,把JSON字符串以文本的形式通过response输出即可,
    2016-05-05
  • java中DecimalFormat四舍五入用法详解

    java中DecimalFormat四舍五入用法详解

    这篇文章主要为大家详细介绍了java中DecimalFormat四舍五入的用法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-03-03
  • Nacos配置中心设计原理分析

    Nacos配置中心设计原理分析

    今天分享一下Nacos配置变更的相关知识点,现在使用Java生态如果使用微服务,如果部署在K8s上,那么可能会使用ConfigMap来存储配置文件,如果没有使用K8s,那么基本上都使用Nacos来做配置中心,所以有必要了解一下Nacos的配置的知识点,本文只是对其中的部分实现原理进行分析
    2023-10-10
  • Java序列化机制详解

    Java序列化机制详解

    Java 序列化机制是一种将对象转换为字节流的过程,以便在网络上传输或保存到文件中,并能在需要时将字节流还原为对象,这一机制通过实现 java.io.Serializable 接口来实现,同时涉及到一些关键概念和注意事项,需要的朋友可以参考下
    2023-12-12
  • 值得Java开发者关注的7款新工具

    值得Java开发者关注的7款新工具

    作为老牌语言Java,其生态圈也出来了一些有关云服务、监控、文档分享方面的工具,这篇文章主要介绍了Java开发者值得关注的7款新工具,感兴趣的小伙伴们可以参考一下
    2016-07-07
  • 简单聊一聊Java线程池ThreadPoolExecutor

    简单聊一聊Java线程池ThreadPoolExecutor

    在使用线程池之后,开启线程就变成了在线程池当中找到一个空闲的线程,销毁线程变成了归还线程到线程池的过程,下面这篇文章主要给大家介绍了关于Java线程池ThreadPoolExecutor的相关资料,需要的朋友可以参考下
    2022-06-06
  • springboot项目中引入本地依赖jar包并打包到lib文件夹中

    springboot项目中引入本地依赖jar包并打包到lib文件夹中

    这篇文章主要介绍了springboot项目中引入本地依赖jar包,如何打包到lib文件夹中,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-04-04

最新评论