文件

读文件

如果你需要在不同端之间读取文件,你可以根据该文件是二进制文件还是文本文件来选择使用 FileInputStream 或者 FileReader

这两个类允许你从文件开始到文件末尾一次读取一个字节或者字符,或者将读取到的字节写入到字节数组或者字符数组。

你不必一次性读取整个文件,相反你可以按顺序地读取文件中的字节和字符。

写文件

如果你需要在不同端之间进行文件的写入,你可以根据你要写入的数据是二进制型数据还是字符型数据选用 FileOutputStream 或者 FileWriter

你可以一次写入一个字节或者字符到文件中,也可以直接写入一个字节数组或者字符数据。数据按照写入的顺序存储在文件当中。

随机存取文件

你可以通过 RandomAccessFile 对文件进行随机存取。

随机存取并不意味着你可以在真正随机的位置进行读写操作,它只是意味着你可以跳过文件中某些部分进行操作,并且支持同时读写,不要求特定的存取顺序。

这使得 RandomAccessFile 可以覆盖一个文件的某些部分、或者追加内容到它的末尾、或者删除它的某些内容,当然它也可以从文件的任何位置开始读取文件。

文件信息

通过 File 类可以获取文件和目录的信息。

File

功能

Java IO API中的FIle类可以让你访问底层文件系统,通过File类,你可以做到以下几点:

  • 检测文件是否存在

  • 读取文件长度

  • 重命名或移动文件

  • 删除文件

  • 检测某个路径是文件还是目录

  • 读取目录中的文件列表

RandomAccessFile

由于RandomAccessFile 可以自由访问文件的任意位置,所以如果需要访问文件的部分内容,而不是把文件从头读到尾,使用RandomAccessFile将是更好的选择。

  • 局限

RandomAccessFile 的方法虽然多,但它有一个最大的局限,就是只能读写文件,不能读写其他IO节点。

RandomAccessFile 的构造函数

RandomAccessFile 类有两个构造函数,其实这两个构造函数基本相同,只不过是指定文件的形式不同——一个需要使用String参数来指定文件名,一个使用File参数来指定文件本身。

除此之外,创建RandomAccessFile对象时还需要指定一个mode参数,该参数指定RandomAccessFile的访问模式,一共有4种模式。

"r" : 以只读方式打开。调用结果对象的任何 write 方法都将导致抛出 IOException。
"rw": 打开以便读取和写入。
"rws": 打开以便读取和写入。相对于 "rw","rws" 还要求对“文件的内容”或“元数据”的每个更新都同步写入到基础存储设备。
"rwd" : 打开以便读取和写入,相对于 "rw","rwd" 还要求对“文件的内容”的每个更新都同步写入到基础存储设备。

核心方法

RandomAccessFile既可以读文件,也可以写文件,所以类似于InputStream的read()方法,以及类似于OutputStream的write()方法,RandomAccessFile都具备。

除此之外,RandomAccessFile具备两个特有的方法,来支持其随机访问的特性。

RandomAccessFile对象包含了一个记录指针,用以标识当前读写处的位置,当程序新创建一个RandomAccessFile对象时,该对象的文件指针记录位于文件头(也就是0处),当读/写了n个字节后,文件记录指针将会后移n个字节。

除此之外,RandomAccessFile还可以自由移动该记录指针。

下面就是RandomAccessFile具有的两个特殊方法,来操作记录指针,实现随机访问:

long getFilePointer():返回文件记录指针的当前位置
void seek(long pos):将文件指针定位到pos位置

实例

利用RandomAccessFile实现文件的多线程下载,即多线程下载一个文件时,将文件分成几块,每块用不同的线程进行下载。

下面是一个利用多线程在写文件时的例子,其中预先分配文件所需要的空间,然后在所分配的空间中进行分块,然后写入:

  • RandomAccessFileTest.java
import java.io.IOException;
import java.io.RandomAccessFile;

public class RandomAccessFileTest {

    public static void main(String[] args) throws Exception {
        // 预分配文件所占的磁盘空间,磁盘中会创建一个指定大小的文件
        RandomAccessFile raf = new RandomAccessFile("abc.txt", "rw");
        // 预分配 8K 的文件空间
        raf.setLength(1024*8);
        raf.close();

        // 所要写入的文件内容
        String s1 = "第一个字符串";
        String s2 = "第二个字符串";
        String s3 = "第三个字符串";
        String s4 = "第四个字符串";
        String s5 = "第五个字符串";

        // 利用多线程同时写入一个文件
        // 从文件的1024字节之后开始写入数据
        new FileWriteThread(1024*1,s1.getBytes()).start();
        // 从文件的2048字节之后开始写入数据
        new FileWriteThread(1024*2,s2.getBytes()).start();
        // 从文件的3072字节之后开始写入数据
        new FileWriteThread(1024*3,s3.getBytes()).start();
        // 从文件的4096字节之后开始写入数据
        new FileWriteThread(1024*4,s4.getBytes()).start();
        // 从文件的5120字节之后开始写入数据
        new FileWriteThread(1024*5,s5.getBytes()).start();
    }

    /**
     * 利用线程在文件的指定位置写入指定数据
     */
    private static class FileWriteThread extends Thread{
        private int skip;
        private byte[] content;

        FileWriteThread(int skip, byte[] content){
            this.skip = skip;
            this.content = content;
        }

        @Override
        public void run(){
            try (RandomAccessFile raf = new RandomAccessFile("abc.txt", "rw")) {
                raf.seek(skip);
                raf.write(content);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

参考资料

File

https://docs.oracle.com/javase/7/docs/api/java/io/File.html

https://blog.csdn.net/u013309870/article/details/52098414

http://ifeve.com/java-io-%E6%96%87%E4%BB%B6/

http://ifeve.com/java-io-file/

RandomAccessFile

https://docs.oracle.com/javase/7/docs/api/java/io/RandomAccessFile.html

https://www.jianshu.com/p/c8aa567f2101

https://blog.csdn.net/akon_vm/article/details/7429245