System.out
se declara como public static final PrintStream out
.
Pero puedes llamar System.setOut()
para reasignarlo.
¿Eh? ¿Cómo es esto posible si lo es final
?
(el mismo punto se aplica a System.in
y System.err
)
Y lo que es más importante, si puede mutar los campos finales estáticos públicos, ¿qué significa esto en cuanto a las garantías (si las hay) que final
le brinda? (Nunca me di cuenta ni esperaba que System.in/out/err se comportara como final
variables)
Respuestas:
Campos protegidos contra escritura de JLS 17.5.4 :
Por cierto, en realidad puede mutar
final
campos a través de la reflexión llamándolossetAccessible(true)
(o usandoUnsafe
métodos). Tales técnicas se utilizan durante la deserialización, por Hibernate y otros marcos, etc., pero tienen una limitación: el código que ha visto el valor del campo final antes de la modificación no está garantizado para ver el nuevo valor después de la modificación. Lo especial de los campos en cuestión es que están libres de esta limitación, ya que el compilador los trata de manera especial.fuente
setAccessible(true)
solo funciona para los que no sonstatic
campos, lo que lo hace adecuado para la tarea de ayudar a la deserialización o clonación del código, pero no hay forma de cambiar losstatic final
campos. Es por eso que el texto citado comienza con “ Normalmente, los campos estáticos finales no se pueden modificar ”, refiriéndose a lafinal static
naturaleza de estos campos y las tres excepciones. El caso de los campos de instancia se analiza en otro lugar.final
modificador; parece más simple que todas estas cosas "protegidas contra escritura". Estoy bastante seguro de que no es un cambio rotundo.Java utiliza un método nativo para poner en práctica
setIn()
,setOut()
ysetErr()
.En mi JDK1.6.0_20, se
setOut()
ve así:public static void setOut(PrintStream out) { checkIO(); setOut0(out); } ... private static native void setOut0(PrintStream out);
Aún no puede reasignar
final
variables "normalmente" , e incluso en este caso, no está reasignando directamente el campo (es decir, aún no puede compilar "System.out = myOut
"). Los métodos nativos permiten algunas cosas que simplemente no puede hacer en Java normal, lo que explica por qué existen restricciones con los métodos nativos, como el requisito de que se firme un subprograma para poder usar bibliotecas nativas.fuente
final
realidad tiene algún significado aquí?Para ampliar lo que dijo Adam, aquí está el impl:
public static void setOut(PrintStream out) { checkIO(); setOut0(out); }
y setOut0 se define como:
private static native void setOut0(PrintStream out);
fuente
Depende de la implementación. Es posible que el último nunca cambie, pero podría ser un proxy / adaptador / decorador para el flujo de salida real, setOut podría, por ejemplo, establecer un miembro en el que el miembro de salida realmente escribe. En la práctica, sin embargo, se configura de forma nativa.
fuente
el
out
que se declara como final en la clase System es una variable de nivel de clase. donde como el que está en el método siguiente es una variable local. no estamos pasando el nivel de la clase, que en realidad es el último en este métodoEl uso del método anterior es el siguiente:
System.setOut(new PrintStream(new FileOutputStream("somefile.txt")));
ahora los datos se desviarán al archivo. Espero que esta explicación tenga sentido.
Entonces, no hay papel de los métodos nativos o reflexiones aquí para cambiar el propósito de la palabra clave final.
fuente
En cuanto a cómo, podemos echar un vistazo al código fuente para
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); } ...
En otras palabras, JNI puede "hacer trampa". ; )
fuente
Creo que
setout0
está modificando la variable de nivel localout
, no puede modificar la variable de nivel de claseout
.fuente