大家在学到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. hdu 3622 二分+2-sat

    /* 二分+2-sat 题意:在一个二维平面上给你n个炸弹,和2*n个位置,每一行的两个位置只能有一个放炸弹 现在炸弹爆炸有一个半径,当炸弹爆炸时两个炸弹的半径化成的圆不能相交,求最大半径 二分半径, ...

  2. 在workbench中导入.sql文件!(导入数据库文件)

    第一步,登陆mysql workbench 第二步,打开自己的数据 ,此处默认(root) 打开数据库后页面 : 第三步,新建一个schema ,随便给个名字,这里起名为test : 可以看到test ...

  3. 【BZOJ4403】序列统计(Lucas定理,组合计数)

    题意:给定三个正整数N.L和R, 统计长度在1到N之间,元素大小都在L到R之间的单调不降序列的数量. 输出答案对10^6+3取模的结果. 对于100%的数据,1≤N,L,R≤10^9,1≤T≤100, ...

  4. 新vim配置文件

    "******************************************************特殊设置************************************ ...

  5. Journey CodeForces - 839C

    There are n cities and n - 1 roads in the Seven Kingdoms, each road connects two cities and we can r ...

  6. winform总结6=>线程和委托的关系

    基础类: using System; using System.Collections.Generic; using System.Linq; using System.Text; using Sys ...

  7. iOS 设备推断 最新统计代码

    - (NSString*) deviceName {     struct utsname systemInfo;          uname(&systemInfo);          ...

  8. Google搜索引擎用法

    Google搜索引擎用法 ★搜索引擎的选择 先简单说一下"搜索引擎的选择". 在咱们天朝,Google 屡屡被 GFW 骚扰,导致百度占了便宜,成为份额最高的搜索引擎.不过今天这篇 ...

  9. 华为OJ2288-合唱队(最长递增子序列)

    一.题目描述 描述: N位同学站成一排,音乐老师要请其中的(N-K)位同学出列,使得剩下的K位同学不交换位置就能排成合唱队形. 合唱队形是指这样的一种队形:设K位同学从左到右依次编号为1, 2, -, ...

  10. 混合式框架-AgileLite

    Agile Lite是一个HTML5移动前端框架.支持jQuery和Zepto双引擎.而且提供与UI无关的独立框架,内置了Flat UI样式和Ratchet样式.同一时候也支持单页模式和多页模式开发. ...