Не учить бесчисленное количество — IO и NIO в Java

Java задняя часть

Ввод/вывод и NIO в JAVA

Проблема ввода-вывода является неизбежной проблемой для любого языка программирования.Можно сказать, что проблема ввода-вывода является основной проблемой всего взаимодействия человека с компьютером, поскольку ввод-вывод является основным каналом для получения и обмена данными между машинами. Информация. В нынешнюю эпоху стремительного роста данных проблема ввода-вывода особенно актуальна и может легко стать узким местом в производительности.

Что такое ввод-вывод

/ O? Или ввод / вывод? Относится к интерфейсу между компьютером и остальным внешним миром или с компьютерной программой. Это очень важно для любой компьютерной системы, и, следовательно, в зависимости от всего ввода / вывода на самом деле встроена в операционную систему. Отдельные программы обычно позволяют системе сделать большую часть работы для них.

  • I: отжесткий дискчитать содержимое вОЗУсередина
  • O: отОЗУчитать содержимое вжесткий дисксередина

в некоторых случаяхI/OОтсутствует взаимодействие с жестким диском, такое как потоковая передача по каналу, которая включает связь между двумя потоками. Сам канал представляет собой циклический буфер, выделенный в памяти, который может использоваться только двумя соединяющими его потоками.

Классы операций ввода-вывода в Java находятся в пакетеjava.ioНиже, вероятно, около 80 классов, но эти классы можно разделить на три группы.

  • Байтовый интерфейс ввода-вывода:InputStreamиOutputStream
  • Интерфейс ввода-вывода на основе персонажей:WriterиReader
  • Интерфейс ввода-вывода на основе дисковых операций:File

Кроме того, под каждым интерфейсом есть свои классы-оболочки, которые используются дляорнамент, добавить в него какие-то функции, да и Java-сложность ввода-вывода тоже тут, да и код для создания классов в разных режимах оформления тоже разный.

Байтовые операции

InputStreamРоль используется для представления тех классов, которые генерируют входные данные из различных источников данных, включая

  • байтовый массив
  • Строковый объект
  • документ
  • Трубы работают так же, как настоящие трубы, с входом с одного конца и выводом с другого конца.
  • другие источники данных, такие какInternetсерединаSocketсоединять

InputStreamдиаграмма классов,OutputStreamДиаграмма классов похожа на эту

своего рода Функции Параметры конструктора как пользоваться
ByteArrayInputStream Позволяет использовать буферы памяти в качествеInputStreamиспользовать буфер, из которого берутся байты как источник данных: объедините его сFilterInputStreamобъекты связаны, чтобы обеспечить полезный интерфейс
StringBufferInputStream Преобразовать строку вInputStream String, базовая реализация фактически используетStringBuffer как источник данных: объедините его сFilterInputStreamОбъекты связаны, чтобы обеспечить полезные интерфейсы
FileInputStream для чтения информации из файла Строка, представляющая имя файла, файл илиFileDescriptorобъект как источник данных он сочетается сFilterInputStreamОбъекты связаны, чтобы обеспечить полезные интерфейсы
PipedInputStream Для написанияPipedOutputStreamОбъем данных для реализации концепции конвейера PipedOutputStream В качестве многопоточного источника данных: объедините его сFilterInputStreamОбъекты связаны, чтобы обеспечить полезные интерфейсы
FilterInputStream абстрактный класс, как интерфейс для декораторов, для другихInputStreamОбеспечьте полезные функции

Используйте фильтры, чтобы добавить полезные свойства и полезные интерфейсы

Библиотека классов ввода-вывода в Java требует комбинации множества различных функций, что и является причиной шаблона декоратора. И это также причина, по которой в библиотеке классов ввода-вывода java есть класс Filter (фильтр), который является базовым классом всех декоративных классов.

своего рода Функции
BufferedInputStream С его помощью можно предотвратить взаимодействие с диском при каждом чтении и использовать буфер для чтения фиксированного значения за один раз, а затем выполнить операцию записи на диск, сократив количество взаимодействий с диском. ускорить
DataInputStream Позволяет приложениям считывать основные типы данных Java из базового потока ввода машинно-независимым способом.

Например, просто используйте фильтр для чтения содержимого файла и вывода, пример выглядит следующим образом:

public static void main(String[] args) throws IOException {
    InputStream inputStream = new BufferedInputStream(new FileInputStream("/Users/hupengfei/Downloads/a.sql"));
    byte[] buffer = new byte[1024];
    while ( inputStream.read(buffer)!=-1){
        System.out.println(new String(buffer));
    }
    inputStream.close();
}

Скопируйте пример файла:

  public static void main(String[] args) throws IOException {
        InputStream inputStream =new BufferedInputStream(new FileInputStream("/Users/hupengfei/Downloads/leijicheng.png"));
        OutputStream outputStream =new BufferedOutputStream(new FileOutputStream("/Users/hupengfei/Downloads/fuzhi.png"));
        byte [] buffer = new byte[1024];
        while (inputStream.read(buffer)!=-1){
            outputStream.write(buffer);
        }
        outputStream.flush();
        inputStream.close();
        outputStream.close();
    }

Если вы хотите использоватьBufferedOutputStreamЕсли вы пишете в файл, не забудьте вызвать после записи буфера.flush()Очистить буфер. Принудительно записать данные в буфер. В противном случае данные могут быть не записаны.

символьные операции

Будь то дисковая или сетевая передача, наименьшая единица хранения — байты, а не символы, поэтому операции ввода-вывода — это байты, а не символы, но почему интерфейс ввода-вывода работает с символами? Это связано с тем, что данные, которыми обычно оперируют в наших программах, имеют форму символов.Для удобства работы, конечно, должен быть предусмотрен интерфейс ввода-вывода для непосредственной записи символов.

Или старые правила, давайте посмотрим наReaderДиаграмма классов , соответствующий поток байтовInputStream

Reader类图

один из нихInputStreamReaderэто возможноInputStreamпреобразовать вReaderТо есть перевести байты в символы. почему дизайнReaderиWriter, в основном для интернационализации. Предыдущий поток байтов поддерживает только 8-битные потоки байтов и не может хорошо обрабатывать 16-битные символы Unicode. Поскольку Unicode используется для интернационализации символов, он добавленReaderиWriterОн предназначен для поддержки Unicode во всех операциях ввода/вывода.

В некоторых случаях ориентированный байтовый потокInputStreamиOutputStreamправильное решение, особенно вjava.util.zipБиблиотеки классов ориентированы на байты, а не на символы. Поэтому самое разумное, что можно сделать, это попытаться расставить приоритеты в использованииReaderиWriter, когда программа не скомпилируется, нам приходится использовать библиотеку, ориентированную на байты.

Или напишите связанный простой пример чтения файла

public static void main(String[] args) throws IOException {
    BufferedReader bufferedReader = new BufferedReader(new FileReader("/Users/hupengfei/Downloads/a.sql"));
    String date;
    StringBuilder stringBuilder = new StringBuilder();
    while ((date = bufferedReader.readLine()) != null){
        stringBuilder.append(date +"\n");
    }
    bufferedReader.close();
    System.out.println(stringBuilder.toString());
}

перечислитьreadLine()способ добавить новую строку, потому чтоreadLine()Разрывы строк удаляются автоматически

Что такое НИО

существуетJDK1.4Класс NIO был добавлен в , мы также можем назвать его новым вводом-выводом. NIO был создан, чтобы позволить Java-программистам реализовывать высокоскоростной ввод-вывод без написания собственного собственного кода. NIO может значительно повысить скорость, перенеся наиболее трудоемкие операции ввода-вывода (например, заполнение и выборку буферов) обратно в операционную систему.

Увеличение скорости происходит за счет использования структур, которые ближе к тому, как операционная система выполняет ввод-вывод: каналы и буферы.

Каналы и буферы — это основные объекты в NIO, и они используются почти во всех операциях ввода-вывода. Канал является аналогом потока в исходном пакете ввода/вывода. Данные в любом месте (из любого места) должны проходить через объект Channel. Буфер — это, по сути, объект-контейнер. Все объекты, отправляемые в канал, должны быть предварительно помещены в буфер Buffer.

Мы можем думать о них как об угольной шахте, канал — это шахта, содержащая уголь (данные), а буфер — это вагонетка, которая отправляется в шахту, вагонетка возвращается полная угля, и мы получаем уголь из вагонетки. То есть мы напрямую не взаимодействуем с каналом, мы взаимодействуем только с буфером.

