关于 JAVA 中顺序 IO 的基本操作
什么是顺序 IO
事实上 JAVA 具有很多操作文件的方案(方法), 许多程序需要将一些事件记录到本地存储中,常见的如数据库,MQ 等,首先文件是许多带数据的块组成的,传统 IO 操作文件具有一个寻址过程(事实上硬件上也会存在寻道,旋转延迟等因素),小文件尚可,大文件就比较消耗性能和时间,比如数据库分配的文件(本地),顺序 IO 具备指定位置的功能,但是任然需要我们维护一个偏移量(游标).
MappedByteBuffer
http://www.atguigu.com/中顺序 IO 通过 MappedByteBuffer 实现,与传统 IO 不同的是,MappedByteBuffer 需要使用者提供一个位置(偏移量),详细看以下代码:
代码中可见,通过 MappedByteBuffer 提供的 api position();来指定位置(偏移量),put()进行写操作,详细如下。
写操作
先看代码:
上述代码中标注 1 位置中使用 RandomAccessFile (随机流)来打开文件,此流与传统 IO 除了兼并读写之外,在一些底层实现方式上也均有不同,在此不多做介绍,感兴趣可另寻资料,在此需记住,此处使用随机流的作用为第二步做准备,且唯一,其中参数 1 为 File 对象,构造方法重载的参数 1 可为文件路径,参数 2 的取值可有 4 种,如下(取至 JAVA 官方文档):
"r"仅供阅读。调用结果对象的任何写方法都会引发 IOException。(Open for reading only. Invoking any of the write methods of the resulting object will cause an IOException to be thrown. )
"rw"开放阅读和写作。如果该文件不存在,那么将尝试创建它。(Open for reading and writing. If the file does not already exist then an attempt will be made to create it. )
“rws”和“rw”一样,对文件内容或元数据的每次更新都要同步写入底层存储设备。(Open for reading and writing, as with "rw", and also require that every update to the file's content or metadata be written synchronously to the underlying storage device. )
“rwd”和“rw”一样,都是打开的,可以读写,并且还要求对文件内容的每次更新都要同步写入底层存储设备。(Open for reading and writing, as with "rw", and also require that every update to the file's content be written synchronously to the underlying storage device. )
上述代码中标注 2 位置中,通过随机流获取到一个读写兼并的通道,实际上获取 IO 通道的方式并不仅仅只有此种方式,但是在此处需要注意的是,顺序读写所需的通道需兼并读写(第一步中参数 2 取值需为:rw,rws,rwd),如果不是,则会触发 IO 异常,除此之外,上述提到过使用其他方式也可以获取到文件 IO 通道,比如:
运行结果,标记 3 处抛出异常:NonWritableChannelException 或者:
运行结果,标记 3 处抛出异常:NonReadableChannelException 从上可以看到,不管是 FileInputStream 还是 FileOutputStream 获取到的 IO 通道,均有局限性,不适用 MappedByteBuffer。
上述代码中标记 3 位置中,通过 IO 通道将该文件的内容(或某个区域)直接映射到内存中,并且对该内存做的修改直接会传播到文件(除了 PRIVATE 模式,后续介绍),通过 FileChannel 对象的 map();api 进行映射,参数一指定映射方式,有如下三种(取至 JAVA 官方文档):
只读:任何修改结果缓冲区的尝试都将导致抛出 ReadOnlyBufferException。(MapMode.READ_ONLY) (Read-only: Any attempt to modify the resulting buffer will cause a ReadOnlyBufferException to be thrown. (MapMode.READ_ONLY) )
读/写:对产生的缓冲区所做的更改最终将传播到文件;它们可能对映射了相同文件的其他程序可见,也可能不可见。(MapMode.READ_WRITE) (Read/write: Changes made to the resulting buffer will eventually be propagated to the file; they may or may not be made visible to other programs that have mapped the same file. (MapMode.READ_WRITE) )
Private:对产生的缓冲区所做的更改不会传播到该文件中,并且不会对映射了该文件的其他程序可见;相反,它们将导致创建缓冲区修改部分的私有副本。(MapMode.PRIVATE) (Private: Changes made to the resulting buffer will not be propagated to the file and will not be visible to other programs that have mapped the same file; instead, they will cause private copies of the modified portions of the buffer to be created. (MapMode.PRIVATE) )
参数二代表从指定位置开始映射,0 表示从头开始映射全部内容,参数三表示要映射的区域大小,可超出文件大小(如字符长度为 3,此处可填写 6 或者其他),但不可为负数或超出 Integer.MAX_VALUE.实际上到此处,IO 通道已经完成了它的任务,可关闭。(在标记 3 之后任意位置可执行 fileChannel.close()而不影响运行结果)此处简要说明了个参数的意思,要加深了解建议自己建立 Demo 并更改此处参数观察运行结果。
上述代码中标记 4 位置中,通过 MappedByteBuffer 对象的 position(); API 设置写入位置,官方解释如下:Sets this buffer's limit. If the position is larger than the new limit then it is set to the new limit. If the mark is defined and larger than the new limit then it is discarded.
上述代码中标记 5 位置中,将内容传输到缓冲区,可理解为写入,因为缓冲区的变动会传播到实际文件中,除了 PRIVATE。
上述代码中标记 6 位置中,返回下一次操作时的位置。
评论