停止变量的重新赋值
虽然这些答案在理论上很有趣,但我还没有读到简短的简单答案:
当你希望编译器阻止变量被重新赋值为不同的对象时,使用关键字final。
无论变量是静态变量、成员变量、局部变量还是参数变量,效果完全相同。
示例
让我们看看实际效果。
考虑这个简单的方法,在这个方法中,两个变量(arg和x)都可以被重新赋值为不同的对象。
void doSomething( String arg ) {
String x = arg;
x = "elephant";
arg = "giraffe";
}
将局部变量标记为
final。这会导致编译器错误。
void doSomething( String arg ) {
final String x = arg;
x = "elephant";
arg = "giraffe";
}
相反,让我们将参数变量标记为
final。这也会导致编译器错误。
void doSomething( final String arg ) {
String x = arg;
x = "elephant";
arg = "giraffe";
}
故事寓意:
如果你想确保一个变量始终指向同一个对象,就将该变量标记为“final”。
永远不要重新分配参数
作为良好的编程实践(无论是哪种语言),你永远不应该将参数/参数变量重新分配给除调用方法传递的对象之外的其他对象。在上面的例子中,永远不应该写下这行代码“arg =”。由于人会犯错误,而程序员也是人,让我们请求编译器来帮助我们。将每个参数/参数变量标记为“final”,以便编译器可以找到并标记任何此类重新分配。
回顾一下
正如其他答案中所指出的...
鉴于Java最初的设计目标是帮助程序员避免愚蠢的错误,例如读取数组末尾之后的内容,Java本应自动将所有参数/参数变量强制为“final”。换句话说,参数不应该是变量。但是事后诸葛亮,当时的Java设计师手头已经很忙了。
所以,每个参数都要添加final吗?
我们应该将final添加到每个声明的方法参数吗?
理论上是的。
实践中不是这样的。只有当方法的代码很长或复杂时,参数可能会被误认为是局部变量或成员变量并重新赋值时,才需要添加final。
如果你坚持不重新分配参数的做法,你会倾向于为每个参数添加final。但这很繁琐,也使声明变得更难阅读。
对于简短的简单代码,参数显然是一个参数,而不是局部变量或成员变量,我不会费心添加final。如果代码非常明显,没有机会让我或其他程序员在维护或重构时错误地将参数变量误认为是其他东西,那就不必费心了。在我的工作中,我只在更长或更复杂的代码中添加final,以防参数被误认为局部变量或成员变量。
#为了完整性添加的另一种情况
public class MyClass {
private int x;
}
void doSomething( final MyClass arg ) {
arg = new MyClass();
arg.setX(20);
}
记录
Java 16引入了新的记录功能。记录是一种非常简洁的方式来定义一个类,其主要目的是仅仅用于携带数据,以不可变和透明的方式。
您只需声明类名以及其成员字段的名称和类型。编译器隐式提供构造函数、getter方法、equals
和hashCode
方法以及toString
方法。
这些字段是只读的,没有setter方法。因此,记录
是一种情况,不需要将参数标记为final
。它们已经是有效的final。实际上,编译器禁止在声明记录的字段时使用final
关键字。
public record Employee( String name , LocalDate whenHired )
{
}
如果您提供一个可选的构造函数,那里您可以标记为final。
public record Employee(String name , LocalDate whenHired)
{
public Employee ( final String name , final LocalDate whenHired )
{
this.name = name;
whenHired = LocalDate.MIN;
this.whenHired = whenHired;
}
}
int foo(int* bar)
,最后一个是int foo(int* &bar)
。后者是通过引用传递指针,前者是通过值传递引用。 - jerslan