Java中操作文件的方式本质上只有两种:字符流和字节流,而字节流和字符流的实现类很多,所以我们在写文件的时候可以选择多种方式。类来实现。在这篇文章中,我们将对这些方法进行盘点,顺便测试一下它们的性能,从而选择出最适合我们的写法。在正式开始之前,先了解几个基本概念:流、字节流、字符流的定义和区别。0.什么是流量?Java中的“流”是一个抽象的概念和隐喻,就像水流一样,水流从一端流向另一端,而Java中的“水流”是数据,数据会从一端“流”到另一端。根据流的方向性,我们可以将流分为输入流和输出流。当程序需要从数据源中读取数据时,它会打开一个输入流。相反,将数据写入数据源目的地。有时会打开一个输出流,数据源可以是文件、内存或网络。1、什么是字节流?字节流的基本单位是字节(Byte),一个字节通常为8位,用于处理二进制(数据)。字节流有两个基类:InputStream(输入字节流)和OutputStream(输出字节流)。常用字节流的继承关系图如下图所示:InputStream用于读操作,OutputStream用于写操作。2.什么是字符流?字符流的基本单位是Unicode,大小为两个字节(Byte)。它通常用于处理文本数据。字符流的两个基类:Reader(输入字符流)和Writer(输出字符流)。常用字符流的继承关系图如下图所示:3.流的分类流可以按照不同的维度进行分类,比如流的方向,传输的单位,或者流的功能等分类,比如下面的。①按流向分类输出流:OutputStream和Writer是基类。输入流:InputStream和Reader是基类。②按传输数据单元字节流分类:OutputStream和InputStream是基类。字符流:Writer和Reader是基类。③按功能分类字节流:可以从特定的地方(节点)读写数据。处理流:是对现有流的连接和封装,通过封装流的函数调用来读写数据。PS:我们通常以传输数据为单位对流进行分类。4.六种写文件的方法。写入文件的方法主要派生自字符流Writer和输出字节流OutputStream的子类,如下图所示:上面标有?的类是用来实现文件写入的类。另外,在JDK1.7中还提供了Files类来实现对文件的各种操作。接下来,我们将分别来看它们。方法一:FileWriterFileWriter是“字符流”系统的一员,也是文件写入的基础类。它包含5个构造函数,可以传递特定的文件位置或File对象。第二个参数表示是否追加文件,默认值为false,表示重写文件内容,而不是追加文件内容(关于如何追加文件,后面会讲到)。FileWriter类的实现如下:/***方法一:使用FileWriter写入文件*@paramfilepath文件目录*@paramcontent要写入的内容*@throwsIOException*/publicstaticvoidfileWriterMethod(Stringfilepath,Stringcontent)throwsIOException{try(FileWriterfileWriter=newFileWriter(filepath)){fileWriter.append(content);}}只需要传入具体的文件路径和要写入的内容,调用代码如下:publicstaticvoidmain(String[]args){fileWriterMethod("/Users/mac/Downloads/io_test/write1.txt","你好,Java中文社区。");}然后我们打开写入的文件,结果如下:关于资源释放:在JDK7以上版本中,我们只需要使用try-with-resource方法就可以实现资源的释放,例如使用try(FileWriterfileWriter=newFileWriter(filepath)){...}实现FileWriter资源的自动释放。方法二:BufferedWriterBufferedWriter也是字符流系统的一员。BufferedWriter与FileWriter不同,BufferedWriter有自己的buffer,因此在写入文件时具有更高的性能(下面将对两者进行测试)。小知识点:buffer缓冲区也叫缓存,是内存空间的一部分。也就是说,在内存空间中预留了一定的存储空间,这些存储空间用来缓冲输入或输出的数据,这部分预留的空间称为缓冲区。缓冲区的优点以文件流的写入为例。如果我们不使用buffer,每次写操作时CPU都会和低速存储设备,也就是磁盘进行交互,整个文件的写入速度都会受到低速存储设备的限制(磁盘)。但如果使用缓冲区,每次写操作都会先将数据保存在高速缓冲存储器中,当缓冲区中的数据达到一定阈值时,一次性将文件写入磁盘。因为内存的写入速度比磁盘的写入速度要快很多,所以当有缓冲区的时候,文件的写入速度会大大提高。了解了缓存区的优势,我们再回到本文的主题。接下来,我们使用BufferedWriter来写入文件。实现代码如下:/***方法二:使用BufferedWriter写入文件*@paramfilepathfile目录*@paramcontent为写入内容*@throwsIOException*/publicstaticvoidbufferedWriterMethod(Stringfilepath,Stringcontent)throwsIOException{try(BufferedWriterbufferedWriter=newBufferedWriter(newFileWriter(filepath))){bufferedWriter.write(content);}}调用代码与方法一类似,这里不再赘述。方法三:PrintWriterPrintWriter也是字符流系统的一员。虽然叫“字符打印流”,但也可以用来写文件。实现代码如下:/***方法三:使用PrintWriter写入文件*@paramfilepath文件目录*@paramcontent要写入的内容*@throwsIOException*/publicstaticvoidprintWriterMethod(Stringfilepath,Stringcontent)throwsIOException{try(PrintWriterprintWriter=newPrintWriter(newFileWriter(filepath))){printWriter.print(content);}}从上面的代码可以看出PrintWriter和BufferedWriter都必须基于FileWriter类才能完成调用。方法四:FileOutputStream上面三个例子是关于字符流写入文件的一些操作,接下来我们将使用字节流来完成文件写入。我们会先使用String自带的getBytes()方法将字符串转换成二进制文件,然后写入文件。其实现代码如下:/***方法四:使用FileOutputStream写入文件*@paramfilepath文件目录*@paramcontent要写入的内容*@throwsIOException*/publicstaticvoidfileOutputStreamMethod(Stringfilepath,Stringcontent)throwsIOException{try(FileOutputStreamfileOutputStream=newFileOutputStream(filepath)){byte[]bytes=content.getBytes();fileOutputStream.write(}bytes)5:BufferedOutputStreamBufferedOutputStream是字节流系统的一员。与FileOutputStream不同的是它有缓冲功能,所以性能更好。其实现代码如下:/***方法五:使用BufferedOutputStream写文件*@paramfilepath文件目录*@paramcontent待写入内容*@throwsIOException*/publicstaticvoidbufferedOutputStreamMethod(Stringfilepath,Stringcontent)throwsIOException{try(BufferedOutputStreambufferedOutputStream=newBufferedOutputStream(newFileOutputStream)(filepath))){bufferedOutputStream.write(content.getBytes());}}方法六:Files接下来的操作方法和前面的代码不同。接下来,我们将使用JDK7中提供的一个新的文件操作类Files来实现文件写入。Files类是JDK7新增的文件操作类,它提供了大量处理文件的方法,如文件复制、读取、写入、获取文件属性、快速遍历文件目录等。这些方法非常有用,方便了对文件的操作,其实现代码如下:/***方法六:使用Files写入文件*@paramfilepath文件目录*@paramcontent要写入的内容*@throwsIOException*/publicstaticvoidfilesTest(Stringfilepath,Stringcontent)throwsIOException{文件。write(Paths.get(filepath),content.getBytes());}以上方法都可以实现文件写入,那么哪种方法性能更高呢?接下来我们测试一下。5.性能测试我们先建一个比较大的字符串,然后用上面6种方法测试文件写入速度,最后打印结果。测试代码如下:importjava.io.*;importjava.nio.file.Files;importjava.nio.file.Paths;publicclassWriteExample{publicstaticvoidmain(String[]args)throwsIOException{//构建写入内容StringBuilderstringBuilder=newStringBuilder();for(inti=0;i<1000000;i++){stringBuilder.append("ABCDEFGHIGKLMNOPQRSUEVWXYZ");}//写入内容finalStringcontent=stringBuilder.toString();//文件存放目录finalStringfilepath1="/Users/mac/Downloads/io_test/write1.txt";finalStringfilepath2="/Users/mac/Downloads/io_test/write2.txt";finalStringfilepath3="/Users/mac/Downloads/io_test/write3.txt";finalStringfilepath4="/Users/mac/Downloads/io_test/write4.txt";finalStringfilepath5="/Users/mac/Downloads/io_test/write5.txt";finalStringfilepath6="/Users/mac/Downloads/io_test/write6.txt";//方法1:使用FileWriter写文件longstime1=System.currentTimeMillis();fileWriterTest(filepath1,content);longetime1=System.currentTimeMillis();System.out.println("FileWriter写入时间:"+(etime1-stime1));//方法二:使用BufferedWriter写入文件longstime2=System.currentTimeMillis();bufferedWriterTest(filepath2,content);longetime2=System.currentTimeMillis();System.out.println("BufferedWriter写入时间:"+(etime2-stime2));//方法三:使用PrintWriter写入文件longstime3=System.currentTimeMillis();printWriterTest(filepath3,内容);longetime3=System.currentTimeMillis();System.out.println("PrintWriterTestwritetime:"+(etime3-stime3));//方法四:使用FileOutputStream写入文件longstime4=System.currentTimeMillis();fileOutputStreamTest(filepath4,内容);longetime4=System.currentTimeMillis();System.out.println("FileOutputStream写入时间:"+(etime4-stime4));//方法五:使用BufferedOutputStream写入文件longstime5=System.currentTimeMillis();bufferedOutputStreamTest(filepath5,content);longetime5=System.currentTimeMillis();System.out.println("写入BufferedOutputStream的时间:"+(etime5-stime5));//方法六:使用Files写入文件longstime6=Ssystem.currentTimeMillis();filesTest(filepath6,content);longetime6=System.currentTimeMillis();System.out.println("文件写入时间:"+(etime6-stime6));}/***方法六:使用Files写入文件*@paramfilepath文件目录*@paramcontent要写入的内容*@throwsIOException*/privatestaticvoidfilesTest(Stringfilepath,Stringcontent)throwsIOException{Files.write(Paths.get(filepath),content.getBytes());}/***方法五:使用BufferedOutputStream写入文件*@paramfilepath文件目录*@paramcontent写入内容*@throwsIOException*/privatestaticvoidbufferedOutputStreamTest(Stringfilepath,Stringcontent)throwsIOException{try(BufferedOutputStreambufferedOutputStream){try(BufferedOutputStreambufferedOutputStream)newBufferedOutputStream){StreambufferedOutputStream()write(content.getBytes());}}/***方法四:使用FileOutputStream写入文件*@paramfilepath文件目录*@paramcontent要写入的内容*@throwsIOException*/privatestaticvoidfileOutputStreamTest(Stringfilepath,Stringcontent)throwsIOException{try(文件输出流文件输出流am=newFileOutputStream(filepath)){byte[]bytes=content.getBytes();fileOutputStream.write(bytes);}}/***方法三:使用PrintWriter写入文件*@paramfilepath文件目录*@paramcontent要写入的内容*@throwsIOException*/privatestaticvoidprintWriterTest(Stringfilepath,Stringcontent)throwsIOException{try(PrintWriterprintWriter=newPrintWriter(newFileWriter(filepath))){printWriter.print(content);}}/***方法二:使用BufferedWriter写入文件*@paramfilepath文件目录*@paramcontent待写入内容*@throwsIOException*/privatestaticvoidbufferedWriterTest(Stringfilepath,Stringcontent)throwsIOException{try(BufferedWriterbufferedWriter=newBufferedWriter(newFileWriter(filepath))){bufferedWriter.write(content);}}/***方法一:使用FileWriter写入文件*@paramfilepath文件目录*@paramcontentto-be-writtencontent*@throwsIOException*/privatestaticvoidfileWriterTest(Stringfilepath,Stringcontent)throwsIOException{try(FileWriterfileWriter=newFileWriter(filepath)){fileWriter.append(content);}}}在检查结果之前,让我们开始吧o对应文件夹查看写入的文件是否正常,如下图:从上面的结果可以看出,每个方法都正常写入了26MB的数据,它们最终的执行结果如图下图:从上面的结果可以看出,字符流的运行速度是最快的Fast,这是因为我们这次测试的代码是对字符串进行操作的,所以在使用字节流的时候,我们需要将字符串转为字节首先流,因此,它在执行效率上并不具有优势。从上面的结果可以看出,性能最好的是带缓冲区的字符串写入流BufferedWriter,性能最慢的是Files。PS:以上测试结果仅对字符串操作场景有效。如果是操作二进制文件,应该使用缓冲字节流BufferedOutputStream。6.知识扩展:在上面的代码中添加内容,会重写文件。如果只想在原有基础上增加内容,需要在创建写流时设置一个append参数为true。比如我们使用FileWriter来实现文件的添加,实现代码如下:publicstaticvoidfileWriterMethod(Stringfilepath,Stringcontent)throwsIOException{//第二个append参数传递一个true=追加文件的意思try(FileWriterfileWriter=newFileWriter(filepath,true)){fileWriter.append(content);}}如果您正在使用BufferedWriter或PrintWriter,您还需要在构建新的FileWriter类时将附加参数设置为true。实现代码如下:try(BufferedWriterbufferedWriter=newBufferedWriter(newFileWriter(filepath,true))){bufferedWriter.write(content);}相对于Files类,它更特殊的是实现文件的附加写入。调用write方法时需要额外传递一个参数StandardOpenOption.APPEND。其实现代码如下:Files.write(Paths.get(filepath),content.getBytes(),StandardOpenOption.APPEND);7.小结在本文中,我们展示了6种写文件的方法,分为3类:字符流写法、字节流写法和Files类写法。最方便的是Files类,但它的性能不是很好。如果对性能有要求,建议使用带有缓冲区的流来完成操作,比如BufferedWriter或者BufferedOutputStream。如果写入的内容是字符串,建议使用BufferedWriter,如果写入的内容是二进制文件,建议使用BufferedOutputStream。参考与致谢https://www.cnblogs.com/absfree/p/5415092.html
