Provides "classical" facilities, based on the model of
stream-based I/O. This model has some advantages when
it comes to flexibility and reusability: for example, we can write a method that
reads data from any type of stream. To read data from a file, we can then pass
that method a FileInputStream. But equally, we could pass it a
ByteArrayInputStream to read cached data from memory.
While flexible, this model of I/O puts a lot of onus on the developer to handle lower-level issues such as buffering and
error checking. It is a relatively verbose API: e.g. to read the contents of a file, we are forced to deal
"manually" with the mechanics of creating a file input stream with relevant buffering, checking appropriately
for error conditions and closing the file when finished.
The classical Java I/O API is not suitable for certain high-performance I/O as it requires all data
to be copied from kernal space into user space.