Java 拷贝文件的 9 种方式和FileInputStream,BufferedInputStream 和 FileReader 区别

avatar 2018年03月05日18:42:28 6 3756 views
博主分享免费Java教学视频,B站账号:Java刘哥
为了比较 Java 几种方式读取字符的速度,博主从网上下载了一个 三国演义.txt ,文件大小 1196684 字节,即 1.1 MB。   先放总结 如果是对图片、音乐、视频、压缩包等文件的拷贝,推荐使用 缓冲字节输入/输出流(BufferedOutputStream)+字节数组 (第4种) 如果是对文本文件的读取和拷贝,推荐使用 字符缓存输入/输出流(BufferedWriter) + 按行读取 (第9种) 对于特大文件,比如 1 个 G 以上的,多线程操作可以使用 NIO (第 10 种)。  

一、字节流

1、FileInputStrem 按字节流 逐字节读写(速度最慢)
  1. /**
  2.      * FileInputStream 方式拷贝文件
  3.      * @param srcFileName
  4.      * @param descFileName
  5.      * @throws IOException
  6.      */
  7.     public static void copyFile(String srcFileName, String descFileName) throws IOException {
  8.         Long startTime = System.currentTimeMillis();
  9.         FileInputStream fis = new FileInputStream(srcFileName);
  10.         FileOutputStream fos = new FileOutputStream(descFileName);
  11.         int len = 0;
  12.         while ((len = fis.read()) != -1) {//读取
  13.             //写入另一个文件
  14.             fos.write(len);
  15.         }
  16.         Long endTime = System.currentTimeMillis();
  17.         System.out.println("总共耗时:" + (endTime - startTime) + "毫秒");//2937
  18.     }
  19.     public static void main(String args[]) throws IOException {
  20.         String srcFileName = "/Users/liuyanzhao/Desktop/temp/三国演义.txt";
  21.         String descFileName = "/Users/liuyanzhao/Desktop/temp/三国演义的拷贝.txt";
  22.         copyFile(srcFileName, descFileName);
  23.     }
总共耗时:2937毫秒   2、FileInputStream 构造一个缓冲数组进行读写(速度提升很多)
  1. /**
  2.      * FileInputStream 方式拷贝文件,添加缓存数组
  3.      * @param srcFileName
  4.      * @param descFileName
  5.      * @throws IOException
  6.      */
  7.     public static void copyFile(String srcFileName, String descFileName) throws IOException {
  8.         Long startTime = System.currentTimeMillis();
  9.         FileInputStream fis = new FileInputStream(srcFileName);
  10.         FileOutputStream fos = new FileOutputStream(descFileName);
  11.         byte[] buffer = new byte[1024];//构造一个长度为1024的字节数组
  12.         int len = 0;
  13.         while ((len = fis.read(buffer)) != -1) {//读取
  14.             //写入另一个文件
  15.             fos.write(buffer);
  16.         }
  17.         Long endTime = System.currentTimeMillis();
  18.         System.out.println("总共耗时:" + (endTime - startTime) + "毫秒");// 6
  19.     }
  20.     public static void main(String args[]) throws IOException {
  21.         String srcFileName = "/Users/liuyanzhao/Desktop/temp/三国演义.txt";
  22.         String descFileName = "/Users/liuyanzhao/Desktop/temp/三国演义的拷贝.txt";
  23.         copyFile(srcFileName, descFileName);
  24.     }
