java IO笔记(RandomAccessFile)

来源:互联网 发布:sftp用的什么端口 编辑:程序博客网 时间:2024/06/06 07:38

本篇讲述的内容是java io中的RandomAccessFile。

RandomAccessFile是java io体系中提供给我们的一种文件访问类,它自己同时包含了读写功能,当然它最大的特点是可以自由访问文件中的位置,无需从头开始访问,下面先贴上RandomAccessFile的源码,通过源码对其进行学习。

package java.io;import java.nio.channels.FileChannel;import sun.nio.ch.FileChannelImpl;public class RandomAccessFile implements DataOutput, DataInput, Closeable {    //声明了一个文件描述符的句柄。    private FileDescriptor fd;    //声明了一个文件通道的句柄,并将其指向null。    private FileChannel channel = null;    //定义了一个boolean型变量rw,用于表示文件的读写权限。    private boolean rw;    //定义了一个String类型的变量path,用于接收    private final String path;    //定义了一个Object对象,为后面close方法中的同步操作提供锁对象。    private Object closeLock = new Object();    //定义了一个boolean型变量closed,用于表示当前流是否关闭,用volatile关键字修饰,保证了其改变的可见性。    private volatile boolean closed = false;        //定义了4个常量,分别表示了RandomAccessFile的4种读写模式。    private static final int O_RDONLY = 1;//只读模式,不具备写权限,如果文件不存在不会创建文件。    private static final int O_RDWR =   2;//读写模式,具备读写权限,如果文件不存在会创建文件,该模式下数据改变时不会立马写入底层存储设备。    private static final int O_SYNC =   4;//同步的读写模式,具备读写模式的所有特性,当文件内容或元数据改变时,会立马同步写入到底层存储设备中。    private static final int O_DSYNC =  8;//同步的读写模式,具备读写模式的所有特性,当文件内容改变时,会立马同步写入到底层存储设备中。    /**     * 带两个参数的构造方法,第一个参数为String类型,表示需要进行操作的文件路径名,第二个参数为String类型,表示以什么模式打开文件(四种模式,r,rw,rws,     * rwd)。内部实质是继续调用后面的构造函数RandomAccessFile(File file, String mode)。     */       public RandomAccessFile(String name, String mode)        throws FileNotFoundException    {        this(name != null ? new File(name) : null, mode);    }    /**     * 带两个参数的构造方法,第一个参数为File类型,表示要进行操作的文件对象,第二个参数为String类型,表示打开文件的模式。     */    public RandomAccessFile(File file, String mode)        throws FileNotFoundException    {// 定义了一个String类型变量name用于接收操作文件的路径名,如果文件为null,则name赋值为null。        String name = (file != null ? file.getPath() : null);// 定义了一个int型变量imode,表示打开文件的模式状态,初始化默认为-1。        int imode = -1;// 对传入的参数mode进行匹配,给imode,rw赋值,表示读写权限。        if (mode.equals("r"))    //"r",只读模式。            imode = O_RDONLY;        else if (mode.startsWith("rw")) {    //"rw",读写模式            imode = O_RDWR;            rw = true;            if (mode.length() > 2) {//同步的读写模式。                if (mode.equals("rws"))                    imode |= O_SYNC;                else if (mode.equals("rwd"))                    imode |= O_DSYNC;                else                    imode = -1;             }        }//如果imode<0(其实就是等于-1),那么抛出相应的异常,提示传入的读写模式是非法的,并告知是要填四种合法模式中的一种。        if (imode < 0)            throw new IllegalArgumentException("Illegal mode \"" + mode                                               + "\" must be one of "                                               + "\"r\", \"rw\", \"rws\","                                               + " or \"rwd\"");//获得java的安全管理器,根据rw的状态监测文件的读写权限。        SecurityManager security = System.getSecurityManager();        if (security != null) {            security.checkRead(name);            if (rw) {                security.checkWrite(name);            }        } //如果name为null,抛出相应的异常,NullPointerException。        if (name == null) {            throw new NullPointerException();        }//如果file.isInvalid的值为true,则表示file对象不合法,抛出相应的异常,FileNotFoundException。        if (file.isInvalid()) {            throw new FileNotFoundException("Invalid file path");        }//将声明的句柄指向一个新建的文件描述符对象,并将本类对象依附在该文件描述符上。为path赋值,然后调用open方法根据指定路径和模式打开文件。        fd = new FileDescriptor();        fd.attach(this);        path = name;        open(name, imode);    }    /**     * 该方法用于获取当前文件的文件描述符。     */    public final FileDescriptor getFD() throws IOException {        if (fd != null) {            return fd;        }        throw new IOException();    }    /**     * 该方法用于获取当前文件的文件管道。     */    public final FileChannel getChannel() {        synchronized (this) {            if (channel == null) {                channel = FileChannelImpl.open(fd, path, true, rw, this);            }            return channel;        }    }    /**     * 一个native方法,根据传入的文件路径及读写模式来打开文件。     */    private native void open(String name, int mode)        throws FileNotFoundException;    //此处开始是RandomAccessFile的读操作    /**     * 定义了一个read方法,每次从文件中读取一个字节的内容,并以int型返回读取的数据。内部实质是调用native方法read0()。     */    public int read() throws IOException {        return read0();    }    /**     * 定义了一个native方法read0,用于每次从文件中读取一个字节的内容,并以int型将读取的数据返回。     */    private native int read0() throws IOException;    /**     * 定义了一个native方法readBytes方法,一次可以读取多个字节,并将读取的数据放入传入的字节数组当中,最终返回实际读取到的字节个数。     */    private native int readBytes(byte b[], int off, int len) throws IOException;    /**     * 定义了一个带三个参数的read方法,第一个参数为一个byte型数组,用于存放读取的字节内容,第二和第三个参数都是一个int型数值,分别表示了读取的起点以及读     * 取的长度。内部实质上是调用native方法readBytes方法来读取数据。     */    public int read(byte b[], int off, int len) throws IOException {        return readBytes(b, off, len);    }    /**     * 定义了一个带一个参数的read方法,传入的参数为一个byte型数组,用于存放读取的字节数据,内部实质上是调用native方法readBytes方法,起点为0长度为传入的     * 数组容量。     */    public int read(byte b[]) throws IOException {        return readBytes(b, 0, b.length);    }    /**     * 定义了一个readFully方法,每次读取多个字节数据,传入的参数为一个byte型数组。内部实质是调用之后的带三个参数的readFully方法,只有当数组存满或者文件     * 结尾或者抛出异常时才停止。     */    public final void readFully(byte b[]) throws IOException {        readFully(b, 0, b.length);    }    /**     * 定义了一个带三个参数的readFully方法,第一个参数为一个byte型数组,用于存放读取的字节数据,第二和第三个参数为int型数据,分别为读取的起点和读取的长度     * 。当数组存满或者文件结尾或者抛出异常时才停止。     */    public final void readFully(byte b[], int off, int len) throws IOException {        int n = 0;// 通过一个循环,调用read方法来进行文件的读取,直到读取指定的长度。        do {            int count = this.read(b, off + n, len - n);            if (count < 0)                throw new EOFException();            n += count;        } while (n < len);    }    /**     * 定义了一个skipBytes方法,该方法用于跳过指定的字节数,传入的参数为一个int型数值,为需要跳过的字节数,最后返回实际跳过的字节数。     */    public int skipBytes(int n) throws IOException {  //声明了3个long型变量,pos为当前读取的字节位置,len为文件的总长度,newpos表示跳过后指定字节后的读取读取位置。        long pos;        long len;        long newpos;//如果传入的参数小于等于零,则返回零,表示没有跳过任何字节的数据。        if (n <= 0) {            return 0;        } //通过getFilePointer方法,获取当前文件读取的索引位置。通过length方法获取文件的总长度。为newpos赋值,为当前位置加上跳过的字节数。        pos = getFilePointer();        len = length();        newpos = pos + n;//如果newpos大于文件总长度,那么newpos置位到文件尾部。        if (newpos > len) {            newpos = len;        }//调用seek方法,跳至newpos处。最终返回newpos-pos的值,表示实际跳过的字节数。        seek(newpos);        return (int) (newpos - pos);    }    //此处开始是RandomAccessFile的写操作。    /**     * 定义了一个write方法,每次写入一个字节的数据,传入的参数为一个int型值,即要写入的数据。内部实质是调用native方法write0来写入数据。     */    public void write(int b) throws IOException {        write0(b);    }    //定义了一个native方法write0,传入的参数为int型数据,每次写入一个字节的数据。    private native void write0(int b) throws IOException;    /**     * 定义了一个native方法writeBytes,每次写入多个字节的数据,有三个参数,第一个参数为一个byte型数组,里面存放了要写入的数据,第二和第三个参数都是int型     * 数据,分别表示了写入的起点以及写入的长度。     */    private native void writeBytes(byte b[], int off, int len) throws IOException;    /**     * 定义了一个write方法,每次写入多个字节的数据。传入的参数为一个byte型数组,里面存放了要写入的数据,内部实质调用了native方法writeBytes来写入数据。     */    public void write(byte b[]) throws IOException {        writeBytes(b, 0, b.length);    }    /**     * 定义了一个带3个参数的write方法,每次写入多个字节的数据。第一个参数为一个byte型数组,里面存放了要写入的字节数据,第二和第三个参数为int型数据,分别     * 表示了写入的起点以及写入的长度。内部实质是调用native方法writeBytes来写入数据。     */    public void write(byte b[], int off, int len) throws IOException {        writeBytes(b, off, len);    }    //RandomAccessFile独有的随机读取操作。    /**     * 定义了一个native方法getFilePointer,该方法用来获取当前文件读取的位置。     */    public native long getFilePointer() throws IOException;    /**     * 定义了一个seek方法,用于跳过指定字节长度的数据。传入的参数为一个long型数据,代表着要跳过的字节数。     */    public void seek(long pos) throws IOException {//对传入的参数进行安全监测,如果其小于零,则抛出相应的异常。        if (pos < 0) {            throw new IOException("Negative seek offset");        } else {    //调用native方法seek0,用来跳过指定字节数量的数据。            seek0(pos);        }    }    //定义了一个native方法seek0,用于跳过指定字节长度的数据,传入的参数为一个long型数据,其表示要跳过的字节长度。    private native void seek0(long pos) throws IOException;    /**     * 定义了一个native方法length,用于获得文件的总长度     */    public native long length() throws IOException;    /**     * 定义了一个native方法setLength,用于重新设定文件的长度。     */    public native void setLength(long newLength) throws IOException;    /**     * 定义了一个close方法,用于关闭流及其相关联的系统资源。     */    public void close() throws IOException {        synchronized (closeLock) {            if (closed) {                return;            }            closed = true;        }        if (channel != null) {            channel.close();        }        fd.closeAll(new Closeable() {            public void close() throws IOException {               close0();           }        });    }    //一些从DataInputStream/DataOuputStream中读写方法,方便对不同类型的数据进行读写。    /**     * 读取一个boolean型的数据,工作原理其实就是读取一个字节的数据,比较其是否等于0,因为0表示false,非0表示true。     */    public final boolean readBoolean() throws IOException {        int ch = this.read();        if (ch < 0)            throw new EOFException();        return (ch != 0);    }    /**     * 读取一个字节的数据,工作原理其实就是调用read方法读取一个字节的数据,然后转换成byte型数据。     */    public final byte readByte() throws IOException {        int ch = this.read();        if (ch < 0)            throw new EOFException();        return (byte)(ch);    }    /**     * 读取一个不带符号字节数,工作原理就是直接调用read方法并返回。     */    public final int readUnsignedByte() throws IOException {        int ch = this.read();        if (ch < 0)            throw new EOFException();        return ch;    }    /**     * 读取一个short型数据,工作原理其实就是调用两次read方法,读取的数据作为short型数据的高八位和低八位,然后返回该数据。     */    public final short readShort() throws IOException {        int ch1 = this.read();        int ch2 = this.read();        if ((ch1 | ch2) < 0)            throw new EOFException();        return (short)((ch1 << 8) + (ch2 << 0));    }    /**     * 读取一个不带符号的short型数据,工作原理其实就是调用两次read方法,读取的数据作为short型数据的高八位和低八位,然后返回改数据。     */    public final int readUnsignedShort() throws IOException {        int ch1 = this.read();        int ch2 = this.read();        if ((ch1 | ch2) < 0)            throw new EOFException();        return (ch1 << 8) + (ch2 << 0);    }    /**     * 读取一个字符,工作原理就是调用两次read方法,读取的数据作为char型数据的高八位和低八位,然后将其返回。     */    public final char readChar() throws IOException {        int ch1 = this.read();        int ch2 = this.read();        if ((ch1 | ch2) < 0)            throw new EOFException();        return (char)((ch1 << 8) + (ch2 << 0));    }    /**     * 读取一个int型数据,调用4次read方法,读取的数据代表着int型数据高位到地位,最终返回一个int型数据     */    public final int readInt() throws IOException {        int ch1 = this.read();        int ch2 = this.read();        int ch3 = this.read();        int ch4 = this.read();        if ((ch1 | ch2 | ch3 | ch4) < 0)            throw new EOFException();        return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4 << 0));    }    /**     * 读取一个long型数据,本质是调用了两次readInt方法,读取的数据作为long型数据的高32位和低32位。     */    public final long readLong() throws IOException {        return ((long)(readInt()) << 32) + (readInt() & 0xFFFFFFFFL);    }    /**     * 读取一个Float型数据,原理是通过readInt读取一个int型数据,然后将其转换为Float型数据并返回。     */    public final float readFloat() throws IOException {        return Float.intBitsToFloat(readInt());    }    /**     * 读取一个Double型数据,原理是通过readLong读取一个lo ng型数据,然后将其转换为Double型数据并返回。     */     public final double readDouble() throws IOException {        return Double.long BitsToDouble(readLong());    }    /**     * 一次读取一行数据,并将读取到的数据返回。     */    public final String readLine() throws IOException {//创建了一个StringBuffer对象,用于接收读取的数据。        StringBuffer input = new StringBuffer();//声明了一个int型变量c,用于接收读取的数据,声明了一个boolean型变量eol,表明是否读取到了换行符。        int c = -1;        boolean eol = false;        //通过一个循环来不断读取数据。        while (!eol) {            switch (c = read()) {            case -1://返回-1表示文件已经读取完毕。            case '\n'://返回'\n',表示读到换行符,此时将eol置为true,跳出循环。                eol = true;                break;            case '\r'://返回'\r',将eol置为true,因为平台换行符不同,向后读取看是否有'\n',如果没有则返回'\r'处,然后跳出循环                eol = true;                long cur = getFilePointer();                if ((read()) != '\n') {                    seek(cur);                }                break;            default://每次操作向input中添加读取的内容。                input.append((char)c);                break;            }        }//如果没有读取到任何数据,则返回null。最终将input转化成String类型然后返回。        if ((c == -1) && (input.length() == 0)) {            return null;        }        return input.toString();    }    /**     * 定义了一个readUTF的方法,实质是直接调用DataInputStream的readUTF方法。     */    public final String readUTF() throws IOException {        return DataInputStream.readUTF(this);    }    /**     * 定义了一个写入Bollean型数据的方法。原理是根据数据是否等于0来决定true和false。     */    public final void writeBoolean(boolean v) throws IOException {        write(v ? 1 : 0);    }    /**     * 每次写入一个字节的数据。     */    public final void writeByte(int v) throws IOException {        write(v);    }    /**     * 每次写入一个short型数据。实际上是通过两次write方法,每次写入一个字节的数据,分别写入short型数据高八位和低八位。     */    public final void writeShort(int v) throws IOException {        write((v >>> 8) & 0xFF);        write((v >>> 0) & 0xFF);    }    /**     * 每次写入一个char型数据。实际上是通过两次write方法,每次写入一个字节的数据,分别写入char型数据高八位和低八位。     */    public final void writeChar(int v) throws IOException {        write((v >>> 8) & 0xFF);        write((v >>> 0) & 0xFF);    }    /**     * 每次写入一个int型数据。实际上是通过4次write方法,每次写入一个字节的数据,从最高位开始,每八位一组依次写入。     */    public final void writeInt(int v) throws IOException {        write((v >>> 24) & 0xFF);        write((v >>> 16) & 0xFF);        write((v >>>  8) & 0xFF);        write((v >>>  0) & 0xFF);    }    /**     * 每次写入一个long型数据。实际上是通过8次write方法,每次写入一个字节的数据,从最高位开始,每八位一组依次写入。     */    public final void writeLong(long v) throws IOException {        write((int)(v >>> 56) & 0xFF);        write((int)(v >>> 48) & 0xFF);        write((int)(v >>> 40) & 0xFF);        write((int)(v >>> 32) & 0xFF);        write((int)(v >>> 24) & 0xFF);        write((int)(v >>> 16) & 0xFF);        write((int)(v >>>  8) & 0xFF);        write((int)(v >>>  0) & 0xFF);    }    /**     * 每次写入一个Float型数据。实际上是先将float型数据装换成int型数据,然后调用writeInt方法写入数据。     */    public final void writeFloat(float v) throws IOException {        writeInt(Float.floatToIntBits(v));    }    /**     * 每次写入一个Double型数据。实际上是先将double型数据转换成long型数据,然后调用writeLong方法写入数据。     */    public final void writeDouble(double v) throws IOException {        writeLong(Double.doubleToLongBits(v));    }    /**     * 每次写入一个字符串,实质上是先将字符串转换为字节数组,然后调用writeBytes方法写入。     */    @SuppressWarnings("deprecation")    public final void writeBytes(String s) throws IOException {        int len = s.length();        byte[] b = new byte[len];        s.getBytes(0, len, b, 0);        writeBytes(b, 0, len);    }    /**     * 每次写入一个字符串,实质上是先将字符串转换成字符数组,在将字符数组转化成字节数组,然后调用writeBytes方法写入。     */    public final void writeChars(String s) throws IOException {        int clen = s.length();        int blen = 2*clen;        byte[] b = new byte[blen];        char[] c = new char[clen];        s.getChars(0, clen, c, 0);        for (int i = 0, j = 0; i < clen; i++) {            b[j++] = (byte)(c[i] >>> 8);            b[j++] = (byte)(c[i] >>> 0);        }        writeBytes(b, 0, blen);    }    /**     * 每次读取一个UTF字符串,实质上是直接调用DataOutputStream的writeUTF方法。     */    public final void writeUTF(String str) throws IOException {        DataOutputStream.writeUTF(str, this);    }    private static native void initIDs();    private native void close0() throws IOException;    static {        initIDs();    }}

通过以上对源码的简单分析,我们对RandomAccessFile有了初步的认识,它自身就包含了读写功能,同时具有随机读取这一大亮点。下面用一个简单的例子来简单展示一下它的用法。

package RandomIO;import java.io.File;import java.io.FileNotFoundException;import java.io.IOException;import java.io.RandomAccessFile;public class RandomIOTest1 {public static void main(String[] args) {final File source = new File("./src/file/test.avi");final File target = new File("./src/file/testcopy.avi");int ThreadNum = (int) Math.ceil(Math.ceil((double) source.length() / 1024 / 1024 / 10));for (int i = 0; i < ThreadNum; i++) {Thread thread = new Thread(new MyRunnable(i, source, target));thread.start();}}}class MyRunnable implements Runnable {private int num;private File source;private File target;MyRunnable(int num, File source, File target) {this.num = num;this.source = source;this.target = target;}@Overridepublic void run() {try (RandomAccessFile sourceFile = new RandomAccessFile(source, "rw");RandomAccessFile targetFile = new RandomAccessFile(target, "rw");) {System.out.println("线程" + num + "启动");sourceFile.seek(num * 1024 * 1024 * 10);targetFile.seek(num * 1024 * 1024 * 10);byte[] buffer = null;if ((sourceFile.length() - sourceFile.getFilePointer()) < 1024 * 1024 * 10) {buffer = new byte[(int) (sourceFile.length() - sourceFile.getFilePointer())];} else {buffer = new byte[1024 * 1024 * 10];}sourceFile.read(buffer);targetFile.write(buffer);} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}System.out.println("线程" + num + "复制结束");}}
该例子使用了多线程同时复制一个文件,每个线程拷贝10MB的数据,运行上述代码后可以在指定路径下看到拷贝成功的文件,笔者拷贝了一个100多MB的小视频,拷贝的视频可以正常播放,效果图如下:

