大家在学到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使用场景及总结的更多相关文章

  1. Java IO流之随机读写流RandomAccessFile

    随机读写流RandomAccessFile 简介 此类的实例支持对随机访问文件的**读取和写入**. 随机访问文件的行为类似存储在文件系统中的一个大型 byte 数组. 存在指向该隐含数组的光标或索引 ...

  2. java - day012 - 异常 , throws, throw , IO ,RandomAccessFile

    异常 封装错误信息的对象 错误信息 类型        例如: NullPointerExce 空指针 提示消息  出错的行号 异常的继承结构 Throwable | - Error 系统级错误 | ...

  3. Java I/O(三)各种Reader和Writer读写器、RandomAccessFile随机访问文件、序列化

    2019 01/01 八.Reader和Writer读写器 前面讲的输入输出流的基本单位都是字节,因此可以称为“字节流”,读写器是以字符为基本单位,可以称为“字符流”.它们的使用方法非常相似,因此我考 ...

  4. IO流之RandomAccessFile和File

    通过学习一些经典案例来复习基础 ------------------------------------------------------------------------------------ ...

  5. 拨开迷雾,找回自我:DDD 应对具体业务场景,Domain Model 到底如何设计?

    写在前面 除了博文内容之外,和 netfocus 兄的讨论,也可以让你学到很多(至少我是这样),不要错过哦. 阅读目录: 迷雾森林 找回自我 开源地址 后记 毫无疑问,领域驱动设计的核心是领域模型,领 ...

  6. [NodeJS] 优缺点及适用场景讨论

    概述: NodeJS宣称其目标是“旨在提供一种简单的构建可伸缩网络程序的方法”,那么它的出现是为了解决什么问题呢,它有什么优缺点以及它适用于什么场景呢? 本文就个人使用经验对这些问题进行探讨. 一. ...

  7. Asp.Net MVC中使用StreamReader读取“Post body”之应用场景。

    场景:有三个市场(Global.China.USA),对前台传过来的数据有些验证需要细化到每个市场去完成. 所以就出现了基类(Global)和派生类(China.USA) 定义基类(Global)Pe ...

  8. Java学习之反射机制及应用场景

    前言: 最近公司正在进行业务组件化进程,其中的路由实现用到了Java的反射机制,既然用到了就想着好好学习总结一下,其实无论是之前的EventBus 2.x版本还是Retrofit.早期的View注解框 ...

  9. Android线程管理之ThreadLocal理解及应用场景

    前言: 最近在学习总结Android的动画效果,当学到Android属性动画的时候大致看了下源代码,里面的AnimationHandler存取使用了ThreadLocal,激起了我很大的好奇心以及兴趣 ...

随机推荐

  1. DD & E-app

    DD & E-app 企业内部开发的E应用 前端 demo https://github.com/open-dingtalk docs https://open-doc.dingtalk.co ...

  2. 【NOIP2016】天天爱跑步(树上差分)

    题意: 小c同学认为跑步非常有趣,于是决定制作一款叫做<天天爱跑步>的游戏.?天天爱跑步?是一个养成类游戏,需要 玩家每天按时上线,完成打卡任务.这个游戏的地图可以看作一一棵包含 N个结点 ...

  3. <项目><day11>查看用户浏览过的商品

    <项目>查看用户浏览过的商品 1.创建一个entity包储存实体对象 1.1创建一个Product的类存储实体对象 对象具有以下属性,并添加set和get方法,含参和不含参的构造方法,to ...

  4. echarts模拟highcharts实现折线图的虚实转换

    多的不说直接上代码: <html><html lang="en"><head> <meta charset="utf-8&quo ...

  5. 使用URL Rewrite实现网站伪静态

    下载urlwrite包 将urlrewrite-***.jar复制到web应用lib文件夹下 web.xml中配置URL Rewrite: 例: <filter> <filter-n ...

  6. IOS程序崩溃报告管理解决方案(Crashlytics 在2014-09-24)

    预研Crashlytics  在2014-09-241:实现原理在原理上,Crashlytics通过以下2步完成崩溃日志的上传和分析:(1)提供应用SDK,你需要在应用启动时调用其SDK来设置你的应用 ...

  7. fuse-dfs挂载hdfs实录

    部署安装了最新稳定版hadoop2.2.0.然后在网上找来fuse-dfs编译教程.可是最后失败了.至今原因未知--,错误描写叙述为:Transport endpoint is not connect ...

  8. 【C++/数据结构】单链表的基本操作

    #pragma once #ifndef _CLIST_H_ #define _CLIST_H_ #include <iostream> #include <assert.h> ...

  9. C#.NET如何判断是否有缺少的using

    调试的时候会报错,红色的波浪线表示出错的位置,右击即可找到对应的using      

  10. Lvs 负载均衡 (VS/NAT模式)

    一.LVS简介 LVS是 Linux Virtual Server 的简称,也就是Linux虚拟服务器.这是一个由章文嵩博士发起的一个开源项目,它的官方网站是 http://www.linuxvirt ...