Класс ByteArrayInputStream пакета java.io можно использовать для чтения массива входных данных (в байтах).

Чтобы создать входной поток массива байтов, мы должны сначала импортировать пакет java.io.ByteArrayInputStream. Как только мы импортируем пакет, у нас есть два конструктора для создания входного потока:


ByteArrayInputStream input = new ByteArrayInputStream(arr);
ByteArrayInputStream input = new ByteArrayInputStream(arr, 2, 2);
    

Внутри класса есть 4 поля:


//Массив байтов, предоставленный создателем потока
protected byte buf[];

//Индекс следующего символа для чтения из буфера входного потока
protected int pos;

//Текущая отмеченная позиция в потоке
protected int mark = 0;

//Индекс на единицу больше, чем последний допустимый символ в буфере входного потока
protected int count;
    

И вот наши конструкторы:


public ByteArrayInputStream(byte buf[]) {
    this.buf = buf;
    this.pos = 0;
    this.count = buf.length;
}

public ByteArrayInputStream(byte buf[], int offset, int length) {
    this.buf = buf;
    this.pos = offset;
    this.count = Math.min(offset + length, buf.length);
    this.mark = offset;
}
    

Методы ByteArrayInputStream

Метод Действие
int read() Считывает следующий байт данных из этого входного потока.
int read(byte b[], int off, int len) Считывает некоторое количество байтов из входного потока и сохраняет их в буферном массиве b.
off – начальное смещение в целевом массиве b.
len – максимальное количество прочитанных байтов.
long skip(long n) Пропускает n байтов ввода из этого входного потока. Возвращает количество пропущенных байтов (может быть меньше, если достигнут конец входного потока).
int available() Возвращает количество оставшихся байтов, которые можно прочитать (или пропустить) из этого входного потока.
void reset() Сбрасывает буфер в отмеченную позицию. Отмеченная позиция равна 0, если не была отмечена другая позиция или указано другое смещение в конструкторе.
boolean markSupported() Проверяет, поддерживает ли этот InputStream отметку/сброс. Возвращает true для ByteArrayInputStream.
void close() Ничего не делает.
void mark(int readAheadLimit) Устанавливает текущую позицию в поле mark. Если будет вызван метод reset, последующее чтение начнется именно с этой позиции. Значение параметра метода readAheadLimit не влияет на его работу и просто не используется.

Давайте разберем эти методы подробнее и посмотрим на практике, как они работают.

read()

Когда нужно читать байты из ByteArrayInputStream так же, как и из обычного InputStream, можно использовать метод read().


public static void main(String[] args) {
   byte[] array = {1, 2, 3, 4};

   try (ByteArrayInputStream input = new ByteArrayInputStream(array)) {
       for (int i = 0; i < array.length; i++) {
           int data = input.read();
           System.out.print(data + ", ");
       }
   } catch (IOException e) {
       e.printStackTrace();
   }
}
    

available()

Если вы хотите проверить, есть ли что-то в вашем буфере, можно вызвать метод available().


public static void main(String[] args) {
   byte[] array = {1, 2, 3, 4};

   try (ByteArrayInputStream input = new ByteArrayInputStream(array)) {
       System.out.println("байтов, доступных для чтения " + input.available());

       input.read();
       System.out.println("байтов, свободных для чтения " + input.available());

       input.read();
       System.out.println("байтов, свободных для чтения " + input.available());
   } catch (IOException e) {
       e.printStackTrace();
   }
}
    

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

Вывод программы:

байтов, доступных для чтения 4
байтов, свободных для чтения 3
байтов, свободных для чтения 2

skip(long n)

С помощью метода skip() можно пропустить определенное кол-во байтов и не считывать их.


public static void main(String[] args) {
   byte[] array = {1, 2, 3, 4};

   try (ByteArrayInputStream input = new ByteArrayInputStream(array)) {
       input.skip(2);

       while (input.available() != 0) {
           int data = input.read();
           System.out.print(data + ", ");
       }
   } catch (IOException e) {
       e.printStackTrace();
   }
}
    

Вывод программы:

3, 4,

reset()

Метод сбрасывает положение буферизированного потока до последней отмеченной позиции. Он занимает позицию метки 0, если явно не указана другая метка.


public static void main(String[] args) {
   byte[] buf = {65, 66, 67, 68, 69};
   try (ByteArrayInputStream input = new ByteArrayInputStream(buf)) {
       System.out.println("прочитали: " + input.read());
       System.out.println("прочитали: " + input.read());
       System.out.println("прочитали: " + input.read());
       System.out.println("прочитали: " + input.read());

       System.out.println("вызов метода reset()");
       input.reset();
       System.out.println("прочитали: " + input.read());
       System.out.println("прочитали: " + input.read());
   } catch (IOException e) {
       e.printStackTrace();
   }
}
    

