Предисловие
В предыдущей статье мы представили соответствующий контент в структуре потоковой передачи файлов Java, в то время как наша статья будет сосредоточена на соответствующем содержании потоковой передачи символов файла.
Прежде всего, должно быть ясно, что файлы обработки потока байтов основаны на байтах, в то время как файлы обработки потока символов основаны на символах в качестве основных единиц.
Но на самом деле, сущностью операции потока символов является инкапсуляция двух процессов «операции по потоку байтового потока» + «кодирования». Как ты так думаешь? Независимо от того, пишете ли вы символ в файл, вам нужно кодировать символы в двоичный файл, а затем записать его в файл в байтах в качестве основного устройства, или вы читаете символ в память, вам необходимо прочитать его в байтах в качестве основного блока, а затем транскодировать его в символы.
Это важно понимать, что определит ваше общее понимание потоков персонажей. Давайте посмотрим на дизайн связанных API вместе.
Base Class Reader/Writer
Прежде чем официально изучать базовый класс потока персонажа, нам нужно знать, как персонаж представлен на Java.
Прежде всего, кодирование символов по умолчанию в Java: UTF-8, и мы знаем, что кодированные символы UTF-8 хранятся с использованием от 1 до 4 байтов, а более часто используемые символы используются меньше байтов.
Тип ChAR определяется как два байта, то есть для обычных символов, Char может хранить персонажа, но для некоторых дополнительных наборов символов два ChARS часто используются для представления персонажа.
Читатель является базовым классом для чтения потоков символов, и он обеспечивает самые основные операции чтения символов. Давайте посмотрим вместе.
Сначала посмотрим на его конструктор:
Защищенный объект Lock; Protected Reader () {this.lock = this;} защищенный читатель (объект блокировка) {if (lock == null) {бросить новый nullpointerexception (); } this.lock = lock;}Читатель - это абстрактный класс, поэтому нет никаких сомнений в том, что эти конструкторы призваны к подклассам и используются для инициализации объектов блокировки блокировки, которые мы подробно объясним позже.
public int Read () бросает ioException {char cb [] = new char [1]; if (read (cb, 0, 1) == -1) return -1; else return cb [0];} public int Read (char cbuf []) Throws ioException {return Read (cbuf, 0, cbuf.length);} Abstract public int Read (char cbuf [], int off, int len)Основная операция чтения персонажа здесь. Первый метод используется для чтения символа. Если он был прочитал до конца файла, он вернет -1. То же самое принимается с int как тип возврата значения, почему бы не использовать char? Причина одинакова, все из -за неопределенности интерпретации значения -1.
Второй метод аналогичен третьему методу, считывая символы указанной длины из файла и помещают их в целевой массив. Третий метод - это абстрактный метод, который должен быть реализован подклассами, в то время как второй метод основан на нем.
Есть некоторые другие методы, которые похожи:
Эти методы на самом деле хорошо известны и, как правило, похожи на наш InputStream, и они не имеют основной реализации. Я не буду вдаваться в подробности здесь, вы можете приблизительно знать, что внутри него.
Писатель - это письменный поток символов, который используется для написания одного или нескольких символов в файл. Конечно, конкретный метод записи по -прежнему является абстрактным методом и должен быть реализован подклассами, поэтому мы не будем повторять его здесь.
Адаптер InpustStramReader/outputStreamWriter
Адаптерные потоки символов наследуют от читателя или писателя базового класса, которые являются очень важными членами системы потока символов. Основная функция состоит в том, чтобы преобразовать поток байта в потоку символов. Давайте сначала возьмем адаптер для чтения в качестве примера.
Прежде всего, его основные члены:
Частный Final Streamdecoder SD;
StreamDecoder - это декодер, используемый для преобразования различных операций байтов в соответствующие операции символов. Мы непрерывно упоминаем об этом в последующем введении, и мы не будем объяснять это здесь одинаково.
Тогда есть конструктор:
public inputStreamReader (inputStream in) {super (in); try {sd = streamdecoder.forinputStreamReader (in, это, (строка) null); } catch (UnsupportEncodingException e) {бросить новую ошибку (e); }} public inputStreamReader (inputStream in, String charsetName) бросает UnsupportedEncodingException {super (in); if (charsetName == null) бросить новое NullPointerException ("charsetName"); SD = StreamDecoder.ForinPutStreamReader (in, this, charsetName);}Цель этих двух конструкторов - инициализация этого декодера. Метод ForInputStreamReader называется, но параметры разные. Давайте посмотрим на реализацию этого метода:
Это типичный статический заводской шаблон. Нечего сказать о трех параметрах, VAR0 и VAR1, представляющих экземпляр Byte Stream и экземпляр адаптера соответственно.
Параметр var2 фактически представляет имя кодирования символов. Если он нулевой, будет использоваться кодирование символов системы по умолчанию: UTF-8.
Наконец, мы можем получить экземпляр декодера.
Почти все методы, представленные следующим образом, реализованы путем полагательства на этот декодер.
public String getEncoding () {return sd.getEncoding ();} public int Read () Throws IoException {return sd.read ();} public int Read (char cbuf [], int offset, int length) {return sd.read (cbuf, смещение, длина);} public void close trows ioexception {sd.);};Код реализации связанных методов в декодере все еще относительно сложный. Мы не будем проводить глубокие исследования здесь, но общая идея реализации такова: процесс «чтения байтового потока + декодирования».
Конечно, должен быть противоположный экземпляр Streamencoder в OutputStreamWriter для кодирования символов.
Кроме того, остальные операции ничем не отличаются, либо записанные в файл через массив символов, записанный в файл через строку или записанные в файл через более низкие 16 бит INT.
File Stecment Stream FileReader/Writer
Поток символов файла можно сказать, что очень прост. Нет другого метода, кроме конструктора, и он полностью зависит от потока байта файла.
Давайте возьмем FileReader в качестве примера.
FileReader наследует от inputStreamReader и имеет только следующие три конструктора: public fileReader (String FileName) Throws FilenOtFoundException {super (new FileInputStream (filename));} public fileReader (файл файл) throws filereDexcept FileInputStream (fd));}Теоретически, все потоки символов должны основываться на нашем адаптере, потому что только он обеспечивает конверсию с символом в байтовую, независимо от того, пишете или читаете, он неразделимый от него.
Наш FileReader не распространяет ни один из своих собственных методов. Предварительно внедренный метод работы символов в inputstreamReader родительского класса достаточно для него. Ему нужно пройти только в соответствующем экземпляре байтового потока.
То же самое относится и к файлам, я не буду вдаваться в подробности здесь.
Персонаж массив Stream ChararrayReader/Writer
Массивы символов и байтовые потоки массива одинаковы, как для решения ситуации, когда существует неопределенный размер файла и требует чтения большого количества контента.
Поскольку они предоставляют механизм динамического расширения внутри, они могут не только размещать целевые файлы, но и управлять размером массива, чтобы не выделять слишком много памяти и тратить много пространства памяти.
Возьмите ChararrayReader в качестве примера
Защищенный char buf []; public chararrayReader (char buf []) {this.buf = buf; this.pos = 0; this.count = buf.length;} public ChararrayReader (char buf [], int offset, int length) {// ..}Основной задачей конструктора является инициализация массива символов во внутренний атрибут BUF. Все последующие операции чтения в экземпляре Stecment Marry Stream будут основаны на массиве символов BUF.
Что касается других методов ChararrayReader и ChararrayWriter, я не буду повторять их здесь, которые в основном похожи на поток байтовых массивов в предыдущей статье.
Кроме того, есть также вовлеченные строки и StringWriter. На самом деле, он по сути такой же, как и поток массива персонажей. В конце концов, суть строки - это массив.
BufferedReader/Writer
Аналогичным образом, BufferedReader/Writer - это буферный поток, а также поток декоратора, используемый для обеспечения буферизации. Как правило, похожий на наш байтовый буферный поток, давайте кратко представим его здесь.
Частный читатель в; Private Char CB []; Private Static Int DefaultChuffersize = 8192; Public BufferedReader (Reader In, int Sz) {..} public BufferedReader (читатель в) {this (in, defaultChuffersize);}CB - это массив символов, который кэширует некоторых символов, читаемых из потока файла. Вы можете инициализировать длину этого массива в конструкторе, в противном случае будет использоваться значение по умолчанию 8192.
public int Read () бросает ioException {..} public int Read (char cbuf [], int off, int len) {...}Что касается чтения, это зависит от метода чтения атрибута члена.
Следовательно, почти все потоки символов не могут быть отделены от экземпляра потока байтов.
Я не буду повторять это здесь о BufferedWriter. Он в основном похож, за исключением того, что один читает, а другой пишет, и он вращается вокруг внутреннего массива персонажей.
Стандартный поток распечатки
Существует два основных типа потоков распечатки, PrintStream и PrintWriter. Первый - это байтовый поток, а последний - это поток персонажей.
Считается, что эти два потока интегрируют потоки в соответствии с их соответствующими категориями. Существуют богатые методы внутренней инкапсуляции, но реализация также немного сложна. Давайте сначала посмотрим на поток PrintStream Byte:
Есть несколько основных конструкторов:
Очевидно, что простые конструкторы будут полагаться на сложные конструкторы, которые уже считаются «старой рутиной» для дизайна JDK. Что отличает его от других байтовых потоков, так это то, что PrintStream предоставляет автофлуш флага, который указывает, автоматически обновлять кэш.
Следующим является метод написания PrintStream:
Кроме того, PrintStream также инкапсулирует большое количество методов печати и записывает различные типы контента в файлы, такие как:
Конечно, эти методы на самом деле не пишут цифровой двоичный файл, а просто напишите их соответствующие строки в файл, например:
Печать (123);
Окончательный файл - это не двоичный оператор, соответствующий 123, а только строку 123, которая является потоком печати.
Поток буферированного символа, используемый PrintStream, реализует все операции печати. Если указано автоматическое обновление, буфер будет автоматически обновляться при встрече с символом новой линии «/N».
Таким образом, PrintStream интегрирует все методы вывода в байтовые потоки и потоки символов, где метод записи используется для операций по потоку байтов, а метод печати используется для операций потока символов, которые необходимо прояснить.
Что касается PrintWriter, это полный поток символов, который полностью работает против символов. Будь то метод записи или метод печати, это операция по потоку символа.
Подводя итог, мы потратили три статьи, объясняющие байтовые потоки и операции по потоку символов в Java. Потоки байтов завершают передачу данных между диском и памятью на основе байтов. Наиболее типичным является потоки символов файла, и их реализации являются локальными методами. Благодаря основным возможностям переноса байта мы также можем повысить эффективность за счет буферизации.
Самая основная реализация потоков символов - InputStreamReader и OutputStreamWriter. Теоретически, они уже могут завершить базовые операции по потоку символов, но они ограничены только самыми основными операциями. Что необходимо для построения их экземпляров, так это «экземпляр Byte Stream» + «формат кодирования».
Следовательно, взаимосвязь между потоком символов и байтовым потоком похожа на вышеупомянутое уравнение. Необходимым шагом для написания символа в файл диска состоит в том, чтобы кодировать символ в указанном формате кодирования, а затем использовать поток байтов для записи кодируемого двоичного символа в файл. Операция чтения противоположна.
Все коды, изображения и файлы в статье хранятся в облаке на моем GitHub:
(https://github.com/singleyam/overview_java)
Вы также можете загрузить локально.
Суммировать
Вышеуказанное - все содержание этой статьи. Я надеюсь, что содержание этой статьи имеет определенную справочную ценность для каждого обучения или работы. Если у вас есть какие -либо вопросы, вы можете оставить сообщение для общения. Спасибо за поддержку Wulin.com.