Java Syntax Legends: System.out
By Shimi Bandiel, Solution Architect @ JFrog
January 5, 2022
4min read

There is a story I want to share with you today that underscores an important principle about seemingly inconsequential decisions. It shows how small design decisions can one day have a huge impact and one possible outcome of technical debt.I’m sure all of you Java programmers out there are well familiar withSystem.out.
It was there since the inception of Java and has been used heavily since day one.At inception, theSystem.out,System.in,System.errwere regular mutable fields (you could redirect the standard input/output/error).Shortly after its inception, one of the popular use-cases for Java wasApplets(good riddance) that were used for rich web applications.
Java introduced a sandbox model,SecurityManager,让那些Appletsto safely be executed on clients’ machines essentially preventing any dangerous operation from taking place.
Some of those “dangerous operations” were assignments toSystem.out,System.err,andSystem.in.
In order for theSecurityManagerto check the operation, amethodto invoke was required. Hence cameSystem.setOut(),System.setErr(),System.setIn(). In these methods, theSecurityManagerwould check for authorized access.However, and this is the design issue, the Java authors didn’t encapsulate the field itself! They still provided access directly to the field for reading (they didn’t introducegetOut(), getErr(), getIn()).
也许这是由于to backward compatibility, or becausegetOut()is a bit too much.In order to prevent direct modification to the fields, they made the fieldfinaland in the setter methods, used Reflection to modify the field.
Doesn’t sound too bad, right?
About 6 years went by (versions 1.2, 1.3 and 1.4)
Then came version 5. Finally Java got a proper memory model (JSR 133).
Now, in thespecification, they finally enforced thatfinalmembers are thread-safe and you don’t have to synchronize access to them.
Makes sense, right?
Wait, what about the final members:System.in/out/err?
他们是final, however, they are not safe (due to mutation by Reflection) and they are part of the standard library. What can be done?
If you go through the Javaspecification和阅读关于Java内存模型的一部分,你’ll see that those three fields are exempted from the thread-safety guarantee offinalfields:
“Normally, a field that is final and static may not be modified. However, System.in, System.out, and System.err are static final fields that, for legacy reasons, must be allowed to be changed by the methods System.setIn, System.setOut, and System.setErr. We refer to these fields as being write-protected to distinguish them from ordinary final fields.”
In short, when accessing these fields, you should use a synchronization mechanism.
Now, not only are most Java developers unaware of this fact, but also bugs might be introduced that are extremely hard to track down. For example, you might reference System.out and the value will not be refreshed after another thread redirects it to a file.
The moral of the story is that a small technical debt/bad design decision (not encapsulating a field properly) might (several years later) result in an exemption in the actual language specification. Exemptions like this can lead to unexpected behavior!