Java: "Final" System.Out, System.In and System.Err

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, and System.err are final static 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.

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



Leave a reply



Submit