Оригинальная ссылка:blog.CSDN.net/wood\_wind/AR…?
Поток ввода/вывода Java
- предисловие
- 1 Введение в Java IO
- 2 объекта потока ввода-вывода
- 3 метода потока ввода-вывода
- 4 Дополнительный контент
предисловие
Кто-то однажды спросил автора fastjson (высокоскоростная железная дорога, технический эксперт Alibaba): «Вы разработали fastjson, но не получили особой пользы. Вместо этого вас ругали и обвиняли. Почему вы занимаетесь такими вещами? "
Скоростная железная дорога ответила: «Потому что сама любовь — это награда!»
Этот ответ поразил меня сразу. Подумайте о себе, почему бы и нет. Писать — мучительный процесс, а писать сердцем еще мучительнее, требует тщательного обдумывания каждого слова и неоднократных вычеркиваний и правок для достижения успеха. Однако, когда перед вами преподносится отличная статья, боль и страдания того стоят. Если этим сообщениям в блогах посчастливится быть прочитанными и признанными всеми, это будет еще более обнадеживающим. Радость технарей в том, что это может быть так чисто и просто.
Не заблудитесь, обратите внимание и не заблудитесь, одним кликом три раза подряд удачи!
Потоки ввода-вывода — важная часть Java, и мы часто с ними имеем дело. Этот пост в блоге о Java IO полон галантереи, и его можно назвать тройкой лучших во всей сети (пожалуйста, слегка распылите!)
Следующие вопросы (вопросы будут добавляться), если вы сможете хорошо на них ответить, то поздравляю, вы хорошо разбираетесь в знаниях IO, и можете сразу закрывать статью. Вместо этого вы можете найти ответ в следующей статье.
- Каковы характеристики потоков ввода-вывода Java?
- Какие существуют типы потоков ввода/вывода Java?
- Какова связь и разница между потоком байтов и потоком символов?
- Использует ли поток символов буферизацию?
- Обязательно ли буферизованные потоки эффективны? Почему?
- Какие идеи шаблонов проектирования в Java воплощает буферизованный поток?
- Зачем реализовывать сериализацию Как реализовать сериализацию?
- После сериализации данных при повторном изменении файла класса возникнет проблема со чтением данных, как ее решить?
1 Введение в Java IO
ИО, т.е.in
а такжеout
, то есть ввод и вывод, относится к передаче данных между приложениями и внешними устройствами.Обычные внешние устройства включают файлы, конвейеры и сетевые подключения.
В Java ввод-вывод обрабатывается через потоки.Итак, что такое поток?
поток(Stream
) — это абстрактное понятие, относящееся к ряду данных (символов или байтов), которые являются каналом для отправки информации в порядке поступления.
Когда программе необходимо считать данные, она открывает поток к источнику данных, который может быть файлом, памятью или сетевым подключением. Точно так же, когда программе нужно записать данные, она запускает поток к месту назначения. В этот момент вы можете себе представить, что данные как бы «текут» в нем.
Вообще говоря, характеристики потоков следующие:
- Первый пришел, первый вышел: первые данные, записанные в выходной поток, сначала считываются входным потоком.
- Последовательный доступ: Вы можете записать строку байтов в поток один за другим и прочитать строку байтов в том порядке, в котором они были записаны.Вы не можете получить доступ к промежуточным данным случайным образом. (
RandomAccessFile
Кроме) - Только для чтения или только для записи: каждый поток может быть только одним из потоков ввода или потока вывода и не может выполнять обе функции одновременно.Поток ввода можно только читать, а поток вывода можно только записывать. В канале передачи данных, если вы хотите записывать данные и читать данные, вам необходимо предоставить два потока соответственно.
1.1 Классификация потоков ввода-вывода
Основные методы классификации IO-потоков следующие:
- По направлению потока данных: входной поток, выходной поток
- По единице обработки данных: поток байтов, поток символов
- По функции: поток узла, поток процесса
1. Входной поток и выходной поток
Ввод и вывод относятся к приложению, например, чтение и запись файла, чтение файла — это входной поток, а запись файла — выходной поток, который легко спутать.
2. Поток байтов и поток символов
Использование потока байтов и потока символов почти одинаково, разница в том, что единица данных, обрабатываемая потоком байтов и потоком символов, отличается.Единицей работы потока байтов является байт, единица данных которого составляет 8 бит, а поток символов работает с данными. Единицей измерения является 16-битный символ.
Зачем нужен поток персонажей?
Символы в Java используют стандарт Unicode.В кодировке Unicode один английский — это один байт, а один китайский — два байта.
В кодировке UTF-8 китайский символ занимает 3 байта. Например, на рисунке ниже 5 китайских иероглифов «Юньшэнь не знаю где» соответствуют 15 байтам: -28-70-111-26-73-79-28-72-115-25-97-91- 27-92 -124
Затем возникает проблема.Если вы используете поток байтов для обработки китайского языка, если вы читаете и записываете количество байтов, соответствующих одному символу за раз, проблем не будет.Как только байты, соответствующие одному символу, будут разделены, будет искаженные символы. Для более удобной работы с этими китайскими иероглифами в Java был введен поток символов.
Другие различия между потоком байтов и потоком символов:
- Потоки байтов обычно используются для обработки файлов изображений, видео, аудио, PPT, Word и других типов. Потоки символов обычно используются для обработки простых текстовых файлов, таких как файлы TXT, но не могут обрабатывать нетекстовые файлы, такие как изображения и видео. В одном предложении: байтовые потоки могут обрабатывать все файлы, а символьные потоки могут обрабатывать только текстовые файлы.
- Сам поток байтов не имеет буфера По сравнению с потоком байтов буферизованный поток байтов имеет очень высокую эффективность. Сам поток символов имеет буфер, и повышение эффективности буферизованного потока символов по сравнению с потоком символов не так велико. Подробности смотрите в сравнении эффективности в конце статьи.
Взяв в качестве примера запись файла, мы смотрим на исходный код символьного потока и обнаруживаем, что буфер действительно используется:
3. Поток узла и поток обработки
узел потока: Потоковые классы, которые напрямую управляют чтением и записью данных, напримерFileInputStream
поток процесса: связывает и инкапсулирует существующий поток, а также предоставляет мощные и гибкие функции чтения и записи для программы путем обработки данных, таких какBufferedInputStream
(буферизованный поток байтов)
Потоки обработки и потоки узлов применяют шаблон проектирования декоратора Java.
На следующем рисунке наглядно изображен поток узла и поток обработки.Поток обработки является инкапсуляцией потока узла, а окончательная обработка данных завершается потоком узла.
Среди множества потоков обработки один очень важен, т.буферизованный поток.
Мы знаем, что взаимодействие между программой и диском очень медленное по сравнению с работой с памятью, и это легко может стать узким местом программы. Сокращение взаимодействия между программой и диском является эффективным средством повышения эффективности программы. Буферизованные потоки реализуют эту идею: обычные потоки читают и записывают по одному байту за раз, в то время как буферизованные потоки создают буфер в памяти, и в буфере хранится достаточно данных для обработки перед взаимодействием с памятью или диском. Таким образом, при условии, что общий объем данных остается неизменным, количество взаимодействий уменьшается за счет увеличения объема данных для каждого взаимодействия.
Подумайте о примере из жизни: когда мы перемещаем кирпичи, определенно неэффективно загружать машину по частям. Мы можем использовать тележку, сначала загрузить кирпичи на тележку, а затем подтолкнуть тележку к передней части тележки, чтобы загрузить кирпичи на тележку. В этом примере тележку можно рассматривать как буферную зону.Наличие тележки сокращает количество раз загрузки и повышает эффективность.
Следует отметить, что эффективность буферного потока должна быть высокой? Не обязательно. В некоторых случаях эффективность потока буфера ниже. Подробнее см. в разделе Сравнение эффективности потока ввода-вывода.
Полная схема классификации IO выглядит следующим образом:
1.2 Практический пример
Далее давайте посмотрим, как использовать Java IO.
Примеры чтения и написания текста, о чем говорилось в начале статьи, будет "Панасоник попросит мальчика, учитель пойдет собирать лекарства. Только в этой горе Юншен не знает где". файл и вывести его на консоль.
1. FileInputStream, FileOutputStream (байтовый поток)
Метод байтового потока менее эффективен и не рекомендуется.
public class IOTest {
public static void main(String[] args) throws IOException {
File file = new File("D:/test.txt");
write(file);
System.out.println(read(file));
}
public static void write(File file) throws IOException {
OutputStream os = new FileOutputStream(file, true);
// 要写入的字符串
String string = "松下问童子,言师采药去。只在此山中,云深不知处。";
// 写入文件
os.write(string.getBytes());
// 关闭流
os.close();
}
public static String read(File file) throws IOException {
InputStream in = new FileInputStream(file);
// 一次性取多少个字节
byte[] bytes = new byte[1024];
// 用来接收读取的字节数组
StringBuilder sb = new StringBuilder();
// 读取到的字节数组长度,为-1时表示没有数据
int length = 0;
// 循环取数据
while ((length = in.read(bytes)) != -1) {
// 将读取的内容转换成字符串
sb.append(new String(bytes, 0, length));
}
// 关闭流
in.close();
return sb.toString();
}
}
123456789101112131415161718192021222324252627282930313233343536373839
2. BufferedInputStream, BufferedOutputStream (буферизованный поток байтов)
Буферизованный поток байтов рассчитан на высокую эффективность, а реальные операции чтения и записи по-прежнему зависят от
FileOutputStream
а такжеFileInputStream
, поэтому неудивительно, что параметры его конструктора являются объектами этих двух классов.
public class IOTest {
public static void write(File file) throws IOException {
// 缓冲字节流,提高了效率
BufferedOutputStream bis = new BufferedOutputStream(new FileOutputStream(file, true));
// 要写入的字符串
String string = "松下问童子,言师采药去。只在此山中,云深不知处。";
// 写入文件
bis.write(string.getBytes());
// 关闭流
bis.close();
}
public static String read(File file) throws IOException {
BufferedInputStream fis = new BufferedInputStream(new FileInputStream(file));
// 一次性取多少个字节
byte[] bytes = new byte[1024];
// 用来接收读取的字节数组
StringBuilder sb = new StringBuilder();
// 读取到的字节数组长度,为-1时表示没有数据
int length = 0;
// 循环取数据
while ((length = fis.read(bytes)) != -1) {
// 将读取的内容转换成字符串
sb.append(new String(bytes, 0, length));
}
// 关闭流
fis.close();
return sb.toString();
}
}
12345678910111213141516171819202122232425262728293031323334
3. InputStreamReader, OutputStreamWriter (поток символов)
Потоки символов подходят для чтения и записи текстовых файлов.,
OutputStreamWriter
класс на самом делеFileOutputStream
Класс реализован, поэтому его конструкторFileOutputStream
Объект
public class IOTest {
public static void write(File file) throws IOException {
// OutputStreamWriter可以显示指定字符集,否则使用默认字符集
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(file, true), "UTF-8");
// 要写入的字符串
String string = "松下问童子,言师采药去。只在此山中,云深不知处。";
osw.write(string);
osw.close();
}
public static String read(File file) throws IOException {
InputStreamReader isr = new InputStreamReader(new FileInputStream(file), "UTF-8");
// 字符数组:一次读取多少个字符
char[] chars = new char[1024];
// 每次读取的字符数组先append到StringBuilder中
StringBuilder sb = new StringBuilder();
// 读取到的字符数组长度,为-1时表示没有数据
int length;
// 循环取数据
while ((length = isr.read(chars)) != -1) {
// 将读取的内容转换成字符串
sb.append(chars, 0, length);
}
// 关闭流
isr.close();
return sb.toString()
}
}
12345678910111213141516171819202122232425262728293031
4. Класс удобства потока символов
Java предоставляет
FileWriter
а такжеFileReader
Упрощенное чтение и запись потоков символов,new FileWriter
Эквивалентноnew OutputStreamWriter(new FileOutputStream(file, true))
public class IOTest {
public static void write(File file) throws IOException {
FileWriter fw = new FileWriter(file, true);
// 要写入的字符串
String string = "松下问童子,言师采药去。只在此山中,云深不知处。";
fw.write(string);
fw.close();
}
public static String read(File file) throws IOException {
FileReader fr = new FileReader(file);
// 一次性取多少个字节
char[] chars = new char[1024];
// 用来接收读取的字节数组
StringBuilder sb = new StringBuilder();
// 读取到的字节数组长度,为-1时表示没有数据
int length;
// 循环取数据
while ((length = fr.read(chars)) != -1) {
// 将读取的内容转换成字符串
sb.append(chars, 0, length);
}
// 关闭流
fr.close();
return sb.toString();
}
}
123456789101112131415161718192021222324252627282930
5. BufferedReader, BufferedWriter (буферный поток символов)
public class IOTest {
public static void write(File file) throws IOException {
// BufferedWriter fw = new BufferedWriter(new OutputStreamWriter(new
// FileOutputStream(file, true), "UTF-8"));
// FileWriter可以大幅度简化代码
BufferedWriter bw = new BufferedWriter(new FileWriter(file, true));
// 要写入的字符串
String string = "松下问童子,言师采药去。只在此山中,云深不知处。";
bw.write(string);
bw.close();
}
public static String read(File file) throws IOException {
BufferedReader br = new BufferedReader(new FileReader(file));
// 用来接收读取的字节数组
StringBuilder sb = new StringBuilder();
// 按行读数据
String line;
// 循环取数据
while ((line = br.readLine()) != null) {
// 将读取的内容转换成字符串
sb.append(line);
}
// 关闭流
br.close();
return sb.toString();
}
}
1234567891011121314151617181920212223242526272829303132
2 объекта потока ввода-вывода
В первом разделе у нас есть общее представление о IO и выполнено несколько кейсов, но нам все еще не хватает более детального понимания IO, затем мы подробно разберем Java IO и разберем полную систему знаний.
Java предоставляет более 40 классов, нам нужно только подробно понять наиболее важные из них, чтобы соответствовать ежедневному приложению.
2.1 Класс файла
File
Класс — это класс, используемый для манипулирования файлами, но он не может манипулировать данными в файле.
public class File extends Object implements Serializable, Comparable<File>
1
File
класс реализуетSerializable
,Comparable<File>
, что указывает на то, что он поддерживает сериализацию и сортировку.
Конструктор класса File
имя метода
иллюстрировать
File(File parent, String child)
Создает новый экземпляр File из строк родительского абстрактного пути и дочернего пути.
File(String pathname)
Создает новый экземпляр File путем преобразования заданной строки пути в абстрактный путь.
File(String parent, String child)
Создает новый экземпляр File из строки родительского пути и строки дочернего пути.
File(URI uri)
Создает новый экземпляр файла путем преобразования данного файла: URI в абстрактный путь.
Общие методы класса File
метод
иллюстрировать
createNewFile()
Inseparably создает новый пустой файл тогда и только тогда, когда не существует файла с именем, указанным этим абстрактным путем.
delete()
Удаляет файл или каталог, представленный этим абстрактным путем.
exists()
Проверяет, существует ли файл или каталог, представленный этим абстрактным путем.
getAbsoluteFile()
Возвращает абсолютную форму пути этого абстрактного пути.
getAbsolutePath()
Возвращает абсолютную строку пути для этого абстрактного пути.
length()
Возвращает длину файла, представленного этим абстрактным путем.
mkdir()
Создает каталог, указанный этим абстрактным путем.
Пример использования файлового класса
public class FileTest {
public static void main(String[] args) throws IOException {
File file = new File("C:/Mu/fileTest.txt");
// 判断文件是否存在
if (!file.exists()) {
// 不存在则创建
file.createNewFile();
}
System.out.println("文件的绝对路径:" + file.getAbsolutePath());
System.out.println("文件的大小:" + file.length());
// 刪除文件
file.delete();
}
}
12345678910111213141516
2.2 Байтовый поток
InputStream
а такжеOutputStream
Это два абстрактных класса, которые являются базовыми классами потоков байтов.Все конкретные классы реализации потоков байтов наследуют эти два класса соответственно.
кInputStream
Например, он наследуетObject
, выполненоCloseable
public abstract class InputStream
extends Object
implements Closeable
123
InputStream
Существует множество подклассов реализации класса, некоторые из наиболее часто используемых перечислены ниже:
Давайте подробнее рассмотрим классы на изображении выше:
-
InputStream
:InputStream
Это абстрактный базовый класс всех входных потоков байтов.Как упоминалось ранее, абстрактные классы не могут быть инстанцированы.Фактически они существуют как шаблоны и определяют методы обработки входных потоков для всех классов реализации. -
FileInputSream
: Поток ввода файла, очень важный поток ввода байтов, используется для чтения из файла. -
PipedInputStream
: Конвейерный входной поток байтов, который может реализовать канальную связь между несколькими потоками. -
ByteArrayInputStream
: Входной поток массива байтов, чтение из массива байтов (byte[]) в единицах байтов, то есть сохранение файлов ресурсов в виде байтов в массив байтов в этом классе. -
FilterInputStream
: класс декоратора, конкретный декоратор наследует этот класс, эти классы являются классами обработки, роль которых заключается в инкапсуляции класса узла и реализации некоторых специальных функций. -
DataInputStream
: поток ввода данных, который используется для украшения других потоков ввода, чтобы «позволить приложениям считывать основные типы данных Java из основного потока ввода машинно-независимым способом». -
BufferedInputStream
: буферизовать поток, украсить поток узла, внутри будет буферная область для хранения байтов, каждый раз, когда область буфера заполняется, а затем отправляется, вместо отправки одного байта или двух байтов более эффективно высокое значение. -
ObjectInputStream
: Входной поток объекта, используемый для предоставленияосновные данные или объектпостоянное хранение. Проще говоря, это означает, что объекты можно передавать напрямую, что обычно используется при десериализации. Это также поток обработки, и входным параметром конструктора являетсяInputStream
объект экземпляра.
OutputStream
Схема наследования классов:
OutputStream
наследование классов иInputStream
Так же следует отметить, чтоPrintStream
.
2.3 Поток символов
Подобно потокам байтов, потоки символов также имеют два абстрактных базовых класса, а именноReader
а такжеWriter
. Другие классы реализации потока символов наследуют эти два класса.
кReader
Например, его основной подкласс реализации выглядит следующим образом:
Подробное описание каждого класса:
-
InputStreamReader
: Мост из потока байтов в поток символов (InputStreamReader
Параметры конструктораFileInputStream
объект экземпляра), который считывает байты и декодирует их в символы, используя указанный набор символов. Используемый набор символов может быть указан по имени, указанному явно, или может быть принят набор символов по умолчанию для платформы. -
BufferedReader
: Чтение текста из потока ввода символов, настройка буфера для повышения эффективности.BufferedReader
правдаInputStreamReader
Инкапсуляция первого конструктора является экземпляром объекта последнего. -
FileReader
: Удобный класс для чтения символьных файлов,new FileReader(File file)
Эквивалентноnew InputStreamReader(new FileInputStream(file, true),"UTF-8")
,ноFileReader
Кодировку символов и размер байтового буфера по умолчанию указать нельзя. -
PipedReader
: Передать поток ввода символов. Реализуйте конвейерную связь между несколькими потоками. -
CharArrayReader
:отChar
Поток мультимедиа для чтения данных из массива. -
StringReader
:отString
Медиапоток, из которого считываются данные.
Writer
а такжеReader
Структура аналогична, а направление обратное, поэтому здесь не повторяется. Единственная разница в том, что,Writer
подклассPrintWriter
.
2.4 Сериализация
продолжение следует…
3 метода потока ввода-вывода
3.1 Метод потока байтов
байтовый входной потокInputStream
Основной метод:
-
read()
: прочитать байт данных из этого входного потока. -
read(byte[] b)
: Считывает до b.length байтов данных из этого входного потока в массив байтов. -
read(byte[] b, int off, int len)
: Считывает до len байтов данных из этого входного потока в массив байтов. -
close()
: закрывает этот входной поток и освобождает все системные ресурсы, связанные с этим потоком.
байтовый выходной потокOutputStream
Основной метод:
-
write(byte[] b)
: записывает байты b.length из указанного массива байтов в этот выходной поток файла. -
write(byte[] b, int off, int len)
: Записывает len байтов, начиная со смещения off в указанном массиве байтов, в этот выходной поток файла. -
write(int b)
: Запишите указанные байты в этот поток вывода файла. -
close()
: закрывает этот входной поток и освобождает все системные ресурсы, связанные с этим потоком.
3.2 Метод символьного потока
поток ввода символовReader
Основной метод:
-
read()
: чтение одного символа. -
read(char[] cbuf)
: чтение символов в массив. -
read(char[] cbuf, int off, int len)
: чтение символов в часть массива. -
read(CharBuffer target)
: Попытка прочитать символы в указанный буфер символов. -
flush()
: очистить буфер потока. -
close()
: Закройте этот поток, но сначала сбросьте его.
поток вывода символовWriter
Основной метод:
-
write(char[] cbuf)
: Напишите массив символов. -
write(char[] cbuf, int off, int len)
: записывает часть массива символов. -
write(int c)
: Напишите один символ. -
write(String str)
: Напишите строку. -
write(String str, int off, int len)
: Напишите часть строки. -
flush()
: очистить буфер потока. -
close()
: Закройте этот поток, но сначала сбросьте его.
Кроме того, поток буфера символов имеет два уникальных метода:
-
BufferedWriter
ДобрыйnewLine()
:Напишите разделитель строк. Этот метод автоматически адаптируется к разделителю строк системы, в которой он работает. -
BufferedReader
ДобрыйreadLine()
: прочитать строку текста.
4 Дополнительный контент
4.1 Биты, байты, символы
Байт — это единица измерения, указывающая количество данных.Это единица измерения, используемая компьютерными информационными технологиями для измерения емкости памяти.Обычно один байт равен восьми битам.
Символ (Character) Буквы, цифры, слова и символы, используемые в компьютерах, такие как «A», «B», «$», «&» и т. д.
Как правило, одна буква или символ в английском языке занимает один байт, а один китайский символ представлен двумя байтами.
Байты и символы:
- В коде ASCII одна английская буква (без учета регистра) составляет один байт, а один китайский символ — два байта.
- В кодировке UTF-8 одно английское слово занимает один байт, а одно китайское слово — три байта.
- В кодировке Unicode один английский — это один байт, а один китайский — два байта.
- Символ: английская пунктуация — один байт, китайская пунктуация — два байта. Например: английская точка занимает 1 байт, китайская точка . Занимает 2 байта.
- В кодировке UTF-16 для хранения символа английского алфавита или китайского символа требуется 2 байта (для хранения некоторых китайских символов в области расширения Unicode требуется 4 байта).
- В кодировке UTF-32 для хранения любого символа мира требуется 4 байта.
4.2 Сравнение эффективности потока ввода-вывода
Во-первых, сравните эффективность обычных байтовых потоков и буферизованных байтовых потоков:
public class MyTest {
public static void main(String[] args) throws IOException {
File file = new File("C:/Mu/test.txt");
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 3000000; i++) {
sb.append("abcdefghigklmnopqrstuvwsyz");
}
byte[] bytes = sb.toString().getBytes();
long start = System.currentTimeMillis();
write(file, bytes);
long end = System.currentTimeMillis();
long start2 = System.currentTimeMillis();
bufferedWrite(file, bytes);
long end2 = System.currentTimeMillis();
System.out.println("普通字节流耗时:" + (end - start) + " ms");
System.out.println("缓冲字节流耗时:" + (end2 - start2) + " ms");
}
// 普通字节流
public static void write(File file, byte[] bytes) throws IOException {
OutputStream os = new FileOutputStream(file);
os.write(bytes);
os.close();
}
// 缓冲字节流
public static void bufferedWrite(File file, byte[] bytes) throws IOException {
BufferedOutputStream bo = new BufferedOutputStream(new FileOutputStream(file));
bo.write(bytes);
bo.close();
}
}
12345678910111213141516171819202122232425262728293031323334353637
результат операции:
普通字节流耗时:250 ms
缓冲字节流耗时:268 ms
12
Такой результат меня удивил, не означает ли это, что буферный поток очень эффективен? Чтобы узнать почему, вы можете обратиться к исходному коду, чтобы найти ответ. посмотрите на поток байтового буфераwrite
метод:
public synchronized void write(byte b[], int off, int len) throws IOException {
if (len >= buf.length) {
/* If the request length exceeds the size of the output buffer,
flush the output buffer and then write the data directly.
In this way buffered streams will cascade harmlessly. */
flushBuffer();
out.write(b, off, len);
return;
}
if (len > buf.length - count) {
flushBuffer();
}
System.arraycopy(b, off, buf, count, len);
count += len;
}
123456789101112131415
Комментарии очень понятны: если длина запроса превышает размер выходного буфера, выходной буфер сбрасывается, а затем данные записываются напрямую. Таким образом, буферизованные потоки будут безвредно каскадироваться.
Однако, что касается того, почему он разработан таким образом, я не хочу понимать.Любой, кто понимает, может оставить сообщение и дать подсказки.
Исходя из вышеизложенной ситуации, чтобы сравнить разрыв в эффективности между обычными байтовыми потоками и буферизованными байтовыми потоками, необходимо избегать прямого чтения и записи длинных строк, поэтому разработан следующий случай сравнения: использование байтовых потоков и буферизованных байтовых потоков. копировать файлы по отдельности.
public class MyTest {
public static void main(String[] args) throws IOException {
File data = new File("C:/Mu/data.zip");
File a = new File("C:/Mu/a.zip");
File b = new File("C:/Mu/b.zip");
StringBuilder sb = new StringBuilder();
long start = System.currentTimeMillis();
copy(data, a);
long end = System.currentTimeMillis();
long start2 = System.currentTimeMillis();
bufferedCopy(data, b);
long end2 = System.currentTimeMillis();
System.out.println("普通字节流耗时:" + (end - start) + " ms");
System.out.println("缓冲字节流耗时:" + (end2 - start2) + " ms");
}
// 普通字节流
public static void copy(File in, File out) throws IOException {
// 封装数据源
InputStream is = new FileInputStream(in);
// 封装目的地
OutputStream os = new FileOutputStream(out);
int by = 0;
while ((by = is.read()) != -1) {
os.write(by);
}
is.close();
os.close();
}
// 缓冲字节流
public static void bufferedCopy(File in, File out) throws IOException {
// 封装数据源
BufferedInputStream bi = new BufferedInputStream(new FileInputStream(in));
// 封装目的地
BufferedOutputStream bo = new BufferedOutputStream(new FileOutputStream(out));
int by = 0;
while ((by = bi.read()) != -1) {
bo.write(by);
}
bo.close();
bi.close();
}
}
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950
результат операции:
普通字节流耗时:184867 ms
缓冲字节流耗时:752 ms
12
На этот раз разница в эффективности между обычным потоком байтов и буферизованным потоком байтов очевидна и достигает 245 раз.
Давайте посмотрим на сравнение эффективности между потоком символов и буферизованным потоком символов:
public class IOTest {
public static void main(String[] args) throws IOException {
// 数据准备
dataReady();
File data = new File("C:/Mu/data.txt");
File a = new File("C:/Mu/a.txt");
File b = new File("C:/Mu/b.txt");
File c = new File("C:/Mu/c.txt");
long start = System.currentTimeMillis();
copy(data, a);
long end = System.currentTimeMillis();
long start2 = System.currentTimeMillis();
copyChars(data, b);
long end2 = System.currentTimeMillis();
long start3 = System.currentTimeMillis();
bufferedCopy(data, c);
long end3 = System.currentTimeMillis();
System.out.println("普通字节流1耗时:" + (end - start) + " ms,文件大小:" + a.length() / 1024 + " kb");
System.out.println("普通字节流2耗时:" + (end2 - start2) + " ms,文件大小:" + b.length() / 1024 + " kb");
System.out.println("缓冲字节流耗时:" + (end3 - start3) + " ms,文件大小:" + c.length() / 1024 + " kb");
}
// 普通字符流不使用数组
public static void copy(File in, File out) throws IOException {
Reader reader = new FileReader(in);
Writer writer = new FileWriter(out);
int ch = 0;
while ((ch = reader.read()) != -1) {
writer.write((char) ch);
}
reader.close();
writer.close();
}
// 普通字符流使用字符流
public static void copyChars(File in, File out) throws IOException {
Reader reader = new FileReader(in);
Writer writer = new FileWriter(out);
char[] chs = new char[1024];
while ((reader.read(chs)) != -1) {
writer.write(chs);
}
reader.close();
writer.close();
}
// 缓冲字符流
public static void bufferedCopy(File in, File out) throws IOException {
BufferedReader br = new BufferedReader(new FileReader(in));
BufferedWriter bw = new BufferedWriter(new FileWriter(out));
String line = null;
while ((line = br.readLine()) != null) {
bw.write(line);
bw.newLine();
bw.flush();
}
// 释放资源
bw.close();
br.close();
}
// 数据准备
public static void dataReady() throws IOException {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 600000; i++) {
sb.append("abcdefghijklmnopqrstuvwxyz");
}
OutputStream os = new FileOutputStream(new File("C:/Mu/data.txt"));
os.write(sb.toString().getBytes());
os.close();
System.out.println("完毕");
}
}
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283
результат операции:
普通字符流1耗时:1337 ms,文件大小:15234 kb
普通字符流2耗时:82 ms,文件大小:15235 kb
缓冲字符流耗时:205 ms,文件大小:15234 kb
123
Проверено много раз, результаты схожи, видно, что эффективность потока символьного буфера существенно не улучшилась, нам больше его использоватьreadLine()
а такжеnewLine()
метод.