IO知识Java篇(4) -Java IO API、read/write 全过程分析

本文将介绍一些与IO有关的java api以及read/write接口调用的过程分析,主要内容有以下几点:

  • 文件File,文件句柄FileDescriptor,RandomAccessFile,FileChannel
  • 输入输出流,包括输入输出字节流,输入输出字符流,字符流与字节流的转换
  • 输入输出流的缓存包装器BufferedInputStream,BufferedOutputStream,BufferedReader,BufferedWriter
  • Java进程中IO read/write 请求的全过程
一 文件File,文件句柄FileDescriptor,RandomAccessFile,FileChannel
1 File
         File对象 代表 通过路径映射到操作系统的文件系统的目录或文件,可通过File对文件或目录的元信息进行访问或对他们进行相关操作,但不能对文件的内容进行操作.
         持有一个FileSystem对象引用,是本地文件系统的一个抽象,在不同的系统平台有不同的FileSystem实现.
文件句柄FileDescriptor

(1)用来表示开放文件、开放套接字等的句柄

(2)持有标准输入标出流(FileDescriptor.in / out / error )的句柄

(3)sync()方法将操作系统缓存中的数据强制刷新到物理磁盘中。

 

3 RandomAccessFile

        属于传统的IO类,持有FileDescriptor与FileChannel,封装了一些读写功能,如访问指定位置的内容,按基本的数据类型读写文件等.

4 FileChannel

(1)FileChannel属于NIO的类,是一个连接到文件的通道,可以通过文件通道读写文件,提供了很多有用的读写控制功能,如权限,定位,锁,裁剪,强制刷到磁盘等等,它与RandomAccessFile最明显的区别在于,其使用ByteBuffer进行读写,而RandomAccessFile使用字节数组

(2)FileChannel无法设置为非阻塞模式,它总是运行在阻塞模式下; 

(3)无法直接打开一个FileChannel,需要通过使用InputStream、OutputStream或RandomAccessFile来获取一个FileChannel实例。

 

二 输入输出字节流,输入输出字符流,字符流与字节流的转换

 

1 基于字节的InputStream OutputStream

是基于字节操作的 I/O 接口

InputStream需要指定一个字节流的来源,如File,调用read方法读取来源的字节数据到指定的地方(或直接返回,如ObjectOutputStream.readObject());

OutputStream需要指定一个字节流的目的地,调用write方法将指定的字节数据写到目的地;

 

例如:

ByteArrayInputStream字节流来源是其内部的字节数组;ByteArrayOutputStream的字节流目的地也是其内部的字节数组;

 ObjectInputStream 对以前使用 ObjectOutputStream 写入的基本数据和对象进行反序列化;

ObjectOutputStream 将 Java 对象的基本数据类型和图形写入 OutputStream。可以使用 ObjectInputStream 读取(重构)对象。

ObjectInputStream的字节流来源可以是FileInputStream; ObjectOutputStream的字节流目的地可以是FileOutputStream;

 

2  FileInputStream和FileOutputStream

(1)用于封装对文件的读写(FileDescriptor.fd=-1且handle != -1)或对标准输入标出流(FileDescriptor.in / out / error )的读写(分别对应FileDescriptor.fd= 0 / 1 / 2)

(2)在其构造方法中,如果传入的不是FD对象,那么表示封装对操作系统文件的读写,会有个open()的系统调用以打开文件进行读写

(3)★这个open()的系统调用会促使系统在内核构造一个对应文件的结构体,并返回一个long型值代表的句柄赋予FileDescriptor.handle

 

3 基于字符操作的 I/O 接口:Writer 和 Reader (StreamEncoder StreamDecoder)

FileReader与 FileWriter:

分别基于InputStreamReader和OutputStreamWriter,他们的来源和目的地分别是InputStream和OutputStream,而他们俩又进一步依赖于StreamDecoder与StreamEncoder,

FileReader的来源是FileInputStream,FileWriter的来源是FileOutputStream.(

FileReader与 FileWriter使用的编码是系统默认编码)

 

