6 Methods of Writing Files in JAVA

There are essentially only two methods of manipulating files in Java: character streams and byte streams, and there are many implementation classes for byte streams and character streams, so we can choose various classes when writing files. to fulfill. In this article, we will take stock of these methods, and test their performance by the way, so as to choose the best writing method for us.

Before officially starting, let's understand a few basic concepts: the definition and difference of stream, byte stream and character stream.

0. What is a stream?

"Flow" in Java is an abstract concept and a metaphor, just like water flow, water flow flows from one end to the other, while "water flow" in Java is data, and data will "flow" from one end another side.

According to the directionality of the flow, we can divide the flow into input flow and output flow. When the program needs to read data from the data source, it will open an input flow. On the contrary, write data to a data source destination. Sometimes an output stream will be opened, and the data source can be a file, memory, or network.

1. What is byte stream?

The basic unit of byte stream is byte (Byte), and a byte is usually 8 bits, which is used to process binary (data). The byte stream has two base classes: InputStream (input byte stream) and OutputStream (output byte stream).

The inheritance relationship diagram of commonly used byte streams is shown in the following figure:

                   

Among them, InputStream is used for read operation, and OutputStream is used for write operation.

2. What is a character stream?

The basic unit of character stream is Unicode, the size is two bytes (Byte), it is usually used to process text data. Two base classes of character streams: Reader (input character stream) and Writer (output character stream).

The inheritance relationship diagram of commonly used character streams is shown in the following figure:

                   

3. Stream Classification

Streams can be classified according to different dimensions, such as the direction of the stream, the unit of transmission, or the function of the stream, such as the following.

① Classified by flow direction

  • Output stream: OutputStream and Writer are base classes.
  • Input stream: InputStream and Reader are base classes.

② Classification by transmission data unit

  • Byte stream: OutputStream and InputStream are base classes.
  • Character stream: Writer and Reader are base classes.

③ Classification according to function

  • Byte stream: data can be read and written from or to a specific place (node).
  • Processing stream: It is the connection and encapsulation of an existing stream, and the data is read and written through the function call of the encapsulated stream.

PS : We usually classify streams by the unit of transmitted data.

4. 6 ways to write files

The method of writing files is mainly derived from the subclasses of character stream Writer and output byte stream OutputStream, as shown in the following figure:

                  

The class marked ✅ above is the class used to implement file writing. In addition, JDK 1.7 also provides the Files class to implement various operations on files. Next, let's look at them separately.

Method 1: FileWriter

FileWriter is a member of the "character stream" system and is also the basic class for file writing. It contains 5 constructors, which can pass a specific file location or File object. The second parameter indicates whether to append a file. The default value False means rewriting the file content instead of appending the file content (about how to append files, we will talk about it later).

                      

 The FileWriter class is implemented as follows:

/** 
  * 方法 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); 
    } 
} 

You only need to pass in the specific file path and the content to be written. The calling code is as follows:

public static void main(String[] args) { 
    fileWriterMethod("/Users/mac/Downloads/io_test/write1.txt", "哈喽,Java中文社群."); 
} 

Then we open the written file, and the results are as follows:

                  

  • Regarding resource release: In versions above JDK 7, we only need to use try-with-resource to release resources, such as using try (FileWriter fileWriter = new FileWriter(filepath)) {...} The automatic release of FileWriter resources can be realized.

Method 2: BufferedWriter

BufferedWriter is also a member of the character stream system. Unlike FileWriter, BufferedWriter has its own buffer, so it has higher performance in writing files (the two will be tested below).

Little Knowledge Point: Buffer

A buffer, also known as a cache, is a part of the memory space. That is to say, a certain storage space is reserved in the memory space, and these storage spaces are used to buffer input or output data, and this part of the reserved space is called a buffer.

Advantages of the buffer

Taking the writing of file stream as an example, if we do not use the buffer, then the CPU will interact with the low-speed storage device, that is, the disk for each write operation, and the speed of writing the entire file will be limited by the low-speed storage device (disk ). But if the buffer is used, each write operation will first save the data in the high-speed buffer memory, and when the data in the buffer reaches a certain threshold, the file will be written to the disk at one time. Because the writing speed of the memory is much faster than the writing speed of the disk, when there is a buffer, the writing speed of the file is greatly improved.

After understanding the advantages of the cache area, let's return to the topic of this article. Next, we use BufferedWriter to write files. The implementation code is as follows:

/** 
 * 方法 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); 
    } 
} 

The calling code is similar to method 1, so I won't repeat it here.

Method 3: PrintWriter

PrintWriter is also a member of the character stream system. Although it is called "character print stream", it can also be used to write files. The implementation code is as follows:

/** 
 * 方法 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); 
    } 
} 

It can be seen from the above code that both PrintWriter and BufferedWriter must complete the call based on the FileWriter class.

Method 4: FileOutputStream

The above three examples are about some operations of writing character streams to files, and then we will use byte streams to complete file writing. We will use the getBytes() method that comes with String to convert the string into a binary file first, and then write the file. Its implementation code is as follows:

/** 
 * 方法 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); 
    } 
} 

Method 5: BufferedOutputStream

BufferedOutputStream is a member of the byte stream system. The difference from FileOutputStream is that it has a buffer function, so its performance is better. Its implementation code is as follows:

/** 
 * 方法 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()); 
    } 
} 

Method 6: Files

The following operation method is different from the previous code, and then we will use a new file operation class Files provided in JDK 7 to realize file writing.

The Files class is a new file operation class added by JDK 7. It provides a large number of methods for processing files, such as file copying, reading, writing, obtaining file attributes, and quickly traversing file directories. These methods are very useful It facilitates the operation of the file, and its implementation code is as follows:

/** 
 * 方法 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()); 
}

All of the above methods can realize file writing, so which method has higher performance? Let's test it next.

5. Performance testing

Let's build a relatively large string first, then use the above six methods to test the speed of file writing, and finally print the results. The test code is as follows:

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); 
        } 
    } 
} 

Before viewing the results, let's go to the corresponding folder to check whether the written files are normal, as shown in the following figure:

                   

 It can be seen from the above results that each method normally writes 26 MB of data, and their final execution results are shown in the figure below:

                   

From the above results, it can be seen that the operation speed of the character stream is the fastest. This is because the code we tested this time operates on a string, so when using a byte stream, the string needs to be converted into a byte stream first, so There is no advantage in execution efficiency.

From the above results, it can be seen that the best performance is the string writing stream BufferedWriter with a buffer, and the slowest performance is Files.

PS : The above test results are only valid for the string operation scenario. If the operation is a binary file, then the buffered byte stream BufferedOutputStream should be used.

6. Expand knowledge: content addition

The above code will rewrite the file. If you only want to append content on the original basis, you need to set an append parameter to true when creating the write stream. For example, if we use FileWriter to implement file appending, The implementation code is like this:

public static void fileWriterMethod(String filepath, String content) throws IOException { 
    // 第二个 append 的参数传递一个 true = 追加文件的意思 
    try (FileWriter fileWriter = new FileWriter(filepath, true)) { 
        fileWriter.append(content); 
    } 
} 

If you are using BufferedWriter or PrintWriter, you also need to set an additional parameter of append to true when building a new FileWriter class. The implementation code is as follows:

try (BufferedWriter bufferedWriter = new BufferedWriter( 
    new FileWriter(filepath, true))) { 
    bufferedWriter.write(content); 
} 

In comparison, the Files class is more special in order to implement additional writing of files. It needs to pass an additional parameter of StandardOpenOption.APPEND when calling the write method. Its implementation code is as follows:

Files.write(Paths.get(filepath), content.getBytes(), StandardOpenOption.APPEND); 

7. Summary

In this article, we have shown 6 methods of writing files, which are divided into 3 categories: character stream writing, byte stream writing, and Files class writing. The most convenient operation is the Files class, but its performance is not very good. If performance is required, it is recommended to use a stream with a buffer to complete the operation, such as BufferedWriter or BufferedOutputStream. If the content to be written is a string, it is recommended to use BufferedWriter, and if the content to be written is a binary file, it is recommended to use BufferedOutputStream.

Guess you like

Origin blog.csdn.net/zs319428/article/details/119926133