Потоки Java, файлы и ввод-вывод

Потоки, файлы и ввод-вывод (ввод/вывод) в Java — это инфраструктура для обработки чтения и записи данных. Они позволяют программам взаимодействовать с внешними данными (такими как файлы, сети, системный ввод и т. д.).

Пакет java.io — это основной пакет в стандартной библиотеке Java, предоставляющий классы для системного ввода и вывода. Он содержит инструменты для работы с потоками данных (байтовыми и символьными потоками), чтения и записи файлов, сериализации и форматирования данных.

java.io — это основополагающий пакет для работы с файловыми операциями, операциями с потоками и низкоуровневыми операциями ввода-вывода.

Потоки в пакете java.io поддерживают множество форматов, таких как примитивные типы, объекты, локализованные наборы символов и т. д.

Поток можно понимать как последовательность данных. Входной поток представляет чтение данных из источника, а выходной поток — запись данных в место назначения.


Чтение консольного ввода

Ввод с консоли в Java обрабатывается с помощью System.in.

Чтобы получить символьный поток, связанный с консолью, можно обернуть System.in в объект BufferedReader, чтобы создать символьный поток.

Ниже приведён базовый синтаксис для создания BufferedReader:

BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

После создания объекта BufferedReader мы можем использовать метод read() для чтения символа с консоли или метод readLine() для чтения строки.


Чтение нескольких символов из консольного ввода

Чтобы прочитать символ из объекта BufferedReader, используйте метод read(). Его синтаксис выглядит следующим образом:

int read( ) throws IOException

Каждый раз, когда вызывается метод read(), он читает символ из входного потока и возвращает символ в виде целочисленного значения. Он возвращает -1, когда поток заканчивается. Этот метод выбрасывает IOException.

Следующая программа демонстрирует использование метода read() для непрерывного чтения символов с консоли, пока пользователь не введётq。

Код файла BRRead.java:

//Чтение символов с консоли с помощью BufferedReader import java.io.*; public class BRRead { public static void main(String[] args) throws IOException { char c; //Создать BufferedReader с использованием System.in BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); System.out.println("Введите символы, нажмите 'q' для выхода."); //Чтение символов do { c = (char) br.read(); System.out.println(c); } while (c != 'q'); } }

Результат компиляции и выполнения приведенного выше примера выглядит следующим образом:

输入字符, 按下 'q' 键退出。
example
r
u
n
o
o
b


q
q

Чтение строк из консольного ввода

Чтобы прочитать строку из стандартного ввода, нужно использовать метод readLine() класса BufferedReader.

Его общий формат:

String readLine( ) throws IOException

Следующая программа читает и отображает строки символов, пока вы не введёте слово "end".

Код файла BRReadLines.java:

//Чтение символов с консоли с помощью BufferedReader import java.io.*; public class BRReadLines { public static void main(String[] args) throws IOException { //Создайте BufferedReader с помощью System.in BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); String str; System.out.println("Введите строки текста."); System.out.println("Введите 'end' для выхода."); do { str = br.readLine(); System.out.println(str); } while (!str.equals("end")); } }

Результат компиляции и выполнения приведенного выше примера выглядит следующим образом:

Enter lines of text.
Enter 'end' to quit.
This is line one
This is line one
This is line two
This is line two
end
end

В версиях после JDK 5 мы также можем использовать классJava Scannerдля получения ввода с консоли.


Вывод на консоль

Как было сказано ранее, вывод на консоль осуществляется с помощью print() и println(). Все эти методы определены в классе PrintStream, а System.out — это ссылка на объект этого класса.

PrintStream наследует класс OutputStream и реализует метод write(). Таким образом, write() также можно использовать для записи в консоль.

Простейшая форма write(), определённая в PrintStream, выглядит так:

void write(int byteval)

Этот метод записывает младшие восемь битов byteval в поток.

Пример

В следующем примере используется write() для вывода на экран символа "A", за которым следует символ новой строки:

Код файла WriteDemo.java:

import java.io.*; //Демонстрирует System.out.write(). public class WriteDemo { public static void main(String[] args) { int b; b = 'A'; System.out.write(b); System.out.write('\n'); } }

Запуск приведённого выше примера выводит символ "A" в окне вывода.

A

Примечание:Метод write() используется нечасто, поскольку методы print() и println() удобнее в использовании.


Чтение и запись файлов

Как упоминалось ранее, поток определяется как последовательность данных. Входные потоки используются для чтения данных из источника, а выходные потоки — для записи данных в место назначения.

Следующая диаграмма — это диаграмма иерархии классов, описывающая входные и выходные потоки.

Байтовые потоки (обработка двоичных данных)

Байтовые потоки используются для обработки двоичных данных, таких как файлы, изображения, видео и т. д.