总共耗时:6毫秒   3、利用字节缓冲区流 BufferedInputStream 和 BufferedOutputStream来直接逐字节读写(比第1个快很多,比第2个慢)
  1. /**
  2.      * 使用 BufferedInputStream 和 BufferedOutputStream 通过字符拷贝
  3.      * @param srcFileName
  4.      * @param descFileName
  5.      * @throws IOException
  6.      */
  7.     public static void copyFile(String srcFileName, String descFileName) throws IOException {
  8.         Long startTime = System.currentTimeMillis();
  9.         //缓冲区字节输入流
  10.         BufferedInputStream bis = new BufferedInputStream(new FileInputStream(srcFileName));
  11.         //缓冲区字节输出流
  12.         BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(descFileName));
  13.         int len = 0;
  14.         while ((len = bis.read()) != -1) {//读取
  15.             //写入另一个文件
  16.             bos.write(len);
  17.         }
  18.         Long endTime = System.currentTimeMillis();
  19.         System.out.println("总共耗时:" + (endTime - startTime) + "毫秒");// 60
  20.     }
  21.     public static void main(String args[]) throws IOException {
  22.         String srcFileName = "/Users/liuyanzhao/Desktop/temp/三国演义.txt";
  23.         String descFileName = "/Users/liuyanzhao/Desktop/temp/三国演义的拷贝.txt";
  24.         copyFile(srcFileName, descFileName);
  25.     }
总共耗时:60毫秒   4、利用字节缓冲区流BufferedInputStream和BufferedOutputStream通过构造一个缓冲数组进行读写(速度最快)
  1. /**
  2.      * 使用 BufferedInputStream 和 BufferedOutputStream 通过一个缓冲数组 字符拷贝
  3.      * @param srcFileName
  4.      * @param descFileName
  5.      * @throws IOException
  6.      */
  7.     public static void copyFile(String srcFileName, String descFileName) throws IOException {
  8.         Long startTime = System.currentTimeMillis();
  9.         //缓冲区字节输入流
  10.         BufferedInputStream bis = new BufferedInputStream(new FileInputStream(srcFileName));
  11.         //缓冲区字节输出流
  12.         BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(descFileName));
  13.         byte[] buffer = new byte[1024];//构造一个长度为1024的字节数组
  14.         int len = 0;
  15.         while ((len = bis.read(buffer)) != -1) {//读取
  16.             //写入另一个文件
  17.             bos.write(buffer);
  18.         }
  19.         Long endTime = System.currentTimeMillis();
  20.         System.out.println("总共耗时:" + (endTime - startTime) + "毫秒");// 4
  21.     }
  22.     public static void main(String args[]) throws IOException {
  23.         String srcFileName = "/Users/liuyanzhao/Desktop/temp/三国演义.txt";
  24.         String descFileName = "/Users/liuyanzhao/Desktop/temp/三国演义的拷贝.txt";
  25.         copyFile(srcFileName, descFileName);
  26.     }
总共耗时:4 毫秒  

二、字符流

5、利用字符流InputStreamWriter和 OutputStreamWriter直接按字节读取
  1. /**
  2.      * 使用 InputStreamWriter和 OutputStreamWriter直接按字节读取拷贝
  3.      * @param srcFileName
  4.      * @param descFileName
  5.      * @throws IOException
  6.      */
  7.     public static void copyFile(String srcFileName, String descFileName) throws IOException {
  8.         Long startTime = System.currentTimeMillis();
  9.         //缓冲区字符输入流
  10.         InputStreamReader isr = new InputStreamReader(new FileInputStream(srcFileName));
  11.         //缓冲区字符输出流
  12.         OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(descFileName));
  13.         int len = 0;
  14.         while ((len = isr.read()) != -1) {//直接按字节读取
  15.             //写入另一个文件
  16.             osw.write(len);
  17.         }
  18.         Long endTime = System.currentTimeMillis();
  19.         System.out.println("总共耗时:" + (endTime - startTime) + "毫秒");// 586
  20.     }
  21.     public static void main(String args[]) throws IOException {
  22.         String srcFileName = "/Users/liuyanzhao/Desktop/temp/三国演义.txt";
  23.         String descFileName = "/Users/liuyanzhao/Desktop/temp/三国演义的拷贝.txt";
  24.         copyFile(srcFileName, descFileName);
  25.     }
