RandomAccessFile是用来访问那些保存数据记录的文件的,这样你就可以用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(
)返回这个位置,但是它的功能太弱了,而且也不怎么实用。
下面的实例实现了把员工信息写入本地文件,然后逆序读取的功能.
1.员工类
<pre name="code" class="java">
* 流与文件:本程序以一个员工记录表演示了RandomAccessFile随机读取
* (原文参考:《Java核心技术》第九版)
* @author lijtaos
* @version 1.0 2015-04-12
* @Email lijtaos@sina.com
*
*/
/*这是一个员工类*/
import java.util.*;
public class Employee
{
//姓名的长度:40字符=80字节
public static final int NAME_SIZE = 40;
//每条记录的长度
public static final int RECORD_SIZE = 2 * NAME_SIZE + 8 + 4 + 4 + 4;
private String name;
private double salary;
private Date hireDay;
public Employee() {}
public Employee(String n, double s, int year, int month, int day)
{
name = n;
salary = s;
GregorianCalendar calendar = new GregorianCalendar(year, month - 1, day);
hireDay = calendar.getTime();
}
public String getName()
{
return name;
}
public double getSalary()
{
return salary;
}
public Date getHireDay()
{
return hireDay;
}
/**
Raises the salary of this employee.
@byPercent the percentage of the raise
*/
public void raiseSalary(double byPercent)
{
double raise = salary * byPercent / 100;
salary += raise;
}
public String toString()
{
return getClass().getName()
+ "[name=" + name
+ ",salary=" + salary
+ ",hireDay=" + hireDay
+ "]";
}
}
2.数据读写类
/**
* 流与文件:本程序以一个员工记录表演示了RandomAccessFile随机读取
* (原文参考:《Java核心技术》第九版)
* @author lijtaos
* @version 1.0 2015-04-12
* @Email lijtaos@sina.com
*
*/
//对数据进行读写的工具类
import java.io.*;
public class DataIO
{
//读取指定长度的数据
public static String readFixedString(int size, DataInput in)
throws IOException
{
StringBuilder b = new StringBuilder(size);
int i = 0;
boolean more = true;
while (more && i < size)
{
char ch = in.readChar();
i++;
if (ch == 0)
more = false;
else
b.append(ch);
}
in.skipBytes(2 * (size - i));
return b.toString();
}
//写入指定长度的数据
public static void writeFixedString(String s, int size, DataOutput out)
throws IOException
{
for (int i = 0; i < size; i++)
{
char ch = 0;
if (i < s.length())
ch = s.charAt(i);
out.writeChar(ch);
}
}
}
3.测试类
/**
* 流与文件:本程序以一个员工记录表演示了RandomAccessFile随机读取
* (原文参考:《Java核心技术》第九版)
* @author lijtaos
* @version 1.0 2015-04-12
* @Email lijtaos@sina.com
*
*/
/*
将三条员工记录写入文件中,然后逆序读出。
*/
import java.io.*;
import java.util.*;
public class RandomAccessEmployee
{
public static void main(String[] args)
throws IOException
{
//员工对象数组
Employee[] emps=new Employee[3];
emps[0]=new Employee("石凉",9000.00,2015,12,23);
emps[1]=new Employee("赵忠祥",8000.00,2013,11,24);
emps[2]=new Employee("罗京",7000.00,2012,10,25);
//写数据
DataOutputStream out=
new DataOutputStream(new FileOutputStream("emp.dat"));
for(Employee e : emps)
writeData(out,e);
out.close();
//读数据
RandomAccessFile in=new RandomAccessFile("emp.dat","r");
//计算文件中包含的员工数
int n=(int)(in.length()/Employee.RECORD_SIZE);
Employee[] newEmps=new Employee[n];
//逆序读取员工记录
for(int i=n-1;i>=0;i--)
{
newEmps[i]=new Employee();
//把指针跳到文件中该员工的记录头部
in.seek(i*Employee.RECORD_SIZE);
newEmps[i]=readData(in);
}
//打印读取的员工记录
for(Employee e : newEmps)
System.out.println(e);
in.close();
}
/**
*把员工数据写入输出流
*@param out 数据输出流
*@param e 员工对象
**/
public static void writeData(DataOutput out,Employee e)
throws IOException
{
DataIO.writeFixedString(e.getName(),Employee.NAME_SIZE,out);
out.writeDouble(e.getSalary());
//Gregorian calendar 公历 ; 格里历 ; 格列高利历 ; 阳历
GregorianCalendar calendar=new GregorianCalendar();
calendar.setTime(e.getHireDay());
out.writeInt(calendar.get(Calendar.YEAR));
out.writeInt(calendar.get(Calendar.MONTH)+1);
out.writeInt(calendar.get(Calendar.DAY_OF_MONTH));
}
/**
从输入流中读取员工数据
@param in 数据输入流
@return 员工对象
*/
public static Employee readData(DataInput in)
throws IOException
{
String name=DataIO.readFixedString(Employee.NAME_SIZE,in);
double salary=in.readDouble();
int y=in.readInt();
int m=in.readInt();
int d=in.readInt();
return new Employee(name,salary,y,m-1,d);
}
}