Standard I/O in Java
This post is a brief summary (“TL;DR”) of the official Java tutorial lesson on Basic I/O.
At first glance, standard input/output seems so complicated! There are so many options! This post should clear the cloud up.
Streams
It all starts from “streams”. There are two types of streams: byte streams and character streams.
- Byte streams:
InputStream
OutputStream
- Character streams:
Reader
Writer
- The familiar
System.out
is aPrintStream
, which is a subclass ofOutputStream
, which is discussed far below. - The (perhaps not-so-) familiar
System.in
is aInputStream
.
File I/O
These are obviously subclasses of the base stream classes of corresponding names.
FileInputStream
FileOutputStream
FileReader
FileWriter
Adapting byte streams into character streams
This can be done through these two classes:
InputStreamReader
OutputStreamWriter
They are “adaptors” (in the design patterns sense). They:
- Are subclasses of the character stream base classes; and
- Take instances of the byte stream classes as a constructor argument.
Obviously, a charset is needed. If not provided, the “default charset” is used.
Decorating streams into buffered streams
There are “buffered” versions of each of the four base stream classes:
BufferedInputStream
BufferedOutputStream
BufferedReader
BufferedWriter
These clases are “decorators” (in the design patterns sense). They:
- Are subclasses of their decorated classes; and
- Take instances of their decorated classes.
Since they’re “buffers”, obviously:
- Input buffers fetch when empty;
- Output buffers flush when full.
The buffered stream classes are subclasses of each of their decoratees.
Up to this point, you can already have enough things to fiddle with to get creative, e.g.:
The above is exactly what Mk Yong introduced in his article, which I claimed, at the start of this article, to be “complicated”. Now we should have an idea of what’s happening.
Higher-level stuff: “scanning” and “formatting”
- “Scanning” means taking character/byte streams and interpreting (raw) data
(i.e. chars/bytes) into structured data (e.g. ints).
- It provides all the
hasNextBigInteger
,nextLine
APIs.
- It provides all the
- “Formatting” means the reverse: taking structured data and displaying it as
characters/bytes.
- It provides all the
.format("The int is: %d", 42)
,.println(42)
APIs.
- It provides all the
Java classes involved:
Scanner
, which can “scan” from all sorts of streams;PrintStream
, which can “format” to byte streams;PrintWriter
, which can “format” to characters streams.
The inheritance status of these 3 classes aren’t as symmetric as the other classes mentioned above.
Scanner
doesn’t inherit from anything; andPrintStream
is anOutputStream
; andPrintWriter
is aWriter
.
It’s worth mentioning that both the Scanner
and PrintWriter
classes can be
constructed from byte streams as well as character streams. However in the
case of byte streams, quite obviously, if not specified explicitly, the “default
charset” will be used.
That’s all!
I have been using Java every now and then for quite a while, but I seldom needed to do standard I/O - it was more often some OOP in some framework, like Android.
I might be spoilt by Python with regards to I/O - just an input()
call and a print()
call, and it’s all done. I used C++ a tiny bit for
some programming contests, and encountered the idea of “streams” — which
made the Java stuff less intimidating. Eventually — e.g. now — I figured
the Java stuff out.