RandomAccessFile使用场景及总结
大家在学到Java中IO流的时候学到了各种流,对文件的各种操作。但是唯独可能对RandomAccessFile对象不会去过多的研究,那么这个到底有什么用呢?
RandomAccessFile的唯一父类是Object,与其他流父类不同。是用来访问那些保存数据记录的文件的,这样你就可以用seek( )方法来访问记录,并进行读写了。这些记录的大小不必相同;但是其大小和位置必须是可知的。
RandomAccessFile是不属于InputStream和OutputStream类系的。实际上,除了实现DataInput和DataOutput接口之外(DataInputStream和DataOutputStream也实现了这两个接口),它和这两个类系毫不相干,甚至都没有用InputStream和OutputStream已经准备好的功能;它是一个完全独立的类,所有方法(绝大多数都只属于它自己)都是从零开始写的。这可能是因为RandomAccessFile能在文件里面前后移动,所以它的行为与其它的I/O类有些根本性的不同。总而言之,它是一个直接继承Object的,独立的类。
基本上,RandomAccessFile的工作方式是,把DataInputStream和DataOutputStream粘起来,再加上它自己的一些方法,比如定位用的getFilePointer( ),在文件里移动用的seek( ),以及判断文件大小的length( )。此外,它的构造函数还要一个表示以只读方式("r"),还是以读写方式("rw")打开文件的参数 (和C的fopen( )一模一样)。它不支持只写文件,从这一点上看,假如RandomAccessFile继承了DataInputStream,它也许会干得更好。
只有RandomAccessFile才有seek方法,而这个方法也只适用于文件。BufferedInputStream有一个mark( )方法,你可以用它来设定标记(把结果保存在一个内部变量里),然后再调用reset( )返回这个位置,但是它的功能太弱了,而且也不怎么实用。
RandomAccessFile的绝大多数功能,如果不是全部的话,已经被JDK1.4的nio的"内存映射文件(memory-mapped files)"给取代了。
RandomAccessFile有什么用呢?
平常创建流对象关联文件,开始读文件或者写文件都是从头开始的,不能从中间开始,如果是开多线程下载一个文件我们之前学过的FileWriter或者FileReader等等都无法完成,而当前介绍的RandomAccessFile他就可以解决这个问题,因为它可以指定位置读,指定位置写的一个类,通常开发过程中,多用于多线程下载一个大文件等场景。
RandomAccessFile解析
首先我们来看他的构造方法

我们可以看到,他接受一个name和mode,

另外一个构造方法,接受一个file和一个mode,另外大家看到这里可能有就些懵了,这个mode是什么东东呢?其实mode代表了几种模式,下面我们来看:
- r:以只读方式打开指定文件。如果试图对该RandomAccessFile指定的文件执行写入方法则会抛出IOException
- rw:以读取、写入方式打开指定文件。如果该文件不存在,则尝试创建文件
- rws:以读取、写入方式打开指定文件。相对于rw模式,还要求对文件的内容或元数据的每个更新都同步写入到底层存储设备,默认情形下(rw模式下),是使用buffer的,只有cache满的或者使用RandomAccessFile.close()关闭流的时候儿才真正的写到文件
- rwd:与rws类似,只是仅对文件的内容同步更新到磁盘,而不修改文件的元数据
看了上面的模式后大家应该也明白了。接下来我们来看他的一些API

可以看到,他的写入方法有这么多。下面我们来看个DEMO
public static void main(String[] args) {
try {
//声明一个RandomAccessFile对象
RandomAccessFile r = new RandomAccessFile("F:/tmp/xx.xx", "rw");
//写入到xx.xx
r.write("Hello World".getBytes());
r.close();
} catch (Exception e) {
e.printStackTrace();
}
}
上面的代码写一个字符串到xx.xx,我们可以看到,用RandomAccessFile写文件是非常简单的。下面我们来看读文件相关的API:

public static void main(String[] args) {
try {
// 声明一个RandomAccessFile对象
RandomAccessFile r = new RandomAccessFile("F:/tmp/xx.xx", "rw");
byte[] b = new byte[1024];
r.read(b);
System.out.println(new String(b));
r.seek(5);
byte[] b1 = new byte[1024];
r.read(b1);
System.out.println(new String(b1));
r.close();
} catch (Exception e) {
e.printStackTrace();
}
}

