java--io和nio读写文件

java–io和nio读写文件

文章目录

  • java--io和nio读写文件
      • 1.java io
        • 1.1 java io主要的类
        • 1.2 io中字符流和字节流的区别
      • 2.文件拷贝
        • 2.1文件复制:io流读写文件、大文件
        • 2.2文件复制:nio缓冲区读写文件
        • 2.3文件复制:MappedByteBuffer内存映射读写文件
        • 2.4文件复制:FileChannel 传输:Channel-to-Channel传输
        • 2.5简单测试:

环境

idea2018,jdk1.8

记录一下java–io和nio笔记,参考文献:《Java NIO》

1.java io

详细介绍:https://blog.csdn.net/qq_25184739/article/details/51205186

1.1 java io主要的类

java--io和nio读写文件_第1张图片

Java IO面向流意味着每次从流中读一个或多个字节,直至读取所有字节,它们没有被缓存在任何地方;Java NIO的缓冲导向方法略有不同。数据读取到一个它稍后处理的缓冲区,需要时可在缓冲区中前后移动。这就增加了处理过程中的灵活性。
1. File(文件特征与管理):用于文件或者目录的描述信息,例如生成新目录,修改文件名,删除文件,判断文件所在路径等。
2. InputStream(二进制格式操作):抽象类,基于字节的输入操作,是所有输入流的父类。定义了所有输入流都具有的共同特征。
3. OutputStream(二进制格式操作):抽象类。基于字节的输出操作。是所有输出流的父类。定义了所有输出流都具有的共同特征。
4.Reader(文件格式操作):抽象类,基于字符的输入操作。
5. Writer(文件格式操作):抽象类,基于字符的输出操作。
6. RandomAccessFile(随机文件操作):一个独立的类,直接继承至Object.它的功能丰富,可以从文件的任意位置进行存取(输入输出)操作。
注意:FileInputStream FileOutputStream两个类允许我们从文件开始至文件结尾一个字节或字符的读取文件,或者将读取的文件
写入字节数组或字符数组。如果我们想随机的读取文件内容,可以使用RandomAccessFile

1.2 io中字符流和字节流的区别

1)字节流没有缓冲区,是直接输出的,而字符流是输出到缓冲区的。因此在输出时,字节流不调用colse()方法时,信息已经输出了,而字符流只有在调用close()方法关闭缓冲区时,信息才输出。要想字符流在未关闭时输出信息,则需要手动调用flush()方法。

2) 读写单位不同:字节流以字节(8bit)为单位,字符流以字符为单位,根据码表映射字符,一次可能读多个字节。

· 处理对象不同:字节流能处理所有类型的数据(如图片、avi等),而字符流只能处理字符类型的数据

字节流和字符流的区别:(详细可以参见http://blog.csdn.net/qq_25184739/article/details/51203733)

2.文件拷贝

2.1文件复制:io流读写文件、大文件

注意:文件流的关闭一般要放在finally块中。

读: 读取内容,放到字节数组(字符数组)里面

写:可以一次写入一个字节或一个字符到文件中,也可以直接写入字节数组或字符数组到文件中

复制文件:FileInputStream FileOutputStream

 public void test0(String srcFile, String destFile) throws IOException {
        InputStream in = new FileInputStream(srcFile);
        OutputStream out = new FileOutputStream(destFile);
        //BufferedInputStream in = new FileInputStream(srcFile);
        // BufferedOutputStream out = new FileOutputStream(destFile);
        int length = 0;// 每次读取到的字节数组的长度
        //用来存储每次读取到的字节数组,设置的读取大小跟耗时有关
        byte[] bytes = new byte[1024*4];
        long start = System.currentTimeMillis();
        while ((length = in.read(bytes)) > 0) {
            out.write(bytes, 0, length);
        }
        in.close();
        out.close();
        System.out.println("test0 Spend: " + (double) (System.currentTimeMillis() - start) + "ms");
    }

复制文件:RandomAccessFile

public void test1(String srcFile, String destFile) throws IOException {
        Reader in = new FileReader(srcFile);
        Writer out = new FileWriter(destFile);
        char[] data = new char[1024*2];//设置的读取大小跟耗时有关
        int length = 0;// 每次读取到的字符数组的长度
        long start = System.currentTimeMillis();
        while ((length = in.read(data)) > 0) {
            out.write(data, 0, length);
        }
        in.close();
        out.close();
        System.out.println("test1 Spend: " + (double) (System.currentTimeMillis() - start)+ "ms");
    }

复制文件:FileReader FileWriter

/**
     * 调用系统内核的read()方法,内核从磁盘读取数据到内核缓冲区,
     * 这个过程由磁盘控制器通过DMA操作将数据从磁盘读取取内核缓冲区,此过程不依赖于CPU。
     * 然后用户进程再将数据从内核缓冲区拷贝到用户空间缓冲区。用户进程再从用户空间缓冲区中读取数据
     * 写相反
     *
     * @param srcFile
     * @param destFile
     * @throws IOException
     */
    public void test2(String srcFile, String destFile) throws IOException {
        // 2、自己处理Buffer(RandomAccessFile): 0.13s
        RandomAccessFile rafi = new RandomAccessFile(srcFile, "r");
        RandomAccessFile rafo = new RandomAccessFile(destFile, "rw");
        byte[] buf = new byte[1024*4];//设置的读取大小跟耗时有关
        long start = System.currentTimeMillis();
        int length = 0;
        while ((length = rafi.read(buf)) != -1) {
            rafo.write(buf, 0, length);
        }
        rafi.close();
        rafo.close();
        System.out.println("test2 Spend: " + (double) (System.currentTimeMillis() - start) + "ms");

    }

2.2文件复制:nio缓冲区读写文件

public void test3(String srcFile, String destFile) throws IOException {
        /*
         * 测试结果与ByteBuffer size有关
         */

        RandomAccessFile rafi = new RandomAccessFile(srcFile, "r");
        RandomAccessFile rafo = new RandomAccessFile(destFile, "rw");
        FileChannel fci = rafi.getChannel();
        FileChannel fco = rafo.getChannel();
        //利用堆内存,设置的分配大小跟耗时有关
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024*4);
        //这部分直接用的系统内存,所以对JVM的内存没有影响
        //ByteBuffer byteBuffer = ByteBuffer.allocateDirect(1024*8);
        long start = System.currentTimeMillis();
        while (fci.read(byteBuffer) != -1) {
            //flip方法将Buffer从写模式切换到读模式。调用flip()方法会将position设回0,并将limit设置成之前position的值
            byteBuffer.flip();
            fco.write(byteBuffer);
            //调用clear()方法或者compact()方法
            //用clear()方法:position将被设回0,limit设置成capacity
            byteBuffer.clear();
        }
        fci.close();
        fco.close();
        rafi.close();
        rafo.close();
        System.out.println("test3 Spend: " + (double) (System.currentTimeMillis() - start) + "ms");

    }

