Java IO体系之RandomAccessFile浅析
一、RandomAccessFile综述:
1.1RandomAccessFile简介
RandomAccessFile是java Io体系中功能最丰富的文件内容访问类。即可以读取文件内容,也可以向文件中写入内容。
RandomAccessFile的唯一父类是Object,与其他流父类不同。是用来访问那些保存数据记录的文件的,这样你就可以用seek( )方法来访问记录,并进行读写了。这些记录的大小不必相同;但是其大小和位置必须是可知的。
由于RandomAccessFile可以*访问文件的任意位置,所以如果我们希望只访问文件的部分内容,RandomAccessFile将是更好的选择。
与OutputStearm,Writer等输出流不同的是,RandomAccessFile类允许*定位文件记录指针,程序可以直接跳到文件的任意位置来读写数据,所以RandomAccessFile可以不从文件开始的地方进行输出,如果需要向已存在的文件后追加内容。则可以使用RandomAccessFile。
这个类在很多资料上翻译成中文都是:随机访问文件,在中文里,随机是具有不确定的含义,指一会访问这里,一会访问那里的意思。如果以这种语义来解释的话,就会感到很困惑。其实,Random在英文中不仅仅有随机,还有任意的意思。如果中文名为任意访问文件是不是就会更好的理解。任意表示我们可以指定文件中任何一个位置去操作一个文件。
1.2、RandomAccessFile数据结构
这个是JDK上的截图,我们可以看到它的父类是Object,没有继承字节流、字符流家族中任何一个类。实际上,除了实现DataInput和DataOutput接口之外(DataInputStream和DataOutputStream也实现了这两个接口,意味着这个类即可以读也可以写),它和字节流、字符流类系毫不相干,甚至都没有用InputStream和OutputStream已经准备好的功能;它是一个完全独立的类,所有方法(绝大多数都只属于它自己)都是从零开始写的。这可能是因为RandomAccessFile能在文件里面前后移动,所以它的行为与其它的I/O类有些根本性的不同。总而言之,它是一个直接继承Object的,独立的类。
1.3.RandomAccessFile的整体介绍
-
RandomAccessFile类包含了一个记录指针,用以标识当前读写处的位置,当程序新创建一个RandomAccessFile对象时,该对象的文件记录指针位于文件头(也就是0处),当读/写了n个字节后,文件记录指针将会向后移动n个字节。除此之外,RandomAccessFile可以*的移动记录指针,即可以向前移动,也可以向后移动。RandomAccessFile包含了以下两个方法来操作文件的记录指针:RandomAccessFile即可以读文件,也可以写,所以它即包含了完全类似于InputStream的3个read()方法,其用法和InputStream的3个read()方法完全一样;也包含了完全类似于OutputStream的3个write()方法,其用法和OutputStream的3个Writer()方法完全一样。除此之外,RandomAccessFile还包含了一系类的readXXX()和writeXXX()方法来完成输入和输出。
- long getFilePointer(); 返回文件记录指针的当前位置
- void seek(long pos); 将文件记录指针定位到pos位置
-
RandomAccessFile有两个构造器,其实这两个构造器基本相同,只是指定文件的形式不同而已,一个使用String参数来指定文件名,一个使用File参数来指定文件本身。除此之外,创建RandomAccessFile对象还需要指定一个mode参数。该参数指定RandomAccessFile的访问模式,有以下4个值:
“r” 以只读方式来打开指定文件夹。如果试图对该RandomAccessFile执行写入方法,都将抛出IOException异常。
“rw” 以读,写方式打开指定文件。如果该文件尚不存在,则试图创建该文件。
“rws” 以读,写方式打开指定文件。相对于”rw” 模式,还要求对文件内容或元数据的每个更新都同步写入到底层设备。
“rwd” 以读,写方式打开指定文件。相对于”rw” 模式,还要求对文件内容每个更新都同步写入到底层设备。
1.4工作方式
- 基本上,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方法摘要:
构造方法摘要 | |
---|---|
RandomAccessFile(File file, String mode) 创建从中读取和向其中写入(可选)的随机访问文件流,该文件由 File 参数指定。 |
|
RandomAccessFile(String name, String mode) 创建从中读取和向其中写入(可选)的随机访问文件流,该文件具有指定名称。 |
方法摘要 | |
---|---|
void |
close() 关闭此随机访问文件流并释放与该流关联的所有系统资源。 |
FileChannel |
getChannel() 返回与此文件关联的唯一 FileChannel 对象。 |
FileDescriptor |
getFD() 返回与此流关联的不透明文件描述符对象。 |
long |
getFilePointer() 返回此文件中的当前偏移量。 |
long |
length() 返回此文件的长度。 |
int |
read() 从此文件中读取一个数据字节。 |
int |
read(byte[] b) 将最多 b.length 个数据字节从此文件读入 byte 数组。 |
int |
read(byte[] b, 将最多 len 个数据字节从此文件读入 byte数组。 |
boolean |
readBoolean() 从此文件读取一个 boolean 。 |
byte |
readByte() 从此文件读取一个有符号的八位值。 |
char |
readChar() 从此文件读取一个字符。 |
double |
readDouble() 从此文件读取一个 double 。 |
float |
readFloat() 从此文件读取一个 float 。 |
void |
readFully(byte[] b) 将 b.length 个字节从此文件读入 byte 数组,并从当前文件指针开始。 |
void |
readFully(byte[] b, 将正好 len 个字节从此文件读入 byte数组,并从当前文件指针开始。 |
int |
readInt() 从此文件读取一个有符号的 32 位整数。 |
String |
readLine() 从此文件读取文本的下一行。 |
long |
readLong() 从此文件读取一个有符号的 64 位整数。 |
short |
readShort() 从此文件读取一个有符号的 16 位数。 |
int |
readUnsignedByte() 从此文件读取一个无符号的八位数。 |
int |
readUnsignedShort() 从此文件读取一个无符号的 16 位数。 |
String |
readUTF() 从此文件读取一个字符串。 |
void |
seek(long pos) 设置到此文件开头测量到的文件指针偏移量,在该位置发生下一个读取或写入操作。 |
void |
setLength(long newLength) 设置此文件的长度。 |
int |
skipBytes(int n) 尝试跳过输入的 n 个字节以丢弃跳过的字节。 |
void |
write(byte[] b) 将 b.length 个字节从指定 byte 数组写入到此文件,并从当前文件指针开始。 |
void |
write(byte[] b, 将 len 个字节从指定 byte数组写入到此文件,并从偏移量 off 处开始。 |
void |
write(int b) 向此文件写入指定的字节。 |
void |
writeBoolean(boolean v) 按单字节值将 boolean 写入该文件。 |
void |
writeByte(int v) 按单字节值将 byte 写入该文件。 |
void |
writeBytes(String s) 按字节序列将该字符串写入该文件。 |
void |
writeChar(int v) 按双字节值将 char 写入该文件,先写高字节。 |
void |
writeChars(String s) 按字符序列将一个字符串写入该文件。 |
void |
writeDouble(double v) 使用 Double 类中的 doubleToLongBits 方法将双精度参数转换为一个 long ,然后按八字节数量将该 long 值写入该文件,先定高字节。 |
void |
writeFloat(float v) 使用 Float 类中的 floatToIntBits 方法将浮点参数转换为一个int ,然后按四字节数量将该 int 值写入该文件,先写高字节。 |
void |
writeInt(int v) 按四个字节将 int 写入该文件,先写高字节。 |
void |
writeLong(long v) 按八个字节将 long 写入该文件,先写高字节。 |
void |
writeShort(int v) 按两个字节将 short 写入该文件,先写高字节。 |
void |
writeUTF(String str) 使用 modified UTF-8 编码以与机器无关的方式将一个字符串写入该文件。 |
三、实例分析
1 package me.io.raffile;
2
3 import java.io.File;
4 import java.io.IOException;
5 import java.io.RandomAccessFile;
6 import java.util.Arrays;
7
8 public class RafDemo {
9
10 public static void main(String[] args) throws IOException {
11 File demo = new File("demo");
12 if(!demo.exists())
13 demo.mkdir();
14 File file = new File(demo, "raf.dat");
15 if (!file.exists())
16 file.createNewFile();
17 RandomAccessFile raf = new RandomAccessFile(file, "rw");
18 //指针的位置
19 System.out.println(raf.getFilePointer());
20 raf.write('A');//只写了一个字节
21 System.out.println(raf.getFilePointer());
22
23 raf.write('B');
24 System.out.println(raf.getFilePointer());
25
26 //Java中最大的整数
27 int i = 0x7fffffff;
28
29 //占四个字节,如果要把i写进去,就得写四次
30 raf.write(i >>> 24);//高八位
31 raf.write(i >>> 16);
32 raf.write(i >>> 8);
33 raf.write(i);
34
35 //可以直接写一个int
36 raf.writeInt(i);
37
38 String string = "中";
39 byte[] bytes = string.getBytes();
40 raf.write(bytes);
41 System.out.println(raf.length());
42
43 //读文件,必须把指针移到头部
44 raf.seek(0);
45 //一次性读取,把文件中的内容都堵到字节数组
46 byte[] buf = new byte[(int) raf.length()];
47 raf.read(buf);
48 System.out.println(Arrays.toString(buf));
49 for (byte b : buf) {
50 System.out.print(Integer.toHexString(b & 0xff) + " ");
51 }
52 raf.close();
53 }
54 }
三、专题总结(FIle与RandomAccessFile)
注:这里File类的介绍可参见我的上一篇博文
java.io.File类用于表示文件(目录)
File类只用于表示文件(目录)的信息(名称、大小等),不能用于文件内容的访问
RandomAccessFile java提供的对文件内容的访问,既可以读文件,也可以写文件。
RandomAccessFile支持随机访问文件,可以访问文件的任意位置
(1)java文件模型
在硬盘上的文件是byte byte byte存储的,是数据的集合
(2)打开文件
有两种模式"rw"(读写) "r"(只读)
RandomAccessFile raf = new RandomeAccessFile(file,"rw")
文件指针,打开文件时指针在开头 pointer = 0;
(3) 写方法
raf.write(int)--->只写一个字节(后8位),同时指针指向下一个位置,准备再次写入
(4)读方法
int b = raf.read()--->读一个字节
(5)文件读写完成以后一定要关闭(Oracle官方说明)
参考文章: