That's not correct.
All streams can read many bytes, or only one at a time.
BufferedInputStream fills a buffer before you read from it, regardless of how many bytes you want to read.
FileInputStream doesn't have a buffer. That means when you want to read 4 bytes from it, it will return 4 bytes directly from the underlying file.
Let's take a look what happens if you have a buffered stream around a file stream, and you read a couple of bytes from it. I'm going to assume that the buffer is 8 bytes large, and that the file contains 21 bytes. This is for simplicity. When you first open the file and wrap the stream in a BufferedInputStream, this is the situation:
Buffer: | [0, 0, 0, 0, 0, 0, 0, 0] |
File: | [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21] |
Now, let's say we read 4 bytes from the buffered stream. Because the buffer is empty (shown by striking out all the values), it will first fill itself completely by doing a bulk read on the wrapped file stream:
Buffer: | [1, 2, 3, 4, 5, 6, 7, 8] |
File: | [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21] |
Afterwards it returns the requested 4 bytes:
Buffer: | [1, 2, 3, 4, 5, 6, 7, 8] |
File: | [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21] |
Now, let's ask the buffered stream for 10 bytes. You can see it doesn't have enough bytes in its buffer, so it keeps the bytes that are already available separate, and then refills the entire buffer with a bulk read on the wrapped file stream:
Result: | [5, 6, 7, 8, 0, 0, 0, 0, 0, 0] |
Buffer: | [9, 10, 11, 12, 13, 14, 15, 16] |
File: | [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21] |
After the buffer has refilled, it fills the remaining bytes of the result, and returns it:
Buffer: | [9, 10, 11, 12, 13, 14, 15, 16] |
File: | [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21] |
When we ask for 10 more bytes, the process repeats: the two remaining bytes from the buffer are kept aside, and the buffer attempts to fill itself again, except this time, it detects that the underlying file stream has too few bytes to completely fill it. This is the final situation:
Result: | [15, 16, 17, 18, 19, 20, 21, 0, 0, 0] |
Buffer: | [17, 18, 19, 20, 21, 14, 15, 16] |
File: | [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21] |
The last available bytes are returned, and on subsequent reads to the buffered stream, it will return -1 to signify that there are no more bytes.
Now, the reason to put this buffer between the application and the file, is because the application will often want to read data in small chunks, but it's very inefficient to read small chunks of data from a file. Typically the buffer size is a power of 2, larger than 1000, such as 4096. That means that when you want to read 4 bytes from the file and the buffer is empty, it will first read a chunk of 4kb from the file, and then return 4 bytes from that chunk. Reading small amounts of bytes is much more efficient when the bytes are already in memory.