Имя классаTypeОписание
InputStreamАбстрактный класс (входной поток)Суперкласс всех байтовых входных потоков, обрабатывающий операции байтового ввода.
OutputStreamАбстрактный класс (выходной поток)Суперкласс всех байтовых выходных потоков, обрабатывающий операции байтового вывода.
FileInputStreamВходной потокЧитает байтовые данные из файла.
FileOutputStreamВыходной потокЗаписывает байтовые данные в файл.
BufferedInputStreamВходной потокОбеспечивает буферизацию для байтовых входных потоков для повышения эффективности чтения.
BufferedOutputStreamВыходной потокОбеспечивает буферизацию для байтовых выходных потоков для повышения эффективности записи.
ByteArrayInputStreamВходной потокИспользует массив байтов в памяти в качестве источника ввода.
ByteArrayOutputStreamВыходной потокЗаписывает данные в массив байтов в памяти.
DataInputStreamВходной потокПозволяет читать примитивные типы данных Java из входного потока (например,int、float、boolean)。
DataOutputStreamВыходной потокПозволяет записывать примитивные типы данных Java в выходной поток.
ObjectInputStreamВходной потокЧитает сериализованные объекты из входного потока.
ObjectOutputStreamВыходной потокСериализует объекты и записывает их в выходной поток.
PipedInputStreamВходной потокИспользуется для чтения байтовых данных в канале, обычно сPipedOutputStreamИспользуются вместе.
PipedOutputStreamВыходной потокИспользуется для записи байтовых данных в канале, обычно сPipedInputStreamИспользуются вместе.
FilterInputStreamВходной потокКласс-обёртка для входных потоков байтов, используемый для фильтрации других входных потоков.
FilterOutputStreamВыходной потокКласс-обёртка для выходных потоков байтов, используемый для фильтрации других выходных потоков.
SequenceInputStreamВходной потокОбъединяет несколько входных потоков в один входной поток для обработки.

Символьные потоки (обработка текстовых данных)

Символьные потоки используются для обработки текстовых данных, например для чтения и записи строк или файлов.

Имя классаTypeОписание
ReaderАбстрактный класс (входной поток)Суперкласс всех входных символьных потоков, обрабатывающий операции ввода символов.
WriterАбстрактный класс (выходной поток)Суперкласс всех символьных потоков вывода, обрабатывающий операции вывода символов.
FileReaderВходной потокЧитает символьные данные из файла.
FileWriterВыходной потокЗаписывает символьные данные в файл.
BufferedReaderВходной потокОбеспечивает буферизацию для символьных потоков ввода, поддерживает построчное чтение и повышает эффективность чтения.
BufferedWriterВыходной потокОбеспечивает буферизацию для символьных потоков вывода, поддерживает построчную запись и повышает эффективность записи.
CharArrayReaderВходной потокИспользует массив символов в качестве источника ввода.
CharArrayWriterВыходной потокЗаписывает данные в массив символов.
StringReaderВходной потокИспользует строку в качестве источника ввода.
StringWriterВыходной потокЗаписывает данные в строковый буфер.
PrintWriterВыходной потокУдобный символьный поток вывода, поддерживает автосброс и форматированный вывод.
PipedReaderВходной потокИспользуется для чтения символьных данных в канале, обычно сPipedWriterиспользуется вместе.
PipedWriterВыходной потокИспользуется для записи символьных данных в канал, обычно сPipedReaderиспользуется вместе.
LineNumberReaderВходной потокБуферизованный входной поток символов с номерами строк, позволяющий отслеживать номер считываемой строки.
PushbackReaderВходной потокПозволяет возвращать символы обратно в поток после чтения, чтобы их можно было прочитать снова.

Вспомогательные классы (другие важные классы)

Вспомогательные классы обеспечивают поддержку файлов, каталогов и произвольного доступа к файлам.

Имя классаTypeОписание
FileОперации с файлами и каталогамиИспользуется для представления файла или каталога и предоставляет файловые операции, такие как создание, удаление, переименование и т. д.
RandomAccessFileФайл произвольного доступаПоддерживает произвольный доступ к файлам, позволяя читать данные из любой позиции файла и записывать их в любую позицию файла.
ConsoleКонсольный ввод/выводОбеспечивает поддержку ввода и вывода для системной консоли.

Два важных потока, которые будут рассмотрены далее, — этоFileInputStreamиFileOutputStream。


FileInputStream

Этот поток используется для чтения данных из файла. Его объект можно создать с помощью ключевого слова new.

Существует несколько конструкторов, которые можно использовать для создания объектов.

Вы можете использовать имя файла строкового типа для создания объекта входного потока для чтения файла:

InputStream f = new FileInputStream("C:/java/hello");

