First:
- Binary data: byte[], InputStream, OutputStream;
- (Unicode) text: String, char, Reader, Writer;
- Bridges where binary data has some encoding/Charset and is actually text: InputStreamReader, OutputStreamWriter (converting from/to given or default Charset).
Now consider:
System.in is a InputStream.
System.out and System.err are a PrintStream extending from OutputStream.
They are conceived as for binary data, which for Unix is quite normal and useful. For a console however not so useful. However PrintStream might be a design mishap/error: it has text support, also for passing a Charset; it is a half OutputStreamWriter.
So see PrintStream as an old unclean class doing what an OutputStreamWriter + BufferedWriter does, however not being a Writer.
BufferedWriter+OutputStreamWriter has the same complexity (though being reversed) as PrintStream. One also sees Scanner: new Scanner(System.in). This is not a Reader, and has superfluous support for tokenizing. It like PrintStream has the advantage of briefness, but is definitely more unclean for its unneeded overhead. (There are quite a lot of bugs mentioned in StackOverflow on Scanner.)