В результате мы увидим, как после вызова метод reset() мы попадем на начальную точку нашего потока.

Вывод программы:

прочитали: 65
прочитали: 66
прочитали: 67
прочитали: 68
вызов метода reset()
прочитали: 65
прочитали: 66

mark(int readAheadLimit)

Метод mark() класса ByteArrayInputStream устанавливает внутреннюю метку в текущей позиции байта, то есть сразу после чтения предыдущего байта. Этот метод принимает параметр, указывающий, сколько байтов можно прочитать после этой метки, прежде чем она станет недействительной. По умолчанию, если метка не была установлена явно, ByteArrayInputStream помечает позицию 0 или позицию со смещением, переданным его конструктору. Важно отметить, что для данного класса отметка readAheadLimit недействительна.


/* Note: The {@code readAheadLimit} for this class
*  has no meaning.
*
* @since   1.1
*/
public void mark(int readAheadLimit) {
   mark = pos;
}
    

Вот пример установки метки в ByteArrayInputStream с помощью его метода mark() и метода reset(). Мы добавим к предыдущему примеру вызов метода mark():


public static void main(String[] args) {
   byte[] buf = {65, 66, 67, 68, 69};
   try (ByteArrayInputStream input = new ByteArrayInputStream(buf)) {
       System.out.println("прочитали: " + input.read());
       System.out.println("прочитали: " + input.read());
       System.out.println("прочитали: " + input.read());
       input.mark(5);

       System.out.println("прочитали: " + input.read());
       System.out.println("прочитали: " + input.read());

       System.out.println("вызов метода reset()");
       input.reset();

       System.out.println("прочитали: " + input.read());
       System.out.println("прочитали: " + input.read());

   } catch (IOException e) {
       e.printStackTrace();
   }
}
    

В результате мы можем увидеть, что позиция текущего потока уже изменилась.

Вывод программы:

прочитали: 65
прочитали: 66
прочитали: 67
прочитали: 68
прочитали: 69
вызов метода reset()
прочитали: 68
прочитали: 69

markSupported()

Метод markSupported() позволяет проверить доступ для установки метки. Чтобы понять, на основе чего выдается результат, перейдем в метод:


/**
* Tests if this {@code InputStream} supports mark/reset. The
* {@code markSupported} method of {@code ByteArrayInputStream}
* always returns {@code true}.
*
* @since   1.1
*/
public boolean markSupported() {
   return true;
}
    

Наш метод всегда возвращает true. Проверим это на примере:


public static void main(String[] args) {
   byte[] buf = {65, 66, 67, 68, 69};
   try (ByteArrayInputStream bais = new ByteArrayInputStream(buf)) {
       boolean isMarkSupported = bais.markSupported();

       System.out.println("isMarkSupported: " + isMarkSupported);
       System.out.println("прочитали: " + bais.read());
       System.out.println("прочитали: " + bais.read());

       bais.mark(1);
       System.out.println("прочитали: " + bais.read());
       isMarkSupported = bais.markSupported();
       System.out.println("isMarkSupported: " + isMarkSupported);

       bais.reset();
       isMarkSupported = bais.markSupported();
       System.out.println("isMarkSupported: " + isMarkSupported);
   } catch (IOException e) {
       e.printStackTrace();
   }
}
    

В результате после выполнения метода mark() и метода reset() наш поток всегда готов и может устанавливать отметки:

Вывод программы:

isMarkSupported: true
прочитали: 65
прочитали: 66
прочитали: 67
isMarkSupported: true
isMarkSupported: true

close()

Чтобы понять работу метода close, мы также посмотрим, что у него внутри:


/**
* Closing a {@code ByteArrayInputStream} has no effect. The methods in
* this class can be called after the stream has been closed without
* generating an {@code IOException}.
*/
public void close() throws IOException {
}
    

В документации о методе close сказано: закрытие ByteArrayInputStream не оказывает никакого эффекта. Методы класса ByteArrayInputStream можно вызывать после закрытия потока без создания исключения IOException.

Какой вывод можем сделать?

ByteArrayInputStream нужен нам, когда необходимо читать данные из массива байт. Обычно имеет смысл использовать этот класс не сам по себе, а в комбинации с другим кодом, который умеет работать с InputStream.