Вы также можете использовать файловый объект для создания объекта входного потока для чтения файла. Сначала нам нужно использовать метод File() для создания файлового объекта:

File f = new File("C:/java/hello"); InputStream in = new FileInputStream(f);

После создания объекта InputStream вы можете использовать следующие методы для чтения потока или выполнения других операций с потоком.

МетодОписаниеПример кода
int read()Читает один байт данных, возвращаемое значение — целое число от 0 до 255. Возвращает -1, если достигнут конец потока.int data = inputStream.read();
int read(byte[] b)Читает байты из входного потока и сохраняет их в байтовом массивеbи возвращает фактическое количество прочитанных байтов. Возвращает -1, если достигнут конец потока.byte[] buffer = new byte[1024]; int bytesRead = inputStream.read(buffer);
int read(byte[] b, int off, int len)Читает доlenбайтов из входного потока и сохраняет их в массиве байтовbизoffначиная с позиции offset, возвращая фактическое количество прочитанных байтов. Возвращает -1, если достигнут конец потока.byte[] buffer = new byte[1024]; int bytesRead = inputStream.read(buffer, 0, buffer.length);
long skip(long n)Пропускает и отбрасываетnбайтов во входном потоке, возвращая фактическое количество пропущенных байтов.long skippedBytes = inputStream.skip(100);
int available()Возвращает количество байтов, которые можно прочитать (без блокировки).int availableBytes = inputStream.available();
void close()Закрывает входной поток и освобождает все ресурсы, связанные с этим потоком.inputStream.close();
void mark(int readlimit)Устанавливает метку в текущей позиции потока,readlimitявляется верхним пределом количества байтов, которые можно прочитать.inputStream.mark(1024);
void reset()Перемещает поток в позицию последней метки. Если метки нет или метка недействительна, выбрасываетIOException。inputStream.reset();
boolean markSupported()Проверяет, поддерживает ли текущий входной потокmark()иreset()операцию.boolean isMarkSupported = inputStream.markSupported();

Помимо InputStream, существуют и другие входные потоки. Для получения дополнительной информации перейдите по ссылке ниже:


FileOutputStream

Этот класс используется для создания файла и записи данных в файл.

Если целевой файл не существует до того, как поток откроет файл для вывода, поток создаст файл.

Существуют два конструктора, которые можно использовать для создания объекта FileOutputStream.

Используйте имя файла строкового типа для создания объекта выходного потока:

OutputStream f = new FileOutputStream("C:/java/hello")

Вы также можете использовать объект файла для создания выходного потока для записи в файл. Сначала нам нужно использовать метод File() для создания объекта файла:

File f = new File("C:/java/hello"); OutputStream fOut = new FileOutputStream(f);

После создания объекта OutputStream вы можете использовать следующие методы для записи в поток или выполнения других операций с потоком.

МетодОписаниеПример кода
void write(int b)Записывает указанный байт в выходной поток; младшие 8 бит отbбудут записаны в поток.outputStream.write(255);
void write(byte[] b)Записывает все байты в массиве байтовbв выходной поток.byte[] data = "Hello".getBytes(); outputStream.write(data);
void write(byte[] b, int off, int len)Записывает байты из массива байтовbначиная со смещенияoffначиная сlenбайты в выходной поток.byte[] data = "Hello".getBytes(); outputStream.write(data, 0, data.length);
void flush()Сбрасывает выходной поток и принудительно записывает все буферизованные данные, гарантируя, что данные немедленно записываются в целевой выходной поток.outputStream.flush();
void close()Закрывает выходной поток и освобождает все ресурсы, связанные с этим потоком. После закрытия больше нельзя выполнять запись.outputStream.close();

Помимо OutputStream, существуют и другие выходные потоки. Для получения дополнительных сведений перейдите по ссылке ниже:

Пример

Ниже приведён пример, демонстрирующий использование InputStream и OutputStream:

код файла fileStreamTest.java:

