Как именно работает метод read() у InputStream?

117
06 апреля 2021, 23:50

Как же все-таки устроена работа метода read():

  1. Почему метод возвращает int а не byte?
  2. Почему возвращается byte как int в диапазоне от 0 до 255?
  3. Почему метод не может возвращать byte?
  4. Что вернет метод если он считает -1 из входного потока?
Answer 1

Почему метод read класса InputStream возвращает int, а не byte

Попробуйте выполнить такой код:

byte myByte = -3; // 11111101
byte[] myArray = {myByte};
InputStream in = new ByteArrayInputStream(myArray);
int myInt = in.read();
System.out.println(myInt); // 253

В консоль выводится 253, а не -3.

Почему так происходит?

Тип данных int в Java является дополненнным до двух целым числом и использует 32 бита вместо 8.

В 32х битном виде число 253 будет:

00000000000000000000000011111101

т. е. метод read() возвращает не само значение byte, а его представление в 32х битном виде.

Как хранятся значения в int в byte

| число | 32 bit                           |   8 bit  |
  -----   --------------------------------   --------
|   . . |  . . . . . . . . . . . . . . . . |
|   . . |  . . . . . . . . . . . . . . . . |
|  -128 | 11111111111111111111111110000000 | 10000000 |
|  -127 | 11111111111111111111111110000001 | 10000001 |
|  -126 | 11111111111111111111111110000010 | 10000010 |
|  -125 | 11111111111111111111111110000011 | 10000011 |
|   . . |  . . . . . . . . . . . . . . . . |  . . . . |
|   . . |  . . . . . . . . . . . . . . . . |  . . . . |
|    -2 | 11111111111111111111111111111110 | 11111110 |
|    -1 | 11111111111111111111111111111111 | 11111111 |
|     0 | 00000000000000000000000000000000 | 00000000 |
|     1 | 00000000000000000000000000000001 | 00000001 |
|     2 | 00000000000000000000000000000010 | 00000010 |
|   . . |  . . . . . . . . . . . . . . . . |  . . . . |
|   . . |  . . . . . . . . . . . . . . . . |  . . . . |
|   125 | 00000000000000000000000001111101 | 01111101 |
|   126 | 00000000000000000000000001111110 | 01111110 |
|   127 | 00000000000000000000000001111111 | 01111111 |
|   . . |  . . . . . . . . . . . . . . . . |
|   . . |  . . . . . . . . . . . . . . . . |

Диапазон byte в Java лежит от -128 до 127, а возвращаемое значение метода read() лежит в диапазоне от 0 до 255.

Что происходит с числом byte в методе read

Чтобы получить представление byte в int в методе read() используется побитовое «И» c числом 255, т. е. убираем лидирующие единицы.

| число | 32 bit                           |
  -----   --------------------------------
|    -3 | 11111111111111111111111111111101 |
 И
|   255 | 00000000000000000000000011111111 |
 =
|   253 | 00000000000000000000000011111101 |

Чтобы из представления получить обратно значение byte в int, нужно выполнить обратную операцию побитовое «ИЛИ» c числом -256, т. е. добавляем лидирующие единицы.

| число | 32 bit                           |
  -----   --------------------------------
|   253 | 00000000000000000000000011111101 |
 ИЛИ
|  -256 | 11111111111111111111111100000000 |
 =
|    -3 | 11111111111111111111111111111101 |

Что происходит с числом byte == -1 в методе read()

То же самое: убираем лидирующие единицы.

| число | 32 bit                           |
  -----   --------------------------------
|    -1 | 11111111111111111111111111111111 |
 И
|   255 | 00000000000000000000000011111111 |
 =
|   255 | 00000000000000000000000011111111 |
Answer 2

На все 4 вопроса вообще один ответ. Возвращается int потому что надо такой тип, который может вместить в себя один байт (реальные данные) плюс одно служебное значение (это тот самый -1), которое является признаком окончания чтения.

Вообще конечно можно бы было спроектировать метод read() так, чтобы он возвращал byte. Но тогда этот метод в случае окончания потока либо должен был бросать исключение, либо вводить допольнительный метод, при помощи которого бы можно было проверять окончание потока. С обоими способами бы были проблемы, т.к. с исключениями у программистов всегда бы оно ловилось, а природа исключений немного другая. А с дополнительным методом нельзя заставить обязать программиста его везде вызывать (ведь если этот вызов будет опущен, то как отделить, когда из потока методом read возращается 0 с данными от 0 когда данных нет.

Вообще говоря, этот метод редко используется, в основном из-за своих проблем с производительностью. Предпочтение отдаётся методу read(byte[]), который читает сразу массив байт.

P.S. Другой вопрос почему тут выбрали int, а не short. Точных причин сказать не могу, но скорее потому, что short считается неполноценным братом int. Многие арифметические алгоритмы предпочитают использовать всегда int, даже когда точно известно, что диапазона short вполне хватит. И ещё есть момент с short. JVM-инструкции более заточены на int, нежели на short.

READ ALSO
Подскажите как реализовать сервер для Android приложений

Подскажите как реализовать сервер для Android приложений

Я разрабатываю приложение под Android и хотел бы узнать как реализовать сервер который бы принимал данные и сохранял их в базу данных желательно...

115
Запись матрицы в csv файл на java

Запись матрицы в csv файл на java

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

117
Чем отличаются методы от конструкторов в java?

Чем отличаются методы от конструкторов в java?

Чем отличаются методы от конструкторов в java? Для чего нужно то и другое?

94