Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/jpa/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 爪哇语:;“最后的”;System.out、System.in和System.err?_Java_Final - Fatal编程技术网

Java 爪哇语:;“最后的”;System.out、System.in和System.err?

Java 爪哇语:;“最后的”;System.out、System.in和System.err?,java,final,Java,Final,声明为公共静态最终打印流输出 但你可以打电话重新分配 嗯?如果是最终版,这怎么可能呢 (这一点同样适用于System.in和System.err) 更重要的是,如果您可以修改公共静态final字段,那么final提供的保证(如果有)意味着什么?(我从未意识到也从未期望System.in/out/err作为final变量)Java使用本机方法实现setIn()、setOut()和setErr() 在我的JDK1.6.020上,setOut()如下所示: public static void set

声明为
公共静态最终打印流输出

但你可以打电话重新分配

嗯?如果是
最终版
,这怎么可能呢

(这一点同样适用于
System.in
System.err


更重要的是,如果您可以修改公共静态final字段,那么
final
提供的保证(如果有)意味着什么?(我从未意识到也从未期望System.in/out/err作为
final
变量)

Java使用本机方法实现
setIn()
setOut()
setErr()

在我的JDK1.6.020上,
setOut()
如下所示:

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

...

private static native void setOut0(PrintStream out);

您仍然无法“正常”重新分配
final
变量,即使在这种情况下,您也不能直接重新分配字段(即,您仍然无法编译“
System.out=myOut
”)。本机方法允许一些您在常规Java中无法完成的事情,这解释了为什么本机方法有一些限制,例如要求对小程序进行签名才能使用本机库。

要扩展Adam所说的内容,以下是impl:

public static void setOut(PrintStream out) {
    checkIO();
    setOut0(out);
}
而setOut0定义为:

private static native void setOut0(PrintStream out);

取决于实施情况。最后一个可能永远不会更改,但它可以是实际输出流的代理/适配器/装饰器,例如,setOut可以设置out成员实际写入的成员。但实际上,它是本机设置的。

通常情况下,最终静态字段可能不会被修改。但是
System.in
System.out
System.err
是最终的静态字段,出于遗留原因,必须允许通过
System.setIn
System.setOut
System.setErr
方法更改这些字段。我们将这些字段称为写保护字段,以区别于普通的最终字段

编译器需要将这些字段与其他最终字段区别对待。例如,对普通最终字段的读取对同步“免疫”:锁或易失性读取中涉及的屏障不必影响从最终字段读取的值。由于写保护字段的值可能会发生变化,因此同步事件应该会对其产生影响。因此,语义要求将这些字段视为不能由用户代码更改的正常字段,除非该用户代码位于
系统
类中


顺便说一句,实际上,您可以通过对
setAccessible(true)
字段进行调用(或使用
Unsafe
方法),通过反射来变异
final
字段。Hibernate和其他框架等在反序列化过程中使用了这些技术,但它们有一个限制:在修改之前看到final字段值的代码不能保证在修改之后看到新值。有问题的字段的特殊之处在于,它们不受此限制,因为编译器以特殊方式处理它们。

在系统类中声明为final的
out
是类级变量。 其中,下面方法中的as out是一个局部变量。 我们不需要将类级别传递出去,这实际上是该方法的最终级别

上述方法的用法如下:

System.setOut(new PrintStream(new FileOutputStream("somefile.txt")));
现在数据将被转移到文件中。 希望这个解释有意义


因此,本机方法或反射在改变最后一个关键字的用途方面没有任何作用。

至于如何改变,我们可以看看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);
}

...

换句话说,JNI可以“欺骗”;)

我认为
setout0
正在修改本地级变量
out
,它不能修改类级变量
out

好的,所以它是纯Java语义的后门。。。你能回答我添加的问题的一部分吗,即如果你能重新分配流,
final
在这里真的有什么意义吗?它可能是final,所以你不能做类似System.out=new SomeOtherImp()的事情。但是您仍然可以使用本机方法使用setter,正如您上面看到的。我猜在这种情况下,对本机setIn0和setOut0方法的调用将真正修改最终变量的值,本机方法可能可以做到这一点。。。这就像在游戏中使用作弊代码:S@Danilo,是的,它确实修改:)@Jason,无法直接设置它需要在调用setIn/setErr时进行安全检查。一切都很公平。java确实修改最终字段的示例:java.util.Random(字段种子)可能FSM会以一种巧妙的方式为遗留代码祝福,因为它会损害未来的设计!>>此技术在反序列化过程中使用。
setAccessible(true)
仅适用于非
静态
字段,这使得它适用于帮助反序列化或克隆代码的任务,但无法更改
静态最终
字段。这就是为什么引用的文本以“通常情况下,最终静态字段不能修改”开头,指的是这些字段的
最终静态
性质和三个例外。实例字段的情况在另一个地方讨论。我想知道为什么它们不简单地去掉
final
修饰符;似乎比所有这些“写保护”的东西都简单。我很确定这不是一个突破性的改变。@Holger有一种方法可以改变静态的final字段(直到Java8)。公共类OnePrinter{private static final Integer ONE=1;公共静态void printOne(){System.out.println(ONE);}}},然后您可以使用字段z=OnePrinter.class.getDeclaredField(“ONE”);z、 setAccessible(true);字段f=Field.class.getDeclaredField(“修饰符”);int modifiers=z.getModifiers();f、 setAccessible(true);f、 集合(z,修饰符和~Modifier.FINAL);z、 set(null,2);OnePrinter.prin
/*
 * 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);
}

...