1

When looking into the implementation of the java.lang.System class, I find this:

public final static PrintStream out = null;

From that definition alone, I can tell that the value of System.out will always be null. However, it is initialized when the program starts (with the System.initializeSystemClass() method auto-invoked by the JVM), and I can change its value using System.setOut, which performs a security check, then delegates the call to the setOut0 method which is a native method that changes the value of System.out. Why is it that I can change the value of System.out even though it is declared final?

Roman C
  • 49,761
  • 33
  • 66
  • 176
gparyani
  • 1,958
  • 3
  • 26
  • 39
  • 1
    Check out this thread: http://stackoverflow.com/questions/15565993/strange-out-variable-system-out-println – amb110395 Aug 14 '13 at 21:50
  • @amb110395 That question is asking why the `nullPrintStream()` method is needed (it is a workaround for a bug in older releases). I'm asking why can the value of a final field can be changed by native code (i.e. how it is able to do it). – gparyani Aug 14 '13 at 21:54
  • You can change final fields with reflection, too! – Louis Wasserman Aug 14 '13 at 21:54
  • @gprayani Correct me if I'm wrong, but PrintStream is still being set to null, so isn't it the same case as your question? – amb110395 Aug 14 '13 at 21:58
  • 1
    Are you wanting to know why it's permissible to change the field, or how the change is actually implemented? – chrylis -cautiouslyoptimistic- Aug 14 '13 at 22:00
  • @amb110395 The question and its "duplicate" give the same info, but they have different ending questions. – gparyani Aug 14 '13 at 22:00
  • @chrylis I want to know how the change is implemented. – gparyani Aug 14 '13 at 22:00
  • `I'm asking how can the value of a final field can be changed by native code.` As long as value is not primitive type you don't even need to use native code. [You can do it with Java reflection](http://stackoverflow.com/questions/3301635/change-private-static-final-field-using-java-reflection) – Pshemo Aug 14 '13 at 22:22
  • @Pshemo Yes, you CAN do it using reflection, but the method that changes it is `native`, and I want to know how that works. – gparyani Aug 15 '13 at 00:08

2 Answers2

6

This is a historical wart in the API. The JLS actually special-cases System.{in,out,err}, and if the API were being designed today, there would probably be a different declaration.

This odd behavior is implemented in practice by ignoring the Java definition of System. System is one of the core classes (along with Runtime, Object, the system ClassLoader, and a few others) that are a Java program's interface to the environment outside the JVM. In order to perform their tasks, they have to be supplied in (mostly) native code by the JRE, and in this case the actual C code for System just ignores the fact that the Java API declares the field final.

chrylis -cautiouslyoptimistic-
  • 75,269
  • 21
  • 115
  • 152
  • Huh. That's kinda funny. You might want to add this gem to one of the answers over at http://stackoverflow.com/questions/15565993/strange-out-variable-system-out-println, too. – Jason C Aug 14 '13 at 21:57
  • I don't know where I'd add it; the question there is about the traditional (pre-7) fake method. As of 7, they "fixed" the problem by moving the wart out of the JRE into the JLS. – chrylis -cautiouslyoptimistic- Aug 14 '13 at 21:59
  • Good point. I do like the quote from the accepted answer: "The JDK is like sausages, it's best not to see it being made" – Jason C Aug 14 '13 at 22:03
  • Ah, but if it ignores that it's final, wouldn't an error be induced in the code, since it tries to modify the variable declared `final`? – gparyani Aug 14 '13 at 22:12
  • 1
    You say "it", but you're not being clear. *Java* code can't modify the variable (at least not directly). *Native* code can twiddle whatever it wants, because the JVM's memory protection doesn't apply to it. – chrylis -cautiouslyoptimistic- Aug 14 '13 at 22:13
  • 2
    Basically, `final` fields can be defined as "fields that can't be modified (except by reflection (unless the security manager prevents it)) (except for `System.in/out/err`, which can be changed only via `System.setIn/setOut/setErr`). The edge cases usually get glossed, so that the definition becomes a more intuitive "fields that can't be modified." – yshavit Aug 14 '13 at 22:34
  • 2
    @yshavit The exception for the system streams wasn't actually made in the JLS until Java 7, so before that the existence of the stream setters was technically a violation of the spec rather than an exception in the definition of `final`! – chrylis -cautiouslyoptimistic- Aug 14 '13 at 22:39
  • @yshavit And except for JNI, and generated byte-code . – Jason C Aug 15 '13 at 00:52
1

I'm asking how can the value of a final field can be changed by native code.

It works the same way any other bit of native code changes a fields value. Modifications to a final variable are detected at compile time, by the compiler. There are no runtime checks for it aside from explicit checks initiated by a call to Field.set(). At no point is any runtime memory protection or anything else involved, and even if it was, it would be done by the JVM, not the operating system. Native code doesn't have to care about meta data on fields, it can just set bytes in memory. It's fully outside the scope of the Java compiler's compile time checks.

Native code modifications to final fields may not even be visible throughout the entire program because of optimizations the compiler is allowed to perform on final fields. That's what that nullPrintStream() bit was about pre Java 7.

Jason C
  • 38,729
  • 14
  • 126
  • 182