java I/O 温故知新

123 阅读6分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第5天,点击查看活动详情

什么是I/O

Java中I/O操作主要是指使用Java进行输入,输出操作. Java所有的I/O机制都是基于数据流进行输入输出,这些数据流表示了字符或者字节数据的流动序列。Java的I/O流提供了读写数据的标准方法。任何Java中表示数据源的对象都会提供以数据流的方式读写它的数据的方法。

流IO的好处是简单易用,缺点是效率较低

数据流相关概念

数据流是一串连续不断的数据的集合,就象水管里的水流,在水管的一端一点一点地供水,而在水管的另一端看到的是一股连续不断的水流。数据写入程序可以是一段、一段地向数据流管道中写入数据,这些数据段会按先后顺序形成一个长的数据流。对数据读取程序来说,看不到数据流在写入时的分段情况,每次可以读取其中的任意长度的数据,但只能先读取前面的数据后,再读取后面的数据。不管写入时是将数据分多次写入,还是作为一个整体一次写入,读取时的效果都是完全一样的。

数据流

一组有序,有起点和终点的字节的数据序列。包括输入流和输出流。

输入流

程序从输入流读取数据源。数据源包括外界(键盘、文件、网络…),即是将数据源读入到程序的通信通道

输出流

程序向输出流写入数据。将程序中的数据输出到外界(显示器、打印机、文件、网络…)的通信通道。

数据流分类

流序列中的数据既可以是未经加工的原始二进制数据,也可以是经一定编码处理后符合某种格式规定的特定数据。

Java中的流分为两种:

  1. 字节流:数据流中最小的数据单元是字节
  2. 字符流:数据流中最小的数据单元是字符, Java中的字符是Unicode编码,一个字符占用两个字节。

IO体系

image.png

  1. 流式部分――IO的主体部分;

  2. 非流式部分――主要包含一些辅助流式部分的类,如:File类、RandomAccessFile类和FileDescriptor等类;

  3. 其他类--文件读取部分的与安全相关的类,如:SerializablePermission类,以及与本地操作系统相关的文件系统的类,如:FileSystem类和Win32FileSystem类和WinNTFileSystem类。

    主要的类如下:

    1. File(文件特征与管理):用于文件或者目录的描述信息,例如生成新目录,修改文件名,删除文件,判断文件所在路径等。
    2. InputStream(二进制格式操作):抽象类,基于字节的输入操作,是所有输入流的父类。定义了所有输入流都具有的共同特征。
    3. OutputStream(二进制格式操作):抽象类。基于字节的输出操作。是所有输出流的父类。定义了所有输出流都具有的共同特征。
    4. Reader(文件格式操作):抽象类,基于字符的输入操作。
    5. Writer(文件格式操作):抽象类,基于字符的输出操作。
    6. RandomAccessFile(随机文件操作):它的功能丰富,可以从文件的任意位置进行存取(输入输出)操作。

IO常用类

  • 文件流:FileInputStream/FileOutputStream, FileReader/FileWriter

    这四个类是专门操作文件流的,用法高度相似,区别在于前面两个是操作字节流,后面两个是操作字符流。它们都会直接操作文件流,直接与OS底层交互。因此他们也被称为节点流。

    注意使用这几个流的对象之后,需要关闭流对象,因为java垃圾回收器不会主动回收。不过在Java7之后,可以在 try() 括号中打开流,最后程序会自动关闭流对象,不再需要显示地close。

  • 包装流:PrintStream/PrintWriter/Scanner

    PrintStream可以封装(包装)直接与文件交互的节点流对象OutputStream, 使得编程人员可以忽略设备底层的差异,进行一致的IO操作。因此这种流也称为处理流或者包装流。

    PrintWriter除了可以包装字节流OutputStream之外,还能包装字符流Writer

    Scanner可以包装键盘输入,方便地将键盘输入的内容转换成我们想要的数据类型。

  • 字符串流:StringReader/StringWriter

    这两个操作的是专门操作String字符串的流,其中StringReader能从String中方便地读取数据并保存到char数组,而StringWriter则将字符串类型的数据写入到StringBuffer中(因为String不可写)。

  • 转换流:InputStreamReader/OutputStreamReader

    这两个类可以将字节流转换成字符流,被称为字节流与字符流之间的桥梁。我们经常在读取键盘输入(System.in)或网络通信的时候,需要使用这两个类

  • 缓冲流:BufferedReader/BufferedWriter,BufferedInputStream/BufferedOutputStream

    没有经过Buffered处理的IO, 意味着每一次读和写的请求都会由OS底层直接处理,这会导致非常低效的问题。 经过Buffered处理过的输入流将会从一个buffer内存区域读取数据,本地API只会在buffer空了之后才会被调用(可能一次调用会填充很多数据进buffer)。 经过Buffered处理过的输出流将会把数据写入到buffer中,本地API只会在buffer满了之后才会被调用。 BufferedReader/BufferedWriter可以将字符流(Reader)包装成缓冲流,这是最常见用的做法。另外,BufferedReader提供一个readLine()可以方便地读取一行,而FileInputStream和FileReader只能读取一个字节或者一个字符,因此BufferedReader也被称为行读取器

流的应用场景

  • FileInputStream/FileOutputStream  需要逐个字节处理原始二进制流的时候使用,效率低下
  • FileReader/FileWriter 需要组个字符处理的时候使用
  • StringReader/StringWriter 需要处理字符串的时候,可以将字符串保存为字符数组
  • PrintStream/PrintWriter 用来包装FileOutputStream 对象,方便直接将String字符串写入文件 
  • Scanner 用来包装System.in流,很方便地将输入的String字符串转换成需要的数据类型
  • InputStreamReader/OutputStreamReader ,  字节和字符的转换桥梁,在网络通信或者处理键盘输入的时候用
  • BufferedReader/BufferedWriter , BufferedInputStream/BufferedOutputStream , 缓冲流用来包装字节流后者字符流,提升IO性能,BufferedReader还可以方便地读取一行,简化编程。