RandomAccessFile类的功能十分强大,但人无完人,它也有着一定的缺陷,从源码中可以看出,RandomAccessFile类在进行读写操作时,都是直接与底层介质进行数据传递的,即使是读写一个字节的数据,也必须进行一次I/O操作,这样就大大降低了其工作的效率。在前面如BufferedReader/BufferedWriter的学习中,我们了解了我们可以通过内置一个数据缓存区来提升读写效率,RandomAccessFile也同样可以这样操作,我们可以完全重构一个属于自己的带缓存的BufferedRandomAccessFile类。

package RandomIO;import java.io.File;import java.io.FileNotFoundException;import java.io.IOException;import java.io.RandomAccessFile;import java.util.Arrays;public class BufferedRandomAccessFile1 extends RandomAccessFile {private static final int Default_Buffer_Size = 1024 * 8;private static final int MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8;private static final long BuffMask_ = ~(((long) Default_Buffer_Size) - 1L);//表示缓存区是否有未flush的数据。private boolean hasDatas; //表示是否进行同步操作,将缓存内容flush。private boolean syncNeeded_;//当前操作文件的索引位置(包括在缓存区中)。private long cPos = 0L;//磁盘上操作文件的索引位置(存储介质中)。private long diskPos_ = 0L; private long lo_, hi_ = 0L; private long maxHi_ = (long)Default_Buffer_Size; //是否到了文件结束部分。private boolean isEOF; //内置的一个数组缓存区,默认大小是8k。private byte[] buffer;public BufferedRandomAccessFile1(File file, String mode) throws IOException {this(file, mode, Default_Buffer_Size);}public BufferedRandomAccessFile1(String name, String mode)throws IOException {this(name, mode, Default_Buffer_Size);}public BufferedRandomAccessFile1(File file, String mode, int size)throws IOException {super(file, mode);init(size);}public BufferedRandomAccessFile1(String name, String mode, int size)throws FileNotFoundException {super(name, mode);init(size);}//对内置缓存区进行初始化private void init(int size) {if (size < Default_Buffer_Size) {size = Default_Buffer_Size;} else if (size > MAX_BUFFER_SIZE) {size = MAX_BUFFER_SIZE;}buffer = new byte[size];}//将缓存区中的数据同步写出到存储介质中。public void sync() throws IOException {if (syncNeeded_) {//将内置缓存区中的数据写入flush();//将文件通道内未写入磁盘的数据强制写入到磁盘中,传入的参数表示是否将文件元信息写入到磁盘之上。getChannel().force(true);syncNeeded_ = false;}}// close前将缓存区刷新一次防止缓存区中有未写入的数据,然后将缓存区置为null,调用父类的close方法释放资源。public void close() throws IOException {this.flush();this.buffer = null;super.close();}//将缓存区中内容写入存储介质中public void flush() throws IOException {this.flushBuffer();}//将缓存中内容写入存储介质之中private void flushBuffer() throws IOException {if (hasDatas) {if (diskPos_ != lo_)super.seek(lo_);int len = (int) (cPos - lo_);super.write(buffer, 0, len);diskPos_ = cPos;hasDatas = false;}}//向缓存区中填充数据。返回实际填充了多少字节的数据。private int fillBuffer() throws IOException {int nextChar = 0;int nChars = buffer.length;//通过一个循环,向缓存区中填充数据,直至将缓存区填满或者文件读到末尾。while (nChars > 0) {int n = super.read(buffer, nextChar, nChars);if (n < 0)break;nextChar += n;nChars -= n;}if ((nextChar < 0) && (isEOF = (nextChar < buffer.length))) {//将为缓存区中未填充到的部分全用-1初始化。Arrays.fill(buffer, nextChar, buffer.length, (byte) 0xff);}diskPos_ += nextChar;return nextChar;}//跳过指定的字节数public void seek(long pos) throws IOException {if (pos >= hi_ || pos < lo_) {flushBuffer();lo_ = pos & BuffMask_; maxHi_ = lo_ + (long) buffer.length;if (diskPos_ != lo_) {super.seek(lo_);diskPos_ = lo_;}int n = fillBuffer();hi_ = lo_ + (long) n;} else {if (pos < cPos) {flushBuffer();}}cPos = pos;}public long getFilePointer() {return cPos;}public long length() throws IOException {return Math.max(cPos, super.length());}public int read() throws IOException {if (cPos >= hi_) {if (isEOF)return -1;seek(cPos);if (cPos == hi_)return -1;}byte res = buffer[(int) (cPos - lo_)];cPos++;return ((int) res) & 0xFF; }public int read(byte[] b) throws IOException {return read(b, 0, b.length);}public int read(byte[] b, int off, int len) throws IOException {if (cPos >= hi_) {if (isEOF)return -1;seek(cPos);if (cPos == hi_)return -1;}len = Math.min(len, (int) (hi_ - cPos));int buffOff = (int) (cPos - lo_);System.arraycopy(buffer, buffOff, b, off, len);cPos += len;return len;}public void write(int b) throws IOException {if (cPos >= hi_) {if (isEOF && hi_ < maxHi_) {hi_++;} else {seek(cPos);if (cPos == hi_) {hi_++;}}}buffer[(int) (cPos - lo_)] = (byte) b;cPos++;hasDatas = true;syncNeeded_ = true;}public void write(byte[] b) throws IOException {write(b, 0, b.length);}public void write(byte[] b, int off, int len) throws IOException {while (len > 0) {int n = writeAtMost(b, off, len);off += n;len -= n;hasDatas = true;syncNeeded_ = true;}}private int writeAtMost(byte[] b, int off, int len) throws IOException {if (cPos >= hi_) {if (isEOF && hi_ < maxHi_) {hi_ = maxHi_;} else {seek(cPos);if (cPos == hi_) {hi_ = maxHi_;}}}len = Math.min(len, (int) (hi_ - cPos));int buffOff = (int) (cPos - lo_);System.arraycopy(b, off, buffer, buffOff, len);cPos += len;return len;}}
最终,用一个小例子来验证其工作效率,我们将比较RandomAccessFile,BufferedRandomAccessFile,BufferedInput/OutputStream的效率。
package RandomIO;import java.io.BufferedInputStream;import java.io.BufferedOutputStream;import java.io.File;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.RandomAccessFile;public class RandomIOTest2 {public static void main(String[] args) {long startTime;long endTime;File source = new File("./src/file/test.avi");File target = new File("./src/file/testcopy.avi");byte[] buffer = new byte[1024];startTime = System.currentTimeMillis();int len;try (RandomAccessFile sourceFile = new RandomAccessFile(source, "rw");RandomAccessFile targetFile = new RandomAccessFile(target, "rw")) {while ((len = sourceFile.read(buffer)) != -1) {targetFile.write(buffer, 0, len);}endTime = System.currentTimeMillis();System.out.println("RandomAccessFile拷贝耗时" + (endTime - startTime)+ "ms");} catch (Exception e) {}startTime = System.currentTimeMillis();try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(source));BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(target));) {while ((len = bis.read(buffer)) != -1) {bos.write(buffer, 0, len);}endTime = System.currentTimeMillis();System.out.println("BufferedInputStream/BuffedOutputStream拷贝耗时"+ (endTime - startTime) + "ms");} catch (Exception e) {}startTime = System.currentTimeMillis();try (BufferedRandomAccessFile1 sourceFile = new BufferedRandomAccessFile1(source, "rw");BufferedRandomAccessFile1 targetFile = new BufferedRandomAccessFile1(target, "rw")) {while ((len = sourceFile.read(buffer)) != -1) {targetFile.write(buffer, 0, len);}endTime = System.currentTimeMillis();System.out.println("BufferedRandomAccessFile1拷贝耗时" + (endTime - startTime)+ "ms");} catch (Exception e) {}}}
执行上述代码,可以看到以下输出结果:


从输出中可以看出,RandomAccessFile的效率确实很低,但加上缓存后,工作效率立马提升。
以上为本篇内容。

原创粉丝点击