StreamDecoder担当将来源的字节解码成指定的编码的字符串(内存中以unicode表示,格式为utf-16)

 

StreamEncoder担当将指定的字符串以指定的编码写入目的地

 

所以说,★字符流的操作是在jvm层次而不是在本地的

 

4 字符流与字节流的转换.............................待整理■

 

三 输入输出流的缓存包装

 

1  BufferedInputStream 与 BufferedOutputStream

(1)IO读写的一些原则:尽量一次性读写一段数据,而不是以字节为单位的多次读写,在jvm层面,这样可以减少对本地方法调用的次数,从而导致在系统层面,可减少内存与外存的IO的交互次数以及用户区与内核区数据的传递次数,因为现今的计算机都支持DMA块读写,系统调用也是可以一次性读写一段数据的.

(2)BufferedInputStream封装了一个缓冲区,使用户在操作IO时不用自己设计和维护缓冲区.外界对BufferedInputStream的读取操作实际上是在缓冲区上进行,如果读取的数据超过了缓冲区的范围,那么BufferedInputStream负责重新从原始输入流中载入下一截数据填充缓冲区,然后外界继续通过缓冲区进行数据读取。

(3)BufferedOutputStream封装了一个缓冲区,使用户在操作IO时不用自己设计和维护缓冲区.当使用write()方法写入数据时实际上会先将数据写到buf中,当buf已满,或者调用flush()时才会调用给定的OutputStream对象的write()方法,将buf数据写到目的地,而不是每次都对目的地作写入的动作。

 

2 BufferedReader,BufferedWriter.............................待整理■

 

四 Java进程中IO read/write 请求的全过程

1, read整体过程

①->   生成FileInputStream或RandomAccessFile,构造方法中执行系统调用open,内核创建一个结构体(代表对应的文件或外部设备的信息,包含有存放数据的缓冲区) 并返回句柄值赋予FileDescriptor.handle.

②->  调用java IO流的read()

③->  调用本地方法的read()

④->  本地方法执行系统调用read()  

⑤->  系统通过驱动程序的读指令向硬件发送IO指令将外存的数据搬进内核缓冲区(底层是由中断或DMA方式实现,注意系统可能会预先从外存中读进一批数据,减少了IO的内外交互次数)  

⑥-> 系统再把内核缓冲区的数据拷备到jvm进程缓存区  

 

2, read过程分析

(1) 在①②③④步顺序执行后,java中对应请求io的线程,即调用read()方法的线程将被阻塞,以等待读就绪状态及IO的完成,即等待外存中的数据搬进内核缓冲区

(2) 当⑤步执行后,为读就绪状态

(3)读就绪状态后,执行⑥,IO过程到此完成,★线程不再阻塞,

 

3,write整体过程

①->   生成FileOutputStream或RandomAccessFile,构造方法中执行系统调用open,内核创建一个结构体(代表对应的文件或外部设备的信息,包含有存放数据的缓冲区) 并返回句柄值赋予FileDescriptor.handle.

②->  调用java IO流的write()

③->  调用本地方法的write()

⑤->  本地方法执行系统调用write()  

⑥-> 系统把jvm进程缓存区的数据拷备到内核缓冲区

⑦->  系统通过驱动程序的写指令向硬件发送IO指令将内核缓冲区的数据搬到外存(底层是由中断或DMA方式实现)  

 

4 write过程分析

(1)在①②③⑤步顺序执行后,IO请求线程被阻塞,等待IO写就绪与IO的完成

(2)在⑥步可以执行的时候,说明已经写就绪了,于是系统把用户进程缓存区的数据拷备到内核缓冲区

(3)当第⑥步执行完后,真正的IO过程到此完成,★线程不再阻塞.

(4)可以使用对象持有的FileChannel实例的force(boolean metaData)方法强制执行第⑦步,如果不使用强制,那么将内核缓冲区的数据刷到外存的时机依赖于系统的具体实现. (FileDescriptor.sync()方法可以把文件内容刷到外存,但是,没有boolean metaData参数,不能将文件元数据刷到外存 )

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值