Java Byte Streams: InputStream and OutputStream - Quiz
Total: 6 questions
1. What does InputStream.read(byte[] buffer) return, and why should a file copy loop call out.write(buffer, 0, count) instead of out.write(buffer)?
What does InputStream.read(byte[] buffer) return, and why should a file copy loop call out.write(buffer, 0, count) instead of out.write(buffer)?
The read(byte[] buffer) method reads up to buffer.length bytes and returns the number of bytes actually read, or -1 at the end of the stream. It may read fewer bytes than the array size at any time, and the last block of a file is almost never full.
If you write the whole array with out.write(buffer), the copy gets leftovers from the previous block and becomes larger than the original. So write only what was read:
byte[] buffer = new byte[8192];
int count;
while ((count = in.read(buffer)) != -1) {
out.write(buffer, 0, count);
}
Since Java 9 the whole loop can be replaced with one line: in.transferTo(out).
2. Can you use InputStream.available() to get the file size or to read a stream to the end?
Can you use InputStream.available() to get the file size or to read a stream to the end?
No. available() returns only an estimate of the bytes that can be read right now without blocking. It is not the file size and not the rest of the stream: for a network stream it often returns 0, even though more data is on its way.
A loop like for (int i = 0; i < in.available(); i++) may work with a local file but reads nothing from a socket. The correct way is to read until read() returns -1, or to use readAllBytes() / transferTo().
The exception is ByteArrayInputStream: its data sits in memory, so its available() returns the exact number of remaining bytes.
3. Which methods for reading and copying a whole stream were added to InputStream and OutputStream in Java 9 and Java 11?
Which methods for reading and copying a whole stream were added to InputStream and OutputStream in Java 9 and Java 11?
Java 9:
byte[] readAllBytes()reads all remaining bytes; for very large streams it can throwOutOfMemoryError.long transferTo(OutputStream out)copies the rest of the stream to the output stream and returns the number of bytes copied.
Java 11:
byte[] readNBytes(int len)reads up tolenbytes; at the end of the stream it returns an empty array, not-1.InputStream.nullInputStream()is an empty stream that returns-1immediately.OutputStream.nullOutputStream()is a stream that silently discards every byte written to it.
To turn a stream into a string, use new String(in.readAllBytes(), StandardCharsets.UTF_8); it is better to specify the charset explicitly.
4. Which constructors do FileInputStream and FileOutputStream have, and when do they throw FileNotFoundException?
Which constructors do FileInputStream and FileOutputStream have, and when do they throw FileNotFoundException?
Both classes accept a file name as a String or a File object. FileOutputStream also has a variant with a second parameter, boolean append:
InputStream in = new FileInputStream("data.bin");
OutputStream out = new FileOutputStream("log.txt"); // the file is truncated
OutputStream app = new FileOutputStream("log.txt", true); // append to the end
FileNotFoundException (a subclass of IOException) is thrown:
- by
FileInputStreamif the file does not exist, is a directory, or cannot be opened for reading; - by
FileOutputStreamif the file cannot be created or opened: for example, the directory that should contain it does not exist, or the path points to a directory. A missing file is simply created.
Keep in mind: the constructor without append truncates an existing file before the first write.
5. How do ByteArrayInputStream and ByteArrayOutputStream differ from file streams, and do you need to close them?
How do ByteArrayInputStream and ByteArrayOutputStream differ from file streams, and do you need to close them?
They work with an array in memory: ByteArrayInputStream reads from the array you pass in, and ByteArrayOutputStream writes to an automatically growing array that toByteArray() returns. They are handy wherever a method expects an InputStream/OutputStream, for example in unit tests.
read(),write(int)andwrite(byte[], int, int)are overridden withoutthrows IOException: an in-memory stream has no I/O to fail.writeBytes(byte[])(Java 11) writes the whole array withoutIOException, unlike the inheritedwrite(byte[]).toString(Charset)(Java 10) turns the collected bytes into a string in the given charset.available()inByteArrayInputStreamreturns the exact number of remaining bytes.
Technically you do not need to close them: close() has no effect in both classes, and their methods keep working after it without IOException. You must close streams tied to operating system resources: files, sockets and wrappers around them.
6. Why do you need BufferedInputStream and BufferedOutputStream, and what role does flush() play?
Why do you need BufferedInputStream and BufferedOutputStream, and what role does flush() play?
They are decorators (subclasses of FilterInputStream/FilterOutputStream) around another byte stream. They read and write data in blocks through an internal buffer, 8 KB (8192 bytes) by default; you can set the size with the second constructor parameter: new BufferedInputStream(in, 64 * 1024). Instead of a system call for every read() or write(int), the stream goes to the file once per block, so byte-by-byte processing becomes many times faster.
flush() pushes the bytes collected in the buffer to the destination. If you neither call flush() nor close a BufferedOutputStream, the last bytes stay in memory and never reach the file. close() calls flush() itself, and closing the outer wrapper also closes the nested FileOutputStream. For FileOutputStream itself, flush() does nothing.