至少可以将方法参数上的final关键字视为Java编译器的指示符,表明该参数不能重新分配给另一个引用。 Java参数处理始终是“按值调用” (是的,即使在处理对象时也是如此),这是为什么。这是真的。当处理非原始数据类型时,Java处理对对象的引用。 对象本身不会从被调用者传递给目标函数! 而是传递一个指向所需对象的引用。 但是,此引用不等同于被调用方,因为它只是一个副本。 传递给函数的是将复制的引用作为值-好的,每个人都还在吗? :-)也许Java应该使用更匹配的解释“按复制参考调用作为值” 。
总结一下:
Java仅以“按值调用”样式传递所有方法参数 (原始数据类型或对对象的引用)!
为了证明这一点,让我们看一下下面的演示代码及其输出。
/**
* Call by Value Test Application.
*
* @author Christopher Meyer
* @version 0.1
* Apr 21, 2012
*/
public class CBVTest {
public static void main(String[] args) {
Integer mainInternInteger = new Integer(1);
/*
* Even references are copied during calls!
*
* Explanation Objects are never passed, only references to them, BUT
* references are copied! So only reference COPIES reach the method.
* Neither changes to the reference inside/outside the method will
* influence the counterpart.
*
* Maybe it should be called "Call by Copied Reference as Value".
*/
class RunMe implements Runnable {
Integer runnerInternInteger;
public RunMe(Integer i) {
runnerInternInteger = i;
/*
* The following operation will have no effect on the main
* thread, since the reference to "i" is a copied one.
* Interfacing the "caller" reference is prevented.
*/
i = new Integer(3);
}
@Override
public void run() {
while (true) {
System.out.println(runnerInternInteger.intValue()
+ "\t (runner intern value)");
}
}
}
Thread runner = new Thread(new RunMe(mainInternInteger));
runner.start();
// Create a new object and assign it to "mainInternInteger".
mainInternInteger = new Integer(2);
while (true) {
System.out.println(
mainInternInteger.intValue() + "\t (main intern value)");
}
}
}
代码的输出如下所示:
...
2 (main intern value)
2 (main intern value)
2 (main intern value)
2 (main intern value)
1 (runner intern value)
2 (main intern value)
1 (runner intern value)
2 (main intern value)
1 (runner intern value)
2 (main intern value)
1 (runner intern value)
1 (runner intern value)
1 (runner intern value)
1 (runner intern value)
1 (runner intern value)
...
因此,对已处理参数的赋值(i = new Integer(3))或从调用类进行的重新赋值(mainInternInteger = new Integer(2))都不会相互影响。
那么,如果没有必要,那又有什么价值呢?
如果添加到RunMe的构造方法中(公共RunMe(final Integer i)),则重新分配i = new Integer(3)会引发异常:线程“ main”中的异常java.lang.RuntimeException:无法编译的源代码–最终参数我可能不会被分配。 它可以防止因意外重新分配而导致的故障。 意外分配给已处理的参数将始终失败! 最终迫使开发人员生成准确的代码。
final关键字不是方法签名的一部分。 因此,无论是否声明为final,编译后的代码都是相同的(每个人都可以使用diff轻松检查)。 这意味着,一旦声明了方法参数,就不会重载该方法的参数,而一旦声明则不会。 由于字节码保持相同,因此对性能也绝对没有影响。
更令人困惑的是,请记住,在可以修改变量时(例如,在处理线程的匿名内部类时,如果您不清楚在同一上下文中考虑多个变量,则内部类需要定义一个变量final)。可以更改的相同名称)。
参考:在Java安全和相关主题博客上 ,使用我们JCG合作伙伴 Christopher Meyer的方法参数上的final关键字 。
翻译自: https://www.javacodegeeks.com/2012/04/using-final-keyword-on-method.html