This program appears to do the usual thing in an unusual way.
What does it print?
public class Greeter { public static void main (String[] args) { String greeting = "Hello world"; for (int i = 0; i < greeting.length(); i++) System.out.write(greeting.charAt(i)); } }
Solution 81: Charred Beyond Recognition
Although it's a
bit strange, there is little reason to suspect that this program should
misbehave. It writes "Hello world" to System.out, one
character at a time. You may be aware that the write method uses only
the low-order byte of its input parameter. This would cause trouble if
"Hello world" contained any exotic characters, but it doesn't: It
consists entirely of ASCII characters. Whether you print it one character at a
time or all at once, the result should be the same: The program should print
Hello world. Yet, if you ran it, it almost certainly printed nothing.
Where did the greeting go? Perhaps the program just wasn't feeling all that
cheerful?
The problem is that System.out is buffered. The
characters in Hello world were written to the buffer for
System.out, but the buffer was never flushed. Most programmers believe
that System.out and System.err flush themselves automatically
whenever output is performed. This is almost true but not quite. They are of
type PrintStream, whose documentation says, as of release 5.0 [Java-API]:
A PrintStream can be created so as to flush automatically; this means that the flush method is automatically invoked after a byte array is written, one of the println methods is invoked, or a newline character or byte ('\n') is written.
The streams referenced by System.out and
System.err are indeed instances of the automatically flushing variant
of PrintStream, but no mention is made of the write(int)
method in the above documentation. The documentation for write(int)
says: "Write the specified byte to this stream. If the byte is
a newline and automatic flushing is enabled then
the flush method will be invoked" [Java-API]. In practice, write(int) is the only output method that does not flush a
PrintStream on which automatic flushing is enabled.
Curiously, if the program is modified to use
print(char) instead of write(int), it flushes
System.out and prints Hello world. This behavior contradicts
the documentation for print(char), which says [Java-API]:
Print a character. The character is translated into one or more bytes according to the platform's default character encoding, and these bytes are written in exactly the manner of the write(int) method.
Similarly, if the program is modified to use
print(String), it flushes the stream even though the documentation
prohibits it. The documentation should almost certainly be changed to describe
the actual behavior; it would be too destabilizing to change the behavior.
The simplest change that fixes the
program is to add a System.out.flush invocation after the loop. If this
change is made, the program prints Hello world as expected. It would,
however, be far better to rewrite the program to use the more familiar
System.out.println idiom for producing output on the console.
The lesson of this program is, as in Puzzle 23: Use familiar idioms whenever possible; if you must stray
from familiar APIs, be sure to consult the documentation. There are three
lessons for API designers: Make the behavior of your methods clear from their
names, document this behavior clearly, and correctly implement the documented
behavior.
No comments:
Post a Comment
Your comments are welcome!