我们来看,第一个输出的Hello World,第二个输出的是空格World 那么前面的Hello去哪儿了呢,我们在代码中调用了r.seek(5),意思是什么呢,是把文件的指针移动到了第五个位置,然后从第五个位置开始读取,我们可以假象在打开文件的时候光标默认在第一行第一个,那么seek(5)就是把光标移动到第5个,然后从第五个开始读取后面的。我们来看看seek的源码实现:

我们可以看到他调用了seek0,seek0被声明为native,是jdk底层来实现了。有基础的同学可以去看这部分C的源码。

他里边还有个getFilePointer()的方法,也是jdk底层实现的,他的意思是返回文件的长度,比如上面的调用会返回11.。
这个时候可能有就人问,怎么追加内容呢,其实这个也很简单,我们之前学了seek,调用seek把指针移动到文件的结尾,在开始写,如下:
public static void main(String[] args) {
try {
// 声明一个RandomAccessFile对象
RandomAccessFile r = new RandomAccessFile("F:/tmp/xx.xx", "rw");
//把指针移动到结尾
r.seek(r.length());
//进行写入文件
r.write(" this".getBytes());
//关闭文件
r.close();
} catch (Exception e) {
e.printStackTrace();
}
}

这么写就实现了。下面我们来看一个综合性的案例:
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
/**
*
* @author yuxuan
*
*/
public class RandomFileTest {
public static void main(String[] args) {
String filePath = "F:\\myWorks\\个人中心_1.png";
// 这里声明为偶数片数,下面会进行计算
int parts = 2;
File file = new File(filePath);
long len = file.length();
// 防止除法出校小数点,必须能整除才行
if (len % 2 == 0) {
parts *= 2;
} else if (len % 2 == 0) {
parts = parts * 2 - 1;
}
for (int i = 0; i < parts; i++) {
// 启动线程
new ReadCopyMoreThreadFile(i, parts, file).start();
}
System.out.println("多线程复制文件成功");
}
}
/**
* 线程类,真正干活的
*
* @author yuxuan
*
*/
class ReadCopyMoreThreadFile extends Thread {
private int start = 0;
private int parts = 0;
private File file;
public ReadCopyMoreThreadFile(int start, int parts, File file) {
this.start = start;
this.file = file;
this.parts = parts;
}
public void run() {
System.out.println("第" + start + "个线程正在运行!");
try {
RandomAccessFile rf = new RandomAccessFile(file, "rw");
// 获取到文件的总长度
long len = rf.length();
/*
*
* 5174 * 0 / 2 = 0 5174 * 1 / 2 = 2587
*
* 跳到第start部分开始读
*/
rf.seek(len * start / parts);
byte[] buf = new byte[(int) (len / parts)];
// 读取
rf.read(buf);
// 关闭
rf.close();
int index = file.getName().lastIndexOf(".");
String newFileName = file.getName().substring(0, index) + "-bak_" + file.getName().substring(index);
// 创建目标文件
rf = new RandomAccessFile(newFileName, "rw");
// 指针移动到需要写的位置
rf.seek(len * start / parts);
// 写入
rf.write(buf);
// 关闭
rf.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
上面的案例可以实现多个线程复制文件,当然下载文件也是可以的,大家可以自行研究。
有问题可以在下面评论,技术问题可以在私聊我。
RandomAccessFile使用场景及总结的更多相关文章
- Java IO流之随机读写流RandomAccessFile
随机读写流RandomAccessFile 简介 此类的实例支持对随机访问文件的**读取和写入**. 随机访问文件的行为类似存储在文件系统中的一个大型 byte 数组. 存在指向该隐含数组的光标或索引 ...
- java - day012 - 异常 , throws, throw , IO ,RandomAccessFile
异常 封装错误信息的对象 错误信息 类型 例如: NullPointerExce 空指针 提示消息 出错的行号 异常的继承结构 Throwable | - Error 系统级错误 | ...
- Java I/O(三)各种Reader和Writer读写器、RandomAccessFile随机访问文件、序列化
2019 01/01 八.Reader和Writer读写器 前面讲的输入输出流的基本单位都是字节,因此可以称为“字节流”,读写器是以字符为基本单位,可以称为“字符流”.它们的使用方法非常相似,因此我考 ...
- IO流之RandomAccessFile和File
通过学习一些经典案例来复习基础 ------------------------------------------------------------------------------------ ...
- 拨开迷雾,找回自我:DDD 应对具体业务场景,Domain Model 到底如何设计?
写在前面 除了博文内容之外,和 netfocus 兄的讨论,也可以让你学到很多(至少我是这样),不要错过哦. 阅读目录: 迷雾森林 找回自我 开源地址 后记 毫无疑问,领域驱动设计的核心是领域模型,领 ...
- [NodeJS] 优缺点及适用场景讨论
概述: NodeJS宣称其目标是“旨在提供一种简单的构建可伸缩网络程序的方法”,那么它的出现是为了解决什么问题呢,它有什么优缺点以及它适用于什么场景呢? 本文就个人使用经验对这些问题进行探讨. 一. ...
- Asp.Net MVC中使用StreamReader读取“Post body”之应用场景。
场景:有三个市场(Global.China.USA),对前台传过来的数据有些验证需要细化到每个市场去完成. 所以就出现了基类(Global)和派生类(China.USA) 定义基类(Global)Pe ...
- Java学习之反射机制及应用场景
前言: 最近公司正在进行业务组件化进程,其中的路由实现用到了Java的反射机制,既然用到了就想着好好学习总结一下,其实无论是之前的EventBus 2.x版本还是Retrofit.早期的View注解框 ...
- Android线程管理之ThreadLocal理解及应用场景
前言: 最近在学习总结Android的动画效果,当学到Android属性动画的时候大致看了下源代码,里面的AnimationHandler存取使用了ThreadLocal,激起了我很大的好奇心以及兴趣 ...
随机推荐
- android studio配置so文件路径
将一个项目从eclipse上移植到android studio时,发现总是加载不成功库文件,so库文件放在了main/src/libs下的目录. 参考网上资料,studio默认的库文件路径是main/ ...
- Linux下汇编语言学习笔记31 ---
这是17年暑假学习Linux汇编语言的笔记记录,参考书目为清华大学出版社 Jeff Duntemann著 梁晓辉译<汇编语言基于Linux环境>的书,喜欢看原版书的同学可以看<Ass ...
- hdu - 1429 胜利大逃亡(续) (bfs状态压缩)
http://acm.hdu.edu.cn/showproblem.php?pid=1429 终于开始能够做状态压缩的题了,虽然这只是状态压缩里面一道很简单的题. 状态压缩就是用二进制的思想来表示状态 ...
- Delphi简单的数据操作类
unit MyClass; uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, VCL ...
- Redis集群方案收集
说明: 如果不考虑客户端分片去实现集群,那么市面上基本可以说就三种方案最成熟,它们分别如下所示: 系统 贡献者 是否官方Redis实现 编程语言 Twemproxy Twitter 是 C Redis ...
- firemonkey获取当前文件所在路径的方法
在之前,我们知道有三种方法: ExtractFilePath(ParamStr(0)) ExtractFilePath(Application.ExeName) GetCurrentDir + '\' ...
- maven打包插件maven-shade-plugin简单介绍
作用: 1.可以把依赖打入jar包,然后直接使用这个jar包,从而不用担心依赖问题 2.通过设置MainClass,创建一个可以执行的jar包 3.Java工程经常会遇到第三方 Jar 包冲突,使用 ...
- Linux system log avahi-daemon[3640]: Invalid query packet.
2014-06-11 Check the Linux system log find the errorr: Jun 9 11:18:49 hostname avahi-daemon[3640]: ...
- Markdown 语法的简要规则
标题 标题是每篇文章都须要也是最经常使用的格式,在 Markdown 中.假设一段文字被定义为标题,仅仅要在这段文字前加 # 号就可以. # 一级标题 ## 二级标题 ### 三级标题 以此类推,总共 ...
- ajax请求锁屏功能
我们有时候在进行ajax请求的时候希望页面不允许点击,等请求结束之后才可以进行点击,那么可以写: $(".cloudos-container").ajaxStart($.block ...