I have a problem where my program is not ending as expected when an exception is thrown. I have tracked it down to the combination of InputStream and BufferedInputStream swallowing the exceptions. The following code demonstrates the problem:
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
public class Test {
public static void main(String[] args) throws IOException {
try (InputStream stream = new BufferedInputStream(new MyStream())) {
int x = stream.read();
while (x != -1) {
System.out.println(x);
x = stream.read();
}
}
}
static class MyStream extends InputStream {
int i = 0;
@Override
public int read() throws IOException {
i = i + 1;
if (i == 5) {
throw new IOException("i == 5");
}
if (i > 10) {
return -1;
}
return i;
}
}
}
This produces the output 1 2 3 4 6 7 8 9 10
If the BufferedInputStream is removed, the exception is thrown when i == 5 as expected.
I tracked it down to the InputStream code. If multiple bytes are read, the InputStream will only propagate an IOException for the first byte, not subsequent bytes. BufferedInputStream changes the read behavior to read multiple bytes at a time and so the IOException is caught by InputStream and discarded.
What is the correct behavior here?
Is it a rule that "Once an IOException, always an IOException" i.e. MyStream needs to remember that it threw an IOException and throw again on any subsequent call?
Other alternatives:
- throw an unchecked exception that will not be caught by the InputStream. I don't think I can throw a different checked exception while extending InputStream
- also override the multi byte read method so it does not catch the IOException. However this seems like it would be vulnerable to the same problem if it was wrapped again by other input streams.
I am grateful for any guidance. At the moment, I think the unchecked exception is my best alternative.
If multiple bytes are read, the InputStream will only propagate an IOException for the first byte,
Do you mean "BufferedInputStream"? Otherwise I'm a little confused what your actual test result was. – LactescentInputStream
or at least its default implementation. I'll give you eating I/O errors like that is weird, but that's what it does. The docs give you a hint that you can subclassInputStream
and provide your own, better implementation if needs be. – LactescentInputStream
for is to subclass it. All other input streams are subclasses:FileInputStream
,SocketInputStream
,ObjectInputStream
etc. It's a base class designed to be replaced. – LactescentMyStream
, you just need to override that specifc method as well. And if you don't want your class to get subclassed again, then mark it as final. – Scarabaeoid