import java.io.*; public class fileStreamTest { public static void main(String[] args) { try { byte bWrite[] = { 11, 21, 3, 40, 5 }; OutputStream os = new FileOutputStream("test.txt"); for (int x = 0; x < bWrite.length; x++) { os.write(bWrite[x]); // записывает байты } os.close(); InputStream is = new FileInputStream("test.txt"); int size = is.available(); for (int i = 0; i < size; i++) { System.out.print((char) is.read() + " "); } is.close(); } catch (IOException e) { System.out.print("Exception"); } } }

Вышеприведённая программа сначала создаёт файл test.txt, записывает заданные числа в файл в бинарной форме и одновременно выводит их в консоль.

Поскольку приведённый выше код использует бинарную запись, могут появляться искажённые символы. Вы можете использовать следующий пример кода, чтобы решить проблему с искажёнными символами:

Код файла fileStreamTest2.java:

//Имя файла: fileStreamTest2.java import java.io.*; public class fileStreamTest2 { public static void main(String[] args) throws IOException { File f = new File("a.txt"); FileOutputStream fop = new FileOutputStream(f); //Создаёт объект FileOutputStream. Если файл не существует, он будет создан автоматически. OutputStreamWriter writer = new OutputStreamWriter(fop, "UTF-8"); //Создаёт объект OutputStreamWriter. Параметр может задавать кодировку; по умолчанию используется стандартная кодировка операционной системы, в Windows это gbk. writer.append("Китайский ввод"); //Запись в буфер writer.append("\r\n"); //Newline writer.append("English"); //Сбросить буфер и записать в файл. Если ниже больше нет содержимого для записи, прямой вызов close также выполнит запись. writer.close(); //Закрыть поток записи; в то же время содержимое буфера будет записано в файл, поэтому выше закомментировано. fop.close(); //Закрыть выходной поток и освободить системные ресурсы. FileInputStream fip = new FileInputStream(f); //Создать объект FileInputStream. InputStreamReader reader = new InputStreamReader(fip, "UTF-8"); //Создать объект InputStreamReader; кодировка совпадает с той, что использовалась при записи. StringBuffer sb = new StringBuffer(); while (reader.ready()) { sb.append((char) reader.read()); //Преобразовать в char и добавить его в объект StringBuffer. } System.out.println(sb.toString()); reader.close(); //Закрыть поток чтения. fip.close(); //Закрыть входной поток и освободить системные ресурсы. } }

Файлы и ввод-вывод

Также есть несколько классов, связанных с файлами и вводом-выводом, которые нам нужно знать:


Каталоги в Java

Создание каталогов:

В классе File есть два метода, которые можно использовать для создания папок:

  • mkdir( )Метод создаёт папку. Он возвращает true при успехе и false при неудаче. Неудача означает, что путь, указанный объектом File, уже существует, или что папку нельзя создать, потому что полный путь ещё не существует.
  • mkdirs()Метод создаёт папку и все её родительские папки.

Следующий пример создает папку "/tmp/user/java/bin":

Код файла CreateDir.java:

import java.io.File; public class CreateDir { public static void main(String[] args) { String dirname = "/tmp/user/java/bin"; File d = new File(dirname); //Сейчас создается каталог d.mkdirs(); } }

Скомпилируйте и выполните приведенный выше код, чтобы создать каталог "/tmp/user/java/bin".

Примечание:Java автоматически различает разделители пути к файлам в зависимости от соглашений в UNIX и Windows. Если вы используете разделитель (/) в версии Java для Windows, путь все равно может быть разрешен правильно.


Чтение каталогов

Каталог на самом деле является объектом File, который содержит другие файлы и папки.

Если вы создаете объект File и он является каталогом, то вызов метода isDirectory() вернет true.

Вы можете извлечь список файлов и папок, которые он содержит, вызвав метод list() для этого объекта.

В приведенном ниже примере показано, как использовать метод list() для проверки содержимого папки:

Код файла DirList.java:

import java.io.File; public class DirList { public static void main(String args[]) { String dirname = "/tmp"; File f1 = new File(dirname); if (f1.isDirectory()) { System.out.println("Directory" + dirname); String s[] = f1.list(); for (int i = 0; i < s.length; i++) { File f = new File(dirname + "/" + s[i]); if (f.isDirectory()) { System.out.println(s[i] + "является каталогом"); } else { System.out.println(s[i] + "является файлом"); } } } else { System.out.println(dirname + "не является каталогом"); } } }

Результат компиляции и запуска приведенного выше примера выглядит следующим образом:

目录 /tmp
bin 是一个目录
lib 是一个目录
demo 是一个目录
test.txt 是一个文件
README 是一个文件
index.html 是一个文件
include 是一个目录

Удаление каталогов или файлов

Чтобы удалить файл, можно использовать методjava.io.File.delete().

Следующий код удалит каталог/tmp/java/. Обратите внимание, что при удалении каталога необходимо убедиться, что в этом каталоге нет других файлов, чтобы удалить его корректно; в противном случае удаление не удастся.

Структура тестового каталога:

/tmp/java/
|-- 1.log
|-- test

Код файла DeleteFileDemo.java:

import java.io.File; public class DeleteFileDemo { public static void main(String[] args) { //Измените это на свой собственный тестовый каталог File folder = new File("/tmp/java/"); deleteFolder(folder); } //Удалить файлы и каталоги public static void deleteFolder(File folder) { File[] files = folder.listFiles(); if (files != null) { for (File f : files) { if (f.isDirectory()) { deleteFolder(f); } else { f.delete(); } } } folder.удаление(); } }
Другие расширения