总共耗时:586毫秒 会乱码   6、字符流InputStreamWriter和 OutputStreamWriter直接用缓冲区数组读写
  1. /**
  2.      * 使用 InputStreamWriter和 OutputStreamWriter直接按字节读取拷贝
  3.      * @param srcFileName
  4.      * @param descFileName
  5.      * @throws IOException
  6.      */
  7.     public static void copyFile(String srcFileName, String descFileName) throws IOException {
  8.         Long startTime = System.currentTimeMillis();
  9.         //缓冲区字符输入流
  10.         InputStreamReader isr = new InputStreamReader(new FileInputStream(srcFileName));
  11.         //缓冲区字符输出流
  12.         OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(descFileName));
  13.         char[] buffer = new char[1024];//构造一个长度为1024的字节数组
  14.         int len = 0;
  15.         while ((len = isr.read(buffer)) != -1) {//直接按字节读取
  16.             //写入另一个文件
  17.             osw.write(buffer);
  18.         }
  19.         Long endTime = System.currentTimeMillis();
  20.         System.out.println("总共耗时:" + (endTime - startTime) + "毫秒");// 385
  21.     }
  22.     public static void main(String args[]) throws IOException {
  23.         String srcFileName = "/Users/liuyanzhao/Desktop/temp/三国演义.txt";
  24.         String descFileName = "/Users/liuyanzhao/Desktop/temp/三国演义的拷贝.txt";
  25.         copyFile(srcFileName, descFileName);
  26.     }
  总共耗时:385毫秒   7、字符缓冲流BufferedWriter和BufferedReader直接逐字节读写
  1. /**
  2.      * 使用 BufferedWriter和BufferedReader直接逐字节读写
  3.      * @param srcFileName
  4.      * @param descFileName
  5.      * @throws IOException
  6.      */
  7.     public static void copyFile(String srcFileName, String descFileName) throws IOException {
  8.         Long startTime = System.currentTimeMillis();
  9.         //缓冲区字符输入流
  10.         BufferedReader bf = new BufferedReader(new FileReader(srcFileName));
  11.         //缓冲区字符输出流
  12.         BufferedWriter bw = new BufferedWriter(new FileWriter(descFileName));
  13.         int len = 0;
  14.         while ((len = bf.read()) != -1) {//直接按字节读取
  15.             //写入另一个文件
  16.             bw.write(len);
  17.         }
  18.         Long endTime = System.currentTimeMillis();
  19.         System.out.println("总共耗时:" + (endTime - startTime) + "毫秒");// 412
  20.     }
  21.     public static void main(String args[]) throws IOException {
  22.         String srcFileName = "/Users/liuyanzhao/Desktop/temp/三国演义.txt";
  23.         String descFileName = "/Users/liuyanzhao/Desktop/temp/三国演义的拷贝.txt";
  24.         copyFile(srcFileName, descFileName);
  25.     }
总共耗时:433毫秒   8、字符缓冲流 BufferedWriter 和 BufferedReader 按照数组大小逐块读写
  1. /**
  2.     * 使用 BufferedWriter和BufferedReader直接逐字节读写
  3.     * @param srcFileName
  4.     * @param descFileName
  5.     * @throws IOException
  6.     */
  7.    public static void copyFile(String srcFileName, String descFileName) throws IOException {
  8.        Long startTime = System.currentTimeMillis();
  9.        //缓冲区字符输入流
  10.        BufferedReader bf = new BufferedReader(new FileReader(srcFileName));
  11.        //缓冲区字符输出流
  12.        BufferedWriter bw = new BufferedWriter(new FileWriter(descFileName));
  13.        int len = 0;
  14.        char[] buffer = new char[1024];
  15.        while ((len = bf.read(buffer)) != -1) {//直接按字节读取
  16.            //写入另一个文件
  17.            bw.write(buffer);
  18.        }
  19.        Long endTime = System.currentTimeMillis();
  20.        System.out.println("总共耗时:" + (endTime - startTime) + "毫秒");// 387
  21.    }
  22.    public static void main(String args[]) throws IOException {
  23.        String srcFileName = "/Users/liuyanzhao/Desktop/temp/三国演义.txt";
  24.        String descFileName = "/Users/liuyanzhao/Desktop/temp/三国演义的拷贝.txt";
  25.        copyFile(srcFileName, descFileName);
  26.    }
