FileInputStream represents a 'real' resource. If you don't close it, you leak real memory, and that's a real problem.
InputStreamReader does not represent a 'real' resource. If you don't close it but you DO close the thing it was wrapping, you do not leak real memory, and it's no big deal.
If you have this code:
@Cleanup InputStream is = new BufferedInputStream(new FileInputStream("/some/path/to/a/file"));
then you've written broken code (whether you use @Cleanup or ARM, doesn't matter) - you are now relying on the BufferedInputStream, i.e. the filtering stream, to forward close calls to the inner 'real' resource. However, maybe that goes wrong, or, maybe the creation of the filtering stream goes wrong. In either case, you're failing to close a 'real' resource, and thus you've created a real problem.
Therefore, the above code is NEVER acceptable, and relying on a filtering stream to forward a close call is pointless. Whatever you do, if you call new FileInputStream, then you need to stick 'fis.close();' somewhere in some guard block (or use @Cleanup or ARM on this FileInputStream), and if you want to read this thing via a filtering stream like BufferedInputStream, tough cookies, that does not absolve you of the need to @Cleanup/ARM the real resource.
But here's the trick: While you need to ARM/@Cleanup the 'real' resource, you DO NOT need to ARM/@Cleanup the filter, because that does not actually represent a resource. The following code:
byte[] b = new byte[4096];
{
@Cleanup FileInputStream rawIn = new FileInputStream("/path/to/some/file");
BufferedInputStream in = new BufferedInputStream(rawIn);
in.read(b);
in.close();
}
is NOT BUGGY - even though 'in' is not protected, it doesn't matter, because the relevant resource (rawIn) _IS_ protected. Assuming no exceptional exit occurs in this method, then in.close() effectively serves as a way to 'flush' the bufferedinputstream (irrelevant for reading, but very relevant for writing!) and if you forget to close/flush it your code is still buggy. If an exceptional exit does occur (reading the file causes an exception), then the 'in.close()' call is skipped and thus we fail-to-flush, but this is generally not important anyway (after all the I/O stream just failed on us, it's not gonna work anyway... or something else failed and whatever we wrote to the file is now irrelevant).
I usually program like this - I always throw .close() calls into the code in addition to the @Cleanup annotation, and I only @Cleanup resources that are 'real' and not virtual.