2.3文件复制:MappedByteBuffer内存映射读写文件

 /**
     * 现代操作系统大都支持虚拟内存映射,
     * 这样,我们可以把内核空间地址与用户空间的虚拟地址映射到同一个物理地址
     * 好处:我们在读取磁盘文件时,再也不用通过内核缓冲区到用户进程缓冲区的来回拷贝操作了。操作系统会通过一些页面调度算法来将磁盘文件载入对分页区进行高速缓存的物理内存。
     * 我们就可以通过映射后物理内存来读取磁盘文件了。
     * @param srcFile
     * @param destFile
     * @throws IOException
     */
    public void test5(String srcFile, String destFile) throws IOException {
        /*
         * 测试结果与MappedByteBuffer size有关
         */
        RandomAccessFile rafi = new RandomAccessFile(srcFile, "r");
        RandomAccessFile rafo = new RandomAccessFile(destFile, "rw");
        FileChannel fci = rafi.getChannel();
        FileChannel fco = rafo.getChannel();
        long size = fci.size();
        //FileChannel提供了map方法来把文件影射为内存映像文件(即虚拟内存);
        //可以把文件的从position开始的size大小的区域映射为内存映像文件,mode指出了 可访问该内存映像文件的方式:
        MappedByteBuffer mbbi = fci.map(FileChannel.MapMode.READ_ONLY, 0, size);
        MappedByteBuffer mbbo = fco.map(FileChannel.MapMode.READ_WRITE, 0, size);
        long start = System.currentTimeMillis();
        //或者
        mbbo.put(mbbi.get(new byte[mbbi.limit()]));
//        for(int i=0;i

2.4文件复制:FileChannel 传输:Channel-to-Channel传输

/**
     * FileChannel 传输:Channel-to-Channel传输
     * @param srcFile
     * @param destFile
     * @throws IOException
     */

    public void test6(String srcFile, String destFile) throws IOException {
        /*
         *JAVA在NIO FileChannel 实现零拷贝
         */
        RandomAccessFile rafi = new RandomAccessFile(srcFile, "r");
        RandomAccessFile rafo = new RandomAccessFile(destFile, "rw");
        FileChannel fci = rafi.getChannel();
        FileChannel fco = rafo.getChannel();
        long start = System.currentTimeMillis();
        fci.transferTo(0, fci.size(), fco);
        //destCh.transferFrom(sourceCh, 0, sourceCh.size());
        fci.close();
        fco.close();
        rafi.close();
        rafo.close();
        System.out.println("test6 Spend: " + (double) (System.currentTimeMillis() - start) + "ms");

    }

2.5简单测试:

文件复制的简单测试耗时:大文件(1-2g)读写用 MappedByteBuffer比较高效

注意:

1.跟代码参数设置、文件大小、硬件设备有关;文件大时,内存分配和文件内存映射都需要耗时,可以在程序启动时分配或加载好。

2.使用MappedByteBuffer 进行操作 但是MappedByteBuffer和它和他相关联的资源 在垃圾回收之前一直保持有效 但是MappedByteBuffer 保存着对源文件的引用 ,因此删除源文件失败

1)100m文件复制耗时:

java--io和nio读写文件_第2张图片

2)1g的文件复制耗时:
java--io和nio读写文件_第3张图片

你可能感兴趣的:(Netty学习笔记,JAVA基础,nio,ByteBuffer复制文件,零拷贝)