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

前言

编程语言的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的的典型使用方式进行总结。

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

(0)

相关推荐

  • Java输入输出流实例详解

    本文实例讲述了Java输入输出流.分享给大家供大家参考,具体如下: 字节输出流,输出到文件中(写) OutputStream抽象类 选好文件 复制代码 代码如下: File file = new File("E:" + File.separator + "myFile" + File.separator + "test" + File.separator + "123.txt"); 子类实例化,需要抛异常 稍后传输的数据覆盖原

  • java输入字符串并将每个字符输出的方法

    如下所示: import java.util.Scanner; public class Main{ public static void main(String[] args){ int i,len; String str; Scanner in = new Scanner(System.in); str = in.next(); len = str.length(); //求出长度 for (i=0; i<len; i++){ System.out.println(str.charAt(i)

  • Java输入输出流的使用详细介绍

    1.什么是IO Java中I/O操作主要是指使用Java进行输入,输出操作. Java所有的I/O机制都是基于数据流进行输入输出,这些数据流表示了字符或者字节数据的流动序列.Java的I/O流提供了读写数据的标准方法.任何Java中表示数据源的对象都会提供以数据流的方式读写它的数据的方法. Java.io是大多数面向数据流的输入/输出类的主要软件包.此外,Java也对块传输提供支持,在核心库 java.nio中采用的便是块IO. 流IO的好处是简单易用,缺点是效率较低.块IO效率很高,但编程比较

  • Java输入/输出流体系详解

    在用java的io流读写文件时,总是被它的各种流能得很混乱,有40多个类,理清啦,过一段时间又混乱啦,决定整理一下!以防再忘 Java输入/输出流体系 1.字节流和字符流 字节流:按字节读取.字符流:按字符读取. 字符流读取方便,字节流功能强大,当不能用字符流时,可以用字节流. 字节流基类:InputStream.OutputStream InputStream方法: OutputStream方法: 字符流基类:Reader.Writer Reader方法: Writer方法: 2.输入输出流体

  • Java输入三个整数并把他们由小到大输出(x,y,z)

    题目:输入三个整数x,y,z,请把这三个数由小到大输出. 程序分析:我们想办法把最小的数放到x上,先将x与y进行比较,如果x> y则将x与y的值进行交换,然后再用x与z进行比较,如果x> z则将x与z的值进行交换,这样能使x最小. 程序设计: import java.util.*; public class test { public static void main (String[]args){ int i=0; int j=0; int k=0; int x=0; System.out.

  • Java输入输出流复制文件所用时间对比

    废话不多说,关键代码如下所述: package com.edu.xynu; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; public class IOUnitCopy {

  • Lua教程(十五):输入输出库(I/O库)

    I/O库为文件操作提供了两种不同的模型,简单模型和完整模型.简单模型假设一个当前输入文件和一个当前输出文件,他的I/O操作均作用于这些文件.完整模型则使用显式的文件句柄,并将所有的操作定义为文件句柄上的方法.     1. 简单模型:     I/O库会将进程标准输入输出作为其缺省的输入文件和输出文件.我们可以通过io.input(filename)和io.output(filename)这两个函数来改变当前的输入输出文件.     1). io.write函数:     函数原型为io.wri

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

    前言 编程语言的I/O类库中常使用流这个抽象概念,它代表任何有能力产出数据的数据源对象或者是有能力接收数据的接收端对象."流"屏蔽了实际的I/O设备中处理数据的细节. 在文章:<<Java I/O深入学习之File和RandomAccessFile>>中,我们讲到RandomAccessFile可以写入和读取文件,具备I/O功能,但是其只能针对文件,而I/O还涉及到很多其他场景比如网络.读取内存中的字符串等,所以Java类库中提供了一系列的类库来对其进行支持,也

  • Python零基础入门学习之输入与输出

    简介 在之前的编程中,我们的信息打印,数据的展示都是在控制台(命令行)直接输出的,信息都是一次性的没有办法复用和保存以便下次查看,今天我们将学习Python的输入输出,解决以上问题. 复习 得到输入用的是input(),完成输出用的是print(),之前还有对字符串的操作,这些我们都可以使用help()命令来查看具体的使用方法. 文件 在Python2的时候使用的是file来创建一个file类,对它进行操作.Python3中去掉了这个类(我没有查到,只是猜测),使用open来打开一个文件,返回一

  • Python编程基础之输入与输出

    目录 一.IPO模型  二.基本输入 - input()函数 1.函数格式 2.参数说明 3.实例演示 (1)接收字符串数据 (2)接收整型数据 (3)接收浮点型数据 (4)容易出现的错误 三.基本输出 - print()函数 1.函数格式 2.参数说明 3.实例演示 (1)输出空行 (2)输出一个或多个对象 (3)指定分隔符 (4)指定结束符号 (5)输出到文件 (6)格式输出 (7)引申案例 - 输出斐波拉契数列 四.美观输出 - pprint()函数 1.pprint模块概述 2.ppri

  • Java学习笔记:基本输入、输出数据操作实例分析

    本文实例讲述了Java学习笔记:基本输入.输出数据操作.分享给大家供大家参考,具体如下: 相关内容: 输出数据: print println printf 输入数据: Scanner 首发时间:2018-03-16 16:30 输出数据: JAVA中在屏幕中打印数据可以使用: System.out.print(x):x可以是一个变量.表达式.字符串. System.out.println(x):x可以是一个变量.表达式.字符串.与print不同的是打印完后会换行 System.out.print

  • 深入学习PyTorch中LSTM的输入和输出

    目录 LSTM参数 Inputs Outputs 案例 LSTM参数 官方文档给出的解释为: 总共有七个参数,其中只有前三个是必须的.由于大家普遍使用PyTorch的DataLoader来形成批量数据,因此batch_first也比较重要.LSTM的两个常见的应用场景为文本处理和时序预测,因此下面对每个参数我都会从这两个方面来进行具体解释. input_size:在文本处理中,由于一个单词没法参与运算,因此我们得通过Word2Vec来对单词进行嵌入表示,将每一个单词表示成一个向量,此时input

  • 利用Python中的输入和输出功能进行读取和写入的教程

    读取.写入和 Python 编写程序的最后一个基本步骤就是从文件读取数据和把数据写入文件.阅读完这篇文章之后,可以在自己的 to-do 列表中加上检验这个技能学习效果的任务. 简单输出 贯穿整个系列,一直用 print 语句写入(输出)数据,它默认把表达式作为 string 写到屏幕上(或控制台窗口上).清单 1 演示了这一点.清单 1 重复了第一个 Python 程序 "Hello, World!",但是做了一些小的调整. 清单 1. 简单输出 >>> print

  • Python中输入和输出(打印)数据实例方法

    一个程序要进行交互,就需要进行输入,进行输入→处理→输出的过程.所以就需要用到输入和输出功能.同样的,在Python中,怎么实现输入和输出? Python3中的输入方式: Python提供了 input() 内置函数从标准输入读入一行文本,默认的标准输入是键盘. 例如: n = input() print(n) >>>输入Python3 >>>Python3 其中输入的数据以字符串类型进行储存,如果输入数字的话,后续需要转换类型才能进行操作. n = input() p

  • angular的输入和输出的使用方法

    angular的核心组件化,可以说组件化的相当彻底.而要实现组件之间的通信,一个必不可少的就是输入和输出.通过对输入输出的学习,更加加深我对组件化的认识. 组件的输入 angular允许使用两种形式来定义组件的输入,一种是在装饰器@Component中使用inputs来定义,另一种是使用@Input来定义. inputs 首先先介绍在装饰器中使用的输入.inputs接收的是一个字符串数组,用来指定我们输入的键名. @Component({ selector: 'my-component', in

  • R语言数据的输入和输出操作

    数据的载入 R本身已经提供了超过50个数据集,而在众多功能包中,默认的数据集被存放在datasets程序包中,通过函数data()k可以查看系统提供所有的数据包,同时可以通过函数library()加载程序包中的数据. 矩阵型数据最常用的读取方式是read.table()具体的调用格式是() read.table(file, header = FALSE, sep = "", quote = "\"'",dec = ".", numera

  • 神奇的shell命令行输入与输出功能介绍

    标准输入/输出和重定向,Linux发行版Fedora Core Linux,而Red Hat公司原来Red Hat Linux的开发团队也将继续参与这一发行版本的开发工作. 标准输入与输出 我们知道,执行一个shell命令行时通常会自动打开三个标准文件,即标准输入文件(stdin),通常对应终端的键盘:标准输出文件(stdout)和标准错误输出文件(stderr),这两个文件都对应终端的屏幕.进程将从标准输入文件中得到输入数据,将正常输出数据输出到标准输出文件,而将错误信息送到标准错误文件中.

随机推荐