По сути, в Java есть только два способа манипулировать файлами:поток символов и поток байтов, и существует множество классов реализации для потока байтов и потока символов, поэтому мы можем выбирать различные классы для реализации при записи файлов. В этой статье мы рассмотрим эти методы и проверим их производительность, чтобы выбрать лучший метод записи для нас.
Перед официальным началом давайте сначала разберемся с несколькими основными понятиями: определение и различие между потоком, потоком байтов и потоком символов.
0. Что такое поток?
«Поток» в Java — это абстрактное понятие и метафора, точно так же, как поток воды, поток воды течет от одного конца к другому, тогда как «поток» в Java — это данные, а данные «текут» из одного конца в другой. конец к другой другой стороне.
В зависимости от направленности потока поток можно разделить навходной поток и выходной поток, когда программе нужно прочитать данные из источника данных, она откроет входной поток.Наоборот, при записи данных в место назначения источника данных, она также откроет выходной поток.Источником данных может быть файл, память или сети. Подождите.
1. Что такое поток байтов?
Базовой единицей потока байтов является байт, а байт обычно состоит из 8 битов, которые используются для обработки двоичных файлов (данных). Потоки байтов имеют два базовых класса:InputStream
(Входной поток байтов) иOutputStream
(выходной байтовый поток).
Диаграмма отношений наследования общих потоков байтов показана на следующем рисунке:в
InputStream
для операций чтения, аOutputStream
для операций записи.
2. Что такое поток символов?
Базовой единицей символьного потока является Unicode, размер которого составляет два байта (Byte), который обычно используется для обработки текстовых данных. Два базовых класса символьного потока:Reader
(входной поток символов) иWriter
(выходной поток символов).
Диаграмма отношений наследования общих потоков символов показана на следующем рисунке:
3. Классификация потоков
Потоки можно классифицировать по разным параметрам, таким как направление потока, единица передачи и функция потока, например следующие.
① Классификация по направлению потока
- выходной поток:
OutputStream
иWriter
является базовым классом. - Входной поток:
InputStream
иReader
для базового класса.
② Классификация по единице передаваемых данных
- байтовый поток:
OutputStream
иInputStream
для базового класса. - поток символов:
Writer
иReader
является базовым классом.
③ Классификация по функциям
- Поток байтов: данные могут быть прочитаны и записаны из или в определенное место (узел).
- Процесс Flow: это пакет и подключен к существующему потоку, поток через вызов функции инкапсулирован для чтения и записи данных.
PS: Мы обычно единицы передачи данных для классификации потока.
4. 6 способов записи файлов
Метод записи в файл в основном основан на потоке символов.Writer
и выходной поток байтовOutputStream
Подкласс , как показано ниже:
Классы, отмеченные ✅ выше, являются классами, используемыми для записи файлов.Кроме того, JDK 1.7 также предоставляет
Files
Классы используются для реализации различных операций над файлами, рассмотрим их отдельно.
Способ 1: FileWriter
FileWriter
Он является членом системы "потока символов", а также является базовым классом для записи файлов. Он содержит 5 конструкторов, которые могут передавать определенное местоположение файла илиFile
Object, второй параметр указывает, добавлять ли файл, значение по умолчанию равно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中文社群.");
}
Затем открываем записанный файл и добиваемся следующих результатов:
Вопросы по высвобождению ресурсов: более 7 версии JDK, нам нужно только использовать метод try-with-resource для высвобождения ресурсов, например, вы используете try (FileWriter fileWriter = new FileWriter (filepath)) {.. .} Ресурсы FileWriter могут быть автоматически освобождены.
Метод 2: BufferedWriter
BufferedWriter
Он также является членом системы потока символов иFileWriter
разница в томBufferedWriter
собственный буфер, поэтому более производительно записывать в файл (ниже мы проверим оба варианта).
Маленькая точка знаний: буфер
Буфер, также известный как кеш, является частью пространства памяти. То есть в области памяти зарезервировано определенное количество места для хранения, и эти места для хранения используются для буферизации входных или выходных данных, и эта часть зарезервированного пространства называется буфером.
Преимущества буферовВзяв за пример запись файловых потоков, если мы не используем буферы, ЦП будет взаимодействовать с низкоскоростным запоминающим устройством, то есть диском для каждой операции записи, и скорость записи всего файла будет ограничена на низкоскоростном запоминающем устройстве (диске). Однако, если используется буфер, каждая операция записи будет сначала сохранять данные в высокоскоростной буферной памяти, и когда данные в буфере достигнут определенного порога, файл будет записан на диск за один раз. Поскольку скорость записи в память намного выше, чем скорость записи на диск, при наличии буфера скорость записи файла значительно повышается.
Разобравшись с преимуществами области кэша, вернемся к теме этой статьи, а затем воспользуемся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, поэтому я не буду повторять его здесь.
Способ 3: PrintWriter
PrintWriter
Это также член в системе потока символов, которая называется «поток печати символов», но он также может быть записан в файл, а код выглядит следующим образом:
/**
* 方法 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
класс, чтобы завершить вызов.
Способ 4: FileOutputStream
Приведенные выше 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);
}
}
Метод 5: BufferedOutputStream
BufferedOutputStream
Принадлежит члену системы потока байтов, и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());
}
}
Способ 6: файлы
Следующий метод операции отличается от предыдущего кода.Далее мы будем использовать новый класс операций с файлами, предоставленный в 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 МБ данных, а окончательные результаты их выполнения показаны на следующем рисунке:
Как видно из приведенных выше результатов, скорость работы потока символов самая быстрая, потому что мы этот тестовый код представляет собой строку операций, поэтому, когда поток байтов, необходимо сначала преобразовать строку в поток байтов, поэтому в реализации эффективность не является доминирующей.
Из приведенных выше результатов можно увидеть, чтоНаилучшая производительность — это поток записи строки с буфером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 способов записи в файлы, которые сгруппированы в 3 категории: запись потока символов, запись потока байтов и запись в поток байтов.Files
класс пиши. Самый удобный из нихFiles
класс, но его производительность не очень хорошая. Если требуется производительность, рекомендуется использовать поток с буфером для завершения операции, напримерBufferedWriter
илиBufferedOutputStream
. Если письменный контент представляет собой строку, рекомендуется использоватьBufferedWriter
, если письменный контент представляет собой двоичный файл, рекомендуется использоватьBufferedOutputStream
.
Ссылки и благодарности
ООО, ООО на .cn blog .com / abs бесплатно / fear / 5 ...
Подпишитесь на официальный аккаунт «Java Chinese Community», чтобы узнать больше о галантерейных товарах.