总共耗时:381毫秒   9、字符缓冲流BufferedWriter和BufferedReader按逐行读写(应用于文本读写)
  1. /**
  2.     * 使用 BufferedWriter和BufferedReader直接逐字节读写
  3.     * @param srcFileName
  4.     * @param descFileName
  5.     * @throws IOException
  6.     */
  7.    public static void copyFile(String srcFileName, String descFileName) throws IOException {
  8.        Long startTime = System.currentTimeMillis();
  9.        //缓冲区字符输入流
  10.        BufferedReader bf = new BufferedReader(new FileReader(srcFileName));
  11.        //缓冲区字符输出流
  12.        BufferedWriter bw = new BufferedWriter(new FileWriter(descFileName));
  13.        String str;
  14.        while ((str = bf.readLine()) != null) {//直接按字节读取
  15.            //写入另一个文件
  16.            bw.write(str);
  17.        }
  18.        Long endTime = System.currentTimeMillis();
  19.        System.out.println("总共耗时:" + (endTime - startTime) + "毫秒");// 353
  20.    }
  21.    public static void main(String args[]) throws IOException {
  22.        String srcFileName = "/Users/liuyanzhao/Desktop/temp/三国演义.txt";
  23.        String descFileName = "/Users/liuyanzhao/Desktop/temp/三国演义的拷贝.txt";
  24.        copyFile(srcFileName, descFileName);
  25.    }
总共耗时:353 毫秒   注意: 后面的几种字符流的均会出现中文字符乱码的情况。 需要指定字符集
  1. InputStreamReader isr = new InputStreamReader(new FileInputStream(srcFileName),"GBK");
 
  1. BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(srcFileName),"GBK"));
    以上只是通过一个 1MB 的文件的复制测试,并不能说明问题。
总结:
复制文本文件有几种方式?  9种 复制图片文件有几种方式? 4种           他们各自的优缺点是什么? 字符流按字符处理数据,速度较之于同原理的字节流快,但是使用范围较小,仅限文本文件; 字节流处理范围广,文本,音频,视频都可以处理。
 

三、FileInputStream 与BufferedInputStream区别:

FileInputStream是字节流,BufferedInputStream是字节缓冲流,使用BufferedInputStream读资源比FileInputStream读取资源的效率高(BufferedInputStream的read方法会读取尽可能多的字节),且FileInputStream对象的read方法会出现阻塞;
了解“堵塞”的意思吧!

假设一个文件的长度是100个字节,要将之读取到内存中,再假设您每次只读取10个字节,那么读完整个文件是不是读取10次的呀?

假设老板让你完成100件事情,老板说,你每天只完成10件就可以了,难道你非得等到第十天才完成第100件事情吗?有一天您在中午下班前就完成了10件事情,下午您不妨多干一点,那么也许在第9天的时候就完成了100件事情。

同理,BufferedInputStream有可能会读取比您规定的更多的东西到内存,以减少访问IO的次数,
总之您要记住一句话,访问IO的次数越少,性能就越高,原因就在于CPU和内存的速度》》》》远大于硬盘或其他外部设备的速度。

换一个不太恰当的例子来说,您和您的朋友一起去登山,你朋友太不给力了,走一会儿就要休息,而您呢,您的体力比他要好的多,根本不需要休息,所以每当他休息的时候,您得等着他,您那时候什么也干不了,这就叫堵塞,堵塞就是说您有能力干某事,但是迫于某种原因您什么也干不了,只能干等。所以您朋友休息的次数越少,你们两个到达山顶所花费的时间就越少。CPU访问硬盘的次数越少,程序就越快。BufferedInputStream在小型文件中的性能优势无法体现出来,假设您将以个2G大小的文件从D盘完全复制到E盘,性能之优势便展露无疑!
   

