java: final System.out, System.in and System.err?
JLS 17.5.4 Write Protected Fields:
Normally, final static fields may not be modified. However
System.in
,System.out
, andSystem.err
are final static fields that, for legacy reasons, must be allowed to be changed by the methodsSystem.setIn
,System.setOut
andSystem.setErr
. We refer to these fields as being write-protected to distinguish them from ordinary final fields.The compiler needs to treat these fields differently from other final fields. For example, a read of an ordinary final field is "immune" to synchronization: the barrier involved in a lock or volatile read does not have to affect what value is read from a final field. Since the value of write-protected fields may be seen to change, synchronization events should have an effect on them. Therefore, the semantics dictate that these fields be treated as normal fields that cannot be changed by user code, unless that user code is in the
System
class.
By the way, actually you can mutate final
fields via reflection by calling setAccessible(true)
on them (or by using Unsafe
methods). Such techniques are used during deserialization, by Hibernate and other frameworks, etc, but they have one limitation: code that have seen value of final field before modification is not guaranteed to see the new value after modification. What's special about the fields in question is that they are free of this limitation since they are treated in special way by the compiler.
How is the final variable System.out changing its reference?
Take a look on source code of java.lang.System
:
public static void setOut(PrintStream out) {
checkIO();
setOut0(out);
}
The job is done by setOut0(out)
:
private static native void setOut0(PrintStream out);
This method is native
. It can do whatever it wants including changing of value of final
fields.
BTW theoretically it can be done using pure java as well. They could use wrapper PrintStream
to initialize System.out
. In this case setOut()
could go to the wrapper and change object that it wraps.
System.out is declared as static final and initialized with null?
It is initialized with native code in a static initializer.
At the top of System.java you have:
/* register the natives via the static initializer.
*
* VM will invoke the initializeSystemClass method to complete
* the initialization for this class separated from clinit.
* Note that to use properties set by the VM, see the constraints
* described in the initializeSystemClass method.
*/
private static native void registerNatives();
static {
registerNatives();
}
The registerNatives()
method will initialize in/out/err - and it's doing so in native code - native code can pretty much do whatever it want and are not limited to all of the java language rules. (Though you could get around setting an already initialized final field in Java via reflection too)
Random printing order for System.out & System.err calls
I believe this is because you are writing to two different outputs (one is standard out and the other standard error). These are probably handled by two different threads at runtime to allow writing to both during java execution. Assuming this is the case, the cpu task scheduler is not going to execute the threads in the same order every time.
You should never get this functionality if all of your output is going to the same output stream (ie everything goes to standard out or everything goes to standard err). You will never be guaranteed execution order of standard error vs standard output.
Preventing the JVM from writing to System.out or reading from System.in
The answer of Elliott Frisch will do the trick, but you cant receive output after doing it. If you intend to have output muted just temporarily, use this code:
public class RedirectedOutput {
public static void main(String[] args) {
// Save the old output stream to have the chance to set it back later
InputStream standardIn = System.in;
PrintStream standardOut = System.out;
PrintStream standardErr = System.err;
// Set useless streams
System.setIn(new ByteArrayInputStream(new byte[0]));
System.setOut(new PrintStream(new ByteArrayOutputStream()));
System.setErr(new PrintStream(new ByteArrayOutputStream()));
// Will not be shown
System.out.println("Hello World");
// Now set back the old streams to have output again
System.setIn(standardIn);
System.setOut(standardOut);
System.setErr(standardErr);
// Will be shown again
System.out.println("Finally we got the Hello");
}
}
You can try out this example which should be fully runnable. It should be self-explaining. You are using setIn()
, setOut()
and setErr()
to change the streams and you save it before to be able to set it back.
But if you don't need any output, you are better with Elliott's code because I guess it's better for resources.
System.out.println and System.err.println out of order
They are different streams and are flushed at different times.
If you put
System.out.flush();
System.err.flush();
inside your loop, it will work as expected.
To clarify, output streams are cached so all the write goes into this memory buffer. After a period of quiet, they are actually written out.
You write to two buffers, then after a period of inactivity they both are flushed (one after the other).
System.out.println/System.err.println not working
It's a common occurrence that a file is missing some contents if you don't close the file after writing. There are finalizer()
methods in many OutputStream
subclasses that do close the stream, but they don't always have a chance to get called as is the case here.
The main thread releases the gate and quits immediately, the other threads quickly run their course and at that point the VM is closed and you can't trust that things finished correctly.
The original code is convoluted and it's hard to "fix" something that's broken from the ground up. For instance in real code you wouldn't have 2 threads competing to write into the same file. That just doesn't make sense.
In any case the best "fix" for this would be for the main thread to wait for the other threads to finish (instead of sleeping), and then close the file as below.
Thread t1 = new Thread(() -> ...
Thread t2 = new Thread(() -> ...
t1.start();
t2.start();
t1.join();
t2.join();
out.close();
System.out, System.err : often temporary debugging statements warning
If your application will be updated and/or will become a part of another more complex system, these System.out
and System.err
calls will become less convenient than a properly implemented logging subsystem in your application.
And it will be necessary to rewrite these parts of code to provide the more consistent logging picture for an administrator/end-user.
Related Topics
Persistencecontext Entitymanager Injection Nullpointerexception
How to Accept Date Params in a Get Request to Spring MVC Controller
How to Call the Overridden Method of a Superclass
Is There an Upper Bound to Biginteger
Java Comparator Class to Sort Arrays
Missing Artifact Com.Sun:Tools:Jar
How to Find a Whole Word in a String in Java
Java.Lang.Illegalaccesserror: Tried to Access Method
When to Close Connection, Statement, Preparedstatement and Resultset in Jdbc
Why Is It Not a Good Practice to Synchronize on Boolean
Java Regex Replace with Capturing Group
How to Replace a Character in a String in Java
_ (Underscore) Is a Reserved Keyword