RandomAccessFile详解

RandomAccessFile的常见用法

1.RandomAccessFile的简介

1.1为什么要用到RandomAccessFile

我们平常创建流对象关联文件,开始读文件或者写文件都是从头开始的,不能从中间开始,如果是开多线程下载一个文件我们之前学过的FileWriter或者FileReader等等都无法完成,而当前介绍的RandomAccessFile他就可以解决这个问题,因为它可以指定位置读,指定位置写的一个类,通常开发过程中,多用于多线程下载一个大文件.

1.2.常用方法简介

构造方法:RandomAccessFile raf = newRandomAccessFile(File file, String mode);

其中参数 mode 的值可选 "r":可读,"w" :可写,"rw":可读性;

成员方法:

​ seek(int index);可以将指针移动到某个位置开始读写;

​ setLength(long len);给写入文件预留空间:

2.RandomAccessFile的特点和优势

这个对象有两个优点

1.既可以读也可以写

RandomAccessFile不属于InputStream和OutputStream类系的它是一个完全独立的类,所有方法(绝大多数都只属于它自己)都是自己从头开始规定的,这里面包含读写两种操作

2.可以指定位置读写

RandomAccessFile能在文件里面前后移动,在文件里移动用的seek( ),所以它的行为与其它的I/O类有些根本性的不同。总而言之,它是一个直接继承Object的,独立的类。只有RandomAccessFile才有seek搜寻方法,而这个方法也只适用于文件.

3.通过案例来熟悉RandomAccessFile的最常用的操作

首先创建一个DownLoadThread的类继承Thread

public class DownLoadThread extends Thread {    private long start;
    private File src;
    private long total;
    private File desc;
​
    /**
     * 
     * @param start
     *            开始下载的位置
     * @param src
     *            要下载的文件
     * @param desc
     *            要下载的目的地
     * @param total
     *            要下载的总量
     */
    public DownLoadThread(long start, File src, File desc, long total) {
        this.start = start;
        this.src = src;
        this.desc = desc;
        this.total = total;
    }
​
    @Override
    public void run() {
        try {
            // 创建输入流关联源,因为要指定位置读和写,所以我们需要用随机访问流
            RandomAccessFile src = new RandomAccessFile(this.src, "rw");
            RandomAccessFile desc = new RandomAccessFile(this.desc, "rw");
​
            // 源和目的都要从start开始
            src.seek(start);
            desc.seek(start);
            // 开始读写
            byte[] arr = new byte[1024];
            int len;
            long count = 0;
            while ((len = src.read(arr)) != -1) {
                //分三种情况
                if (len + count > total) {
                     //1.当读取的时候操作自己该线程的下载总量的时候,需要改变len
                    len = (int) (total - count);
                    desc.write(arr, 0, len);
                    //证明该线程下载任务已经完毕,结束读写操作
                    break;
                } else if (len + count < total) {
                    //2.证明还没有到下载总量,直接将内容写入
                    desc.write(arr, 0, len);
                    //并且使计数器任务累加
                    count += arr.length;
                } else {
                    //3.证明改好到下载总量
                    desc.write(arr, 0, len);
                    //结束读写
                    break;
                }
            }
            src.close();
            desc.close();
​
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

然后定义主方法进行文件的测试

public class TestRandomAccess {    public static void main(String[] args) {
        //关联源
        File src = new File("a.txt");
        //关联目的
        File desc = new File("b.txt");
​
        //获取源的总大小
        long length = src.length();
        // 开两条线程,并分配下载任务
        new DownLoadThread(0, src, desc, length / 2).start();
        new DownLoadThread(length / 2 , src, desc, length - (length / 2)).start();
    }
​

     
     

1. RandomAccessFile类常用方法

  RandomAccessFile是Java提供用来访问一些保存数据记录的文件的类,可以进行读取操作,也可以进行写入操作,写入的数据则以byte的形式存储;支持随机访问,也就是可以访问文件的任意位置(通过文件指针实现)。

2. 构造函数

RandomAccessFile(String name, String mode)
RandomAccessFile(File file, String mode)

  两个构造函数用法非常相似,name、file都是用于指定打开的文件路径和名称,mode则是指定打开文件的方式,常用的参数有两个"r"和"rw",也就是只读和读写。

  文件打开后,文件指针指向文件最开始,也就是pointer=0,可通过RandomAccessFile了的getFilePointer()方法查看。

范例: 创建并打开一个数据文件。

//创建目录
File dir = new File("demo");
if (!dir.exists()) {
    dir.mkdir();
}
//创建文件
File file = new File(dir, "test.dat");
if (!file.exists()) {
    file.createNewFile();
}
//实例化RandomAccessFile对象
RandomAccessFile raf = new RandomAccessFile(file, "rw");
//打开文件时指针位置在最前,即0
System.out.println(raf.getFilePointer());

3. 写入操作

write(int i)
write(byte[] b)
write(byte[] b, int off, int len)

  第三个方法中的off为数组b中需要写入的数据的起始索引值,len则是要写入的长度。write方法每次写入一个字节,如果写入的数据超过一个字节,则写入后八位(如果这里不太理解,可看看:二进制运算基础)。

  另外,每写入一个字节,文件指针指向下一个字节。

范例: 通过write()方法向文件中写入一个整型数。(沿用上面例子创建的对象)

//write()方法每次只插入一个字节,大于一个字节的则写入后八位,因此写入一个整型数需要写入四次
int num = 28;
raf.write(num >>> 24);
raf.write(num >>> 16);
raf.write(num >>> 8);
raf.write(num);

  当然,RandomAccessFile类也提供了更简便的方法writeXxx(),如果插入一个整型,可直接writeInt(i);,boolean的则为writeBoolean(),以此类推。但是要清楚的是,这些方法的还是通过上面的write()方法实现的。

范例: 以下为RandomAccessFile类中writeInt()方法的方法体。

public final void writeInt(int v) throws IOException {
    write((v >>> 24) & 0xFF);
    write((v >>> 16) & 0xFF);
    write((v >>>  8) & 0xFF);
    write((v >>>  0) & 0xFF);
    //written += 4;
}

4. 读取操作

read(int i)
read(byte[] b)
read(byte[] b, int off, int len)

  与写入操作类似,读取操作是通过read()方法实现的,每次读取一个字节,同时文件指针指向下一个位置(通过seek()方法将指针移到读取位置)。同时,RandomAccessFile类也封装了readXxx()系列方法用于简便读取,原理和使用方法可参考写入操作,基本类似。

范例: 将数据文件中的所有数据以整型形式读取出来。

//读取文件,在读取前需要通过seek()方法把文件指针移到最前
raf.seek(0);
for (int i = 0; i*4 < raf.length(); i++) {
    System.out.println(raf.readInt());
}

5. 关闭文件

  打开的文件一定要通过close()关闭,否则可能会出现不可预料的错误。

}

4.效果展示

a.txt的内容


b.txt的内容


5.总结

从以上分析可以看出RandomAccessFile最大两个特点:

1.可以指定位置开始操作;

2.既可以读,也可以写;

所以,我们但凡遇到不是需要从文件中中间部分开始读取的时候,可以使用RandomAccessFile这个类,比如:多线程下载是最常用的应该场景

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值