Введение в буфер

Буфер — это объект, который содержит некоторые данные для чтения или данные для передачи. Объект Buffer добавлен в NIO, что отражает важное отличие от предыдущего ввода-вывода. В потоковом вводе-выводе мы взаимодействуем с данными напрямую через потоковые объекты, но в NIO наше взаимодействие с данными должно проходить через буфер.

Буфер — это, по сути, массив. Обычно это массив байтов, но могут использоваться и другие виды массивов. Но буфер — это больше, чем просто массив, буфер обеспечивает структурированный доступ к данным, а также отслеживает процесс чтения и записи в системе.

Далее мы можем посмотреть наBufferСвязанные классы реализации

Buffer相关实现类

КаждыйBufferклассыBufferЭкземпляр интерфейса. КромеByteBuffer,КаждыйBufferВсе классы имеют одни и те же операции, различаются только типы данных, которые они обрабатывают. Поскольку большинство стандартных операций ввода-вывода используютByteBuffer, так что у него есть все операции с общим буфером, а также некоторые уникальные операции.

ByteBufferявляется единственным буфером, взаимодействующим непосредственно с каналом, то есть буфером, который может хранить необработанные байты. когда мы смотрим наByteBufferКогда вы получаете исходный код, вы обнаружите, что он создаетByteBufferобъект, а также имеет набор методов для вывода и чтения данных в необработанных байтах или примитивных типах данных. Однако невозможно вывести или прочитать объекты, даже объекты, являющиеся строками. Это низкоуровневый способ обработки, но это нормально, поскольку это более эффективный способ отображения в большинстве операционных систем.

Введение в канал (канал)

Channelэто объект, через который буфер может читать и записывать данные. По сравнению с исходным вводом-выводом канал подобен потоку. Как упоминалось ранее,ChannelОн не взаимодействует с данными. Но он немного отличается от потока, то есть канал двунаправленный, а поток может быть только односторонним (только InputStream или OutputStream), но канал может использоваться для чтения, записи или и для чтения, и для записи.

В предыдущем вводе-выводе изменены три класса, которые можно использовать для генерацииFileChannelобъект. Три классаFileInputStream,FileOutputStreamи для чтения и для записиRandomAccessFile.

Давайте создадимFileChannelпример.

 FileChannel in = new FileInputStream("fileName").getChannel();

использование NIO

Я приведу простой пример, чтобы продемонстрировать, как использовать NIO для копирования файлов. Как упоминалось выше, NIO работает с буфером и каналом, который взаимодействует с буфером, поэтому теперь нам нужно иметь два объекта, один из которыхBufferиChannel.

    public static void main(String[] args) throws IOException {
    	 //获取读通道
        FileChannel in = new FileInputStream("/Users/hupengfei/Downloads/hu.sql").getChannel();
        //获取写通道
        FileChannel out = new FileOutputStream("/Users/hupengfei/Downloads/a.sql").getChannel();
        //为缓冲器进行初始化大小
        ByteBuffer byteBuffer =ByteBuffer.allocate(1024);
        while (in.read(byteBuffer)!=-1){
            //做好让人读的准备
            byteBuffer.flip();
            out.write(byteBuffer);
            //清除数据
            byteBuffer.clear();
        }
    }

Как только вы захотите прочитать данные из буфера, вызовите метод буфераflip()метод, делая его готовым для чтения байтов кем-то другим. Затем после записи данных будет вызван буфер.clear()метод переставляет все внутренние указатели так, чтобы буфер находился в другомread()Будьте готовы принимать данные во время работы. Затем данные будут непрерывно считываться из исходного файла в целевой файл.

clear()Этот метод описан в исходном коде, этот метод на самом деле не очищает данные в буфере.

Конечно, описанный выше метод также может быть простым, напрямую соединяя два канала, нужно только позвонитьtransferTo()метод, это также эффект копирования файлов.

    public static void main(String[] args) throws IOException {
        FileChannel in = new FileInputStream("/Users/hupengfei/Downloads/hu.sql").getChannel();
        FileChannel out = new FileOutputStream("/Users/hupengfei/Downloads/a.sql").getChannel();
        in.transferTo(0,in.size(),out);
    }

Справочная статья