81

System.out is declared as public static final PrintStream out.

But you can call System.setOut() to reassign it.

Huh? How is this possible if it's final?

(same point applies to System.in and System.err)

And more importantly, if you can mutate the public static final fields, what does this mean as far as the guarantees (if any) that final gives you? (I never realized nor expected System.in/out/err behaved as final variables)

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
Jason S
  • 184,598
  • 164
  • 608
  • 970
  • 3
    Final fields don't enjoy a lot of benefits by the JVM itself, although they are strictly checked by the verifier. There are ways to modify final fields as well but not via standard java code (since it's a subject of the verifier). It's done via Unsafe and exposed in java via Field.set (needs accessible true), which compiles to the mentioned Unsafe stuff. Also JNI can do it, hence the JVM is not so keen at attempting to optimize... {perhaps I should've structure the comment as an answer but meh} – bestsss May 10 '11 at 15:27

7 Answers7

57

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.

Community
  • 1
  • 1
axtavt
  • 239,438
  • 41
  • 511
  • 482
  • 4
    May the FSM bless legacy code for the lovely way it compromises future design! – Sled May 10 '11 at 15:02
  • 1
    >>This technique is used during deserialization<< this is not true now, desereliazation uses Unsafe (faster) – bestsss May 10 '11 at 15:25
  • 1
    It’s important to understand that `setAccessible(true)` only works for non-`static` fields, which makes it suitable for the task of helping deserialization or cloning code, but there is no way to change `static final` fields. That’s why the cited text starts with “*Normally, final static fields may not be modified*”, referring to the `final static` nature of these fields and the three exceptions. The case of instance fields is discussed at another place. – Holger Jun 13 '16 at 13:16
  • I wonder why they didn't simply take off the `final` modifier; seems simpler than all this "write-protected" stuff. I'm pretty sure that's not a breaking change. – Mark VY Jan 09 '18 at 21:34
  • @Holger There is a way to change static final fields (up to Java 8). public class OnePrinter { private static final Integer ONE = 1; public static void printOne(){ System.out.println(ONE); } } and then you can Field z = OnePrinter.class.getDeclaredField("ONE"); z.setAccessible(true); Field f = Field.class.getDeclaredField("modifiers"); int modifiers = z.getModifiers(); f.setAccessible(true); f.set(z,modifiers & ~Modifier.FINAL); z.set(null, 2); OnePrinter.printOne(); – Peter Verhas May 31 '18 at 14:18
  • @PeterVerhas but this is completely off specification and may even break with JVM implementations where it appears to work at the first glance. The JVM’s optimizer is free to assume that such modifications never happen. – Holger May 31 '18 at 14:21
  • @MarkVY IMO the current requirement is to keep it final and indicate to new users that it should be considered not modifiable. Only "allow them to be modified" part is legacy - the set methods are legacy. – Teddy Sep 15 '18 at 12:03
  • Legacy? They provide important functionality. I think the "final" on these fields is arguably legacy, though I guess this way a security manager can at least intercept the calls. Maybe they should have been private, and we should be writing `System.out().println` – Mark VY Sep 15 '18 at 21:49
30

Java uses a native method to implement setIn(), setOut() and setErr().

On my JDK1.6.0_20, setOut() looks like this:

public static void setOut(PrintStream out) {
    checkIO();
    setOut0(out);
}

...

private static native void setOut0(PrintStream out);

You still can't "normally" reassign final variables, and even in this case, you aren't directly reassigning the field (i.e. you still can't compile "System.out = myOut"). Native methods allow some things that you simply can't do in regular Java, which explains why there are restrictions with native methods such as the requirement that an applet be signed in order to use native libraries.

Adam Batkin
  • 51,711
  • 9
  • 123
  • 115
  • 1
    OK, so it's a back door around the pure Java semantics... could you answer the part of the question I added, namely if you can reassign streams, does `final` actually have any meaning here? – Jason S May 10 '11 at 14:21
  • 1
    It's probably final so that one cannot do something like System.out = new SomeOtherImp(). But you can still use the setters using native approach as you see above. – Amir Raminfar May 10 '11 at 14:24
  • I guess in this case the calls to the native setIn0 and setOut0 methods will really modify the value of a final variable, native methods probably can do that... It's like using cheat codes in games :S – Danilo Tommasina May 10 '11 at 14:24
  • @Danilo, yeah it does modify :) – bestsss May 10 '11 at 16:30
  • @Jason, inability to directly set it requires security checks while calling setIn/setErr. all fair and square. Example where java does modify final fields: java.util.Random (field seed) – bestsss May 10 '11 at 16:31
6

To extend on what Adam said, here is the impl:

public static void setOut(PrintStream out) {
    checkIO();
    setOut0(out);
}

and setOut0 is defined as:

private static native void setOut0(PrintStream out);
Amir Raminfar
  • 33,777
  • 7
  • 93
  • 123
5

Depends on the implementation. The final one may never change but it could be a proxy/adapter/decorator for the actual output stream, setOut could for example set a member that the out member actually writes to. In practice however it is set natively.

vickirk
  • 3,979
  • 2
  • 22
  • 37
2

the out which is declared as final in System class is a class level variable. where as out which is in the below method is a local variable. we are no where passing the class level out which is actually a final one into this method

public static void setOut(PrintStream out) {
  checkIO();
  setOut0(out);
    }

usage of the above method is as below:

System.setOut(new PrintStream(new FileOutputStream("somefile.txt")));

now the data will be diverted to the file. hope this explanation makes the sense.

So no role of native methods or reflections here in changing purpose of the final keyword.

  • 2
    setOut0 is modifying the class variable, which is final. – fgb Jul 01 '14 at 12:58
  • @fgb as stated in the answer, out in setOut0 is not a class variable but an input parameter to the setOut and thus falls under local scope. – sankar Feb 23 '23 at 14:06
  • Got you @fgb. System.setOut(PrintStream out) is been called from several other places by passing System.out. – sankar Feb 23 '23 at 16:02
1

As far as how, we can take a look at the source code to java/lang/System.c:

/*
 * The following three functions implement setter methods for
 * java.lang.System.{in, out, err}. They are natively implemented
 * because they violate the semantics of the language (i.e. set final
 * variable).
 */
JNIEXPORT void JNICALL
Java_java_lang_System_setOut0(JNIEnv *env, jclass cla, jobject stream)
{
    jfieldID fid =
        (*env)->GetStaticFieldID(env,cla,"out","Ljava/io/PrintStream;");
    if (fid == 0)
        return;
    (*env)->SetStaticObjectField(env,cla,fid,stream);
}

...

In other words, JNI can "cheat". ; )

Radiodef
  • 37,180
  • 14
  • 90
  • 125
-1

I think setout0 is modifying local level variable out, it can't modify class level variable out.

itsmysterybox
  • 2,748
  • 3
  • 21
  • 26