如何查网站建设时间,工信部网站备案管理系统,不良网站进入窗口软件下载7,电子商务网官方网站作者 | 王磊来源 | Java中文社群#xff08;ID#xff1a;javacn666#xff09;转载请联系授权#xff08;微信ID#xff1a;GG_Stone#xff09;在 Java 中操作文件的方法本质上只有两种#xff1a;字符流和字节流#xff0c;而字节流和字符流的实现类又有很多#x… 作者 | 王磊来源 | Java中文社群IDjavacn666转载请联系授权微信IDGG_Stone在 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.写文件的6种方法写入文件的方法主要源于字符流 Writer 和输出字节流 OutputStream 的子类如下图所示以上标注✅号的类就是用来实现文件写入的类除此之外在 JDK 1.7 中还提供了 Files 类用来实现对文件的各种操作接下来我们分别来看。方法 1FileWriterFileWriter 属于「字符流」体系中的一员也是文件写入的基础类它包含 5 个构造函数可以传递一个具体的文件位置或者 File 对象第二参数表示是否要追加文件默认值为 false 表示重写文件内容而非追加文件内容关于如何追加文件我们后面会讲。FileWriter 类的实现如下/*** 方法 1使用 FileWriter 写文件* param filepath 文件目录* param content 待写入内容* throws IOException*/
public static void fileWriterMethod(String filepath, String content) throws IOException {try (FileWriter fileWriter new FileWriter(filepath)) {fileWriter.append(content);}
}
只需要传入具体的文件路径和待写入的内容即可调用代码如下public static void main(String[] args) {fileWriterMethod(/Users/mac/Downloads/io_test/write1.txt, 哈喽,Java中文社群.);
}
然后我们打开写入的文件实现结果如下关于资源释放的问题在 JDK 7 以上的版本我们只需要使用 try-with-resource 的方式就可以实现资源的释放就比如使用 try (FileWriter fileWriter new FileWriter(filepath)) {...} 就可以实现 FileWriter 资源的自动释放。方法 2BufferedWriterBufferedWriter 也属于字符流体系的一员与 FileWriter 不同的是 BufferedWriter 自带缓冲区因此它写入文件的性能更高下文会对二者进行测试。小知识点缓冲区缓冲区又称为缓存它是内存空间的一部分。也就是说在内存空间中预留了一定的存储空间这些存储空间用来缓冲输入或输出的数据这部分预留的空间就叫做缓冲区。缓冲区的优势以文件流的写入为例如果我们不使用缓冲区那么每次写操作 CPU 都会和低速存储设备也就是磁盘进行交互那么整个写入文件的速度就会受制于低速的存储设备磁盘。但如果使用缓冲区的话每次写操作会先将数据保存在高速缓冲区内存上当缓冲区的数据到达某个阈值之后再将文件一次性写入到磁盘上。因为内存的写入速度远远大于磁盘的写入速度所以当有了缓冲区之后文件的写入速度就被大大提升了。了解了缓存区的优点之后咱们回到本文的主题接下来我们用 BufferedWriter 来文件的写入实现代码如下/*** 方法 2使用 BufferedWriter 写文件* param filepath 文件目录* param content 待写入内容* throws IOException*/
public static void bufferedWriterMethod(String filepath, String content) throws IOException {try (BufferedWriter bufferedWriter new BufferedWriter(new FileWriter(filepath))) {bufferedWriter.write(content);}
}
调用代码和方法 1 类似这里就不再赘述了。方法 3PrintWriterPrintWriter 也属于字符流体系中的一员它虽然叫“字符打印流”但使用它也可以实现文件的写入实现代码如下/*** 方法 3使用 PrintWriter 写文件* param filepath 文件目录* param content 待写入内容* throws IOException*/
public static void printWriterMethod(String filepath, String content) throws IOException {try (PrintWriter printWriter new PrintWriter(new FileWriter(filepath))) {printWriter.print(content);}
}
从上述代码可以看出无论是 PrintWriter 还是 BufferedWriter 都必须基于 FileWriter 类来完成调用。方法 4FileOutputStream上面 3 个示例是关于字符流写入文件的一些操作而接下来我们将使用字节流来完成文件写入。我们将使用 String 自带的 getBytes() 方法先将字符串转换成二进制文件然后再进行文件写入它的实现代码如下/*** 方法 4使用 FileOutputStream 写文件* param filepath 文件目录* param content 待写入内容* throws IOException*/
public static void fileOutputStreamMethod(String filepath, String content) throws IOException {try (FileOutputStream fileOutputStream new FileOutputStream(filepath)) {byte[] bytes content.getBytes();fileOutputStream.write(bytes);}
}
方法 5BufferedOutputStreamBufferedOutputStream 属于字节流体系中的一员与 FileOutputStream 不同的是它自带了缓冲区的功能因此性能更好它的实现代码如下/*** 方法 5使用 BufferedOutputStream 写文件* param filepath 文件目录* param content 待写入内容* throws IOException*/
public static void bufferedOutputStreamMethod(String filepath, String content) throws IOException {try (BufferedOutputStream bufferedOutputStream new BufferedOutputStream(new FileOutputStream(filepath))) {bufferedOutputStream.write(content.getBytes());}
}
方法 6Files接下来的操作方法和之前的代码都不同接下来咱们就使用 JDK 7 中提供的一个新的文件操作类 Files 来实现文件的写入。Files 类是 JDK 7 添加的新的操作文件的类它提供了提供了大量处理文件的方法例如文件复制、读取、写入获取文件属性、快捷遍历文件目录等这些方法极大的方便了文件的操作它的实现代码如下/*** 方法 6使用 Files 写文件* param filepath 文件目录* param content 待写入内容* throws IOException*/
public static void filesTest(String filepath, String content) throws IOException {Files.write(Paths.get(filepath), content.getBytes());
}
以上这些方法都可以实现文件的写入那哪一种方法性能更高呢接下来我们来测试一下。5.性能测试我们先来构建一个比较大的字符串然后分别用以上 6 种方法来测试文件写入的速度最后再把结果打印出来测试代码如下import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;public class WriteExample {public static void main(String[] args) throws IOException {// 构建写入内容StringBuilder stringBuilder new StringBuilder();for (int i 0; i 1000000; i) {stringBuilder.append(ABCDEFGHIGKLMNOPQRSEUVWXYZ);}// 写入内容final String content stringBuilder.toString();// 存放文件的目录final String filepath1 /Users/mac/Downloads/io_test/write1.txt;final String filepath2 /Users/mac/Downloads/io_test/write2.txt;final String filepath3 /Users/mac/Downloads/io_test/write3.txt;final String filepath4 /Users/mac/Downloads/io_test/write4.txt;final String filepath5 /Users/mac/Downloads/io_test/write5.txt;final String filepath6 /Users/mac/Downloads/io_test/write6.txt;// 方法一:使用 FileWriter 写文件long stime1 System.currentTimeMillis();fileWriterTest(filepath1, content);long etime1 System.currentTimeMillis();System.out.println(FileWriter 写入用时: (etime1 - stime1));// 方法二:使用 BufferedWriter 写文件long stime2 System.currentTimeMillis();bufferedWriterTest(filepath2, content);long etime2 System.currentTimeMillis();System.out.println(BufferedWriter 写入用时: (etime2 - stime2));// 方法三:使用 PrintWriter 写文件long stime3 System.currentTimeMillis();printWriterTest(filepath3, content);long etime3 System.currentTimeMillis();System.out.println(PrintWriterTest 写入用时: (etime3 - stime3));// 方法四:使用 FileOutputStream 写文件long stime4 System.currentTimeMillis();fileOutputStreamTest(filepath4, content);long etime4 System.currentTimeMillis();System.out.println(FileOutputStream 写入用时: (etime4 - stime4));// 方法五:使用 BufferedOutputStream 写文件long stime5 System.currentTimeMillis();bufferedOutputStreamTest(filepath5, content);long etime5 System.currentTimeMillis();System.out.println(BufferedOutputStream 写入用时: (etime5 - stime5));// 方法六:使用 Files 写文件long stime6 System.currentTimeMillis();filesTest(filepath6, content);long etime6 System.currentTimeMillis();System.out.println(Files 写入用时: (etime6 - stime6));}/*** 方法六:使用 Files 写文件* param filepath 文件目录* param content 待写入内容* throws IOException*/private static void filesTest(String filepath, String content) throws IOException {Files.write(Paths.get(filepath), content.getBytes());}/*** 方法五:使用 BufferedOutputStream 写文件* param filepath 文件目录* param content 待写入内容* throws IOException*/private static void bufferedOutputStreamTest(String filepath, String content) throws IOException {try (BufferedOutputStream bufferedOutputStream new BufferedOutputStream(new FileOutputStream(filepath))) {bufferedOutputStream.write(content.getBytes());}}/*** 方法四:使用 FileOutputStream 写文件* param filepath 文件目录* param content 待写入内容* throws IOException*/private static void fileOutputStreamTest(String filepath, String content) throws IOException {try (FileOutputStream fileOutputStream new FileOutputStream(filepath)) {byte[] bytes content.getBytes();fileOutputStream.write(bytes);}}/*** 方法三:使用 PrintWriter 写文件* param filepath 文件目录* param content 待写入内容* throws IOException*/private static void printWriterTest(String filepath, String content) throws IOException {try (PrintWriter printWriter new PrintWriter(new FileWriter(filepath))) {printWriter.print(content);}}/*** 方法二:使用 BufferedWriter 写文件* param filepath 文件目录* param content 待写入内容* throws IOException*/private static void bufferedWriterTest(String filepath, String content) throws IOException {try (BufferedWriter bufferedWriter new BufferedWriter(new FileWriter(filepath))) {bufferedWriter.write(content);}}/*** 方法一:使用 FileWriter 写文件* param filepath 文件目录* param content 待写入内容* throws IOException*/private static void fileWriterTest(String filepath, String content) throws IOException {try (FileWriter fileWriter new FileWriter(filepath)) {fileWriter.append(content);}}
}
在查看结果之前我们先去对应的文件夹看看写入的文件是否正常如下图所示从上述结果可以看出每种方法都正常写入了 26 MB 的数据它们最终执行的结果如下图所示从以上结果可以看出字符流的操作速度最快这是因为我们本次测试的代码操作的是字符串所以在使用字节流时需要先将字符串转换为字节流因此在执行效率上不占优势。从上述结果可以看出性能最好的是带有缓冲区的字符串写入流 BufferedWriter性能最慢的是 Files。PS以上的测试结果只是针对字符串的操作场景有效如果操作的是二进制的文件那么就应该使用带缓冲区的字节流 BufferedOutputStream。6.扩展知识内容追加以上代码会对文件进行重写如果只想在原有的基础上追加内容就需要在创建写入流的时候多设置一个 append 的参数为 true比如如果我们使用 FileWriter 来实现文件的追加的话实现代码是这样的public static void fileWriterMethod(String filepath, String content) throws IOException {// 第二个 append 的参数传递一个 true 追加文件的意思try (FileWriter fileWriter new FileWriter(filepath, true)) {fileWriter.append(content);}
}
如果使用的是 BufferedWriter 或 PrintWriter也是需要在构建 new FileWriter 类时多设置一个 append 的参数为 true实现代码如下try (BufferedWriter bufferedWriter new BufferedWriter(new FileWriter(filepath, true))) {bufferedWriter.write(content);
}
相比来说 Files 类要想实现文件的追加写法更加特殊一些它需要在调用 write 方法时多传一个 StandardOpenOption.APPEND 的参数它的实现代码如下Files.write(Paths.get(filepath), content.getBytes(), StandardOpenOption.APPEND);
7.总结本文我们展示了 6 种写入文件的方法这 6 种方法总共分为 3 类字符流写入、字节流写入和 Files 类写入。其中操作最便利的是 Files 类但它的性能不怎么好。如果对性能有要求就推荐使用带有缓存区的流来完成操作如 BufferedWriter 或 BufferedOutputStream。如果写入的内容是字符串的话那么推荐使用 BufferedWriter如果写入的内容是二进制文件的话就推荐使用 BufferedOutputStream。参考 鸣谢https://www.cnblogs.com/absfree/p/5415092.html
往期推荐
线程池的7种创建方式强烈推荐你用它...求求你别再用wait和notify了硬核Redis总结看这篇就够了关注我每天陪你进步一点点