四、FileInputStream与FileReader区别:

两个类的构造函数的形式和参数都是相同的,参数为 File 对象或者表示路径的 String ,它们到底有何区别呢? FileInputStream :以字节流方式读取; FileReader :把文件转换为字符流读入; InputStream提供的是字节流的读取,而非文本读取,这是和Reader类的根本区别。用Reader读取出来的是char数组或者String ,使用InputStream读取出来的是byte数组。 Reader类及其子类提供的字符流的读取char,inputStream及其子类提供字节流的读取byte,所以FileReader类是将文件按字符流的方式读取,FileInputStream则按字节流的方式读取文件;InputStreamReader可以将读如stream转换成字符流方式,是reader和stream之间的桥梁 最初Java是不支持对文本文件的处理的,为了弥补这个缺憾而引入了Reader和Writer两个类。 FileInputStream 类以二进制输入 / 输出, I/O 速度快且效率搞,但是它的 read ()方法读到的是一个字节,很不利于人们阅读。 而 FileReader 类弥补了这个缺陷,可以以文本格式输入/ 输出,非常方便;比如可以使用 while((ch = filereader.read())!=-1 ) 循环来读取文件;可以使用BufferedReader 的 readLine() 方法一行一行的读取文本。 当我们读写文本文件的时候,采用 Reader 是非常方便的, 比如 FileReader , InputStreamReader 和 BufferedReader 。其中最重要的类是 InputStreamReader ,它是字节转换为字符的桥梁。 你可以在构造器中指定编码的方式,如果不指定的话将采用底层操作系统的默认编码方式,例如 GBK 等。 FileReader 与 InputStreamReader 涉及编码转换 ( 指定编码方式或者采用 os 默认编码 ) ,可能在不同的平台上出现乱码现象!而 FileInputStream 以二进制方式处理,不会出现乱码现象 . 如果处理纯文本文件,建议使用 FileReader ,因为更方便,也更适合阅读;但是要注意编码问题! 其他情况(处理非纯文本文件),FileInputStream是唯一的选择;FileInputStream是进Socket通讯时会用到很多,如将文件流是Stream的方式传向服务器!    

五、NIO

10、使用 NIO 复制
  1. /**
  2.      * NIO
  3.      * @param sourcePath
  4.      * @param targetPath
  5.      * @throws IOException
  6.      */
  7.     public static void copyFile(String sourcePath, String targetPath) throws IOException {
  8.         FileInputStream in = new FileInputStream(sourcePath);
  9.         FileOutputStream out = new FileOutputStream(targetPath);
  10.         FileChannel inChannel = in.getChannel();
  11.         FileChannel outChannel = out.getChannel();
  12.         ByteBuffer buffer = ByteBuffer.allocate(4 * 1024);
  13.         long startTime = System.currentTimeMillis();
  14.         while (inChannel.read(buffer) != -1) {
  15.             buffer.flip();
  16.             outChannel.write(buffer);
  17.             buffer.clear();
  18.         }
  19.         long endTime = System.currentTimeMillis();
  20.         System.out.println("总共耗时" + (endTime - startTime) + "ms");
  21.     }
  22.     public static void main(String args[]) throws IOException {
  23.         String sourcePath = "/Users/liuyanzhao/Desktop/temp/三国演义.txt";
  24.         String targetPath = "/Users/liuyanzhao/Desktop/temp/三国演义的拷贝.txt";
  25.         copyFile(sourcePath, targetPath);//13ms
  26.     }
IO是阻塞的。NIO是非阻塞的,在多线程的时候效果比较明显。
  • 微信
  • 交流学习,有偿服务
  • weinxin
  • 博客/Java交流群
  • 资源分享,问题解决,技术交流。群号:590480292
  • weinxin
avatar

发表评论

avatar 登录者:匿名
匿名评论,评论回复后会有邮件通知

  

已通过评论:0   待审核评论数:0