停止变量的重新分配
虽然这些答案在智力上很有趣,但我还没有读过简短的答案:
如果希望编译器阻止将变量重新分配给其他对象,请使用关键字 final。
无论变量是静态变量、成员变量、局部变量还是参数/参数变量,效果都是完全相同的。
例
让我们看看效果的实际效果。
考虑这个简单的方法,其中两个变量(arg和x)都可以重新分配不同的对象。
// Example use of this method:
// this.doSomething( "tiger" );
void doSomething( String arg ) {
String x = arg; // Both variables now point to the same String object.
x = "elephant"; // This variable now points to a different String object.
arg = "giraffe"; // Ditto. Now neither variable points to the original passed String.
}
将局部变量标记为最终变量。这会导致编译器错误。
void doSomething( String arg ) {
final String x = arg; // Mark variable as 'final'.
x = "elephant"; // Compiler error: The final local variable x cannot be assigned.
arg = "giraffe";
}
相反,让我们将参数变量标记为 final。这也会导致编译器错误。
void doSomething( final String arg ) { // Mark argument as 'final'.
String x = arg;
x = "elephant";
arg = "giraffe"; // Compiler error: The passed argument variable arg cannot be re-assigned to another object.
}
故事的寓意:
如果要确保变量始终指向同一对象,请将该变量标记为 final。
从不重新分配参数
作为良好的编程实践(在任何语言中),切勿将参数/参数变量重新分配给调用方法传递的对象以外的对象。在上面的示例中,永远不应该写行 。由于人类会犯错误,而程序员也是人类,让我们请编译器来协助我们。将每个参数/参数变量标记为“final”,以便编译器可以找到并标记任何此类重新赋值。arg =
回顾
如其他答案所述...鉴于Java最初的设计目标是帮助程序员避免愚蠢的错误,例如读取数组的末尾,Java应该被设计为自动将所有参数/参数变量强制为“final”。换句话说,参数不应该是变量。但事后看来是20/20的愿景,Java设计师当时已经全力以赴。
那么,总是添加到所有参数中?final
我们是否应该添加到每个要声明的方法参数中?final
- 从理论上讲,是的。
- 在实践中,no.
➥ 仅当方法的代码很长或很复杂时才添加,其中参数可能被误认为是局部变量或成员变量,并可能重新分配。final
如果您接受从不重新分配参数的做法,您将倾向于为每个参数添加一个。但这很乏味,使声明更难阅读。final
对于参数显然是参数的简短代码,而不是局部变量或成员变量,我懒得添加 .如果代码非常明显,我和任何其他程序员都不可能进行维护或重构,意外地将参数变量误认为不是参数,那么请不要打扰。在我自己的工作中,我只添加更长或更复杂的代码,其中参数可能被误认为是局部或成员变量。final
final
为完整性添加了#Another案例
public class MyClass {
private int x;
//getters and setters
}
void doSomething( final MyClass arg ) { // Mark argument as 'final'.
arg = new MyClass(); // Compiler error: The passed argument variable arg cannot be re-assigned to another object.
arg.setX(20); // allowed
// We can re-assign properties of argument which is marked as final
}
record
Java 16 带来了新的记录功能。记录是定义类的一种非常简短的方法,其中心目的只是以不可变和透明的方式携带数据。
您只需声明类名及其成员字段的名称和类型。编译器隐式提供构造函数、getters 和 。equals
hashCode
toString
这些字段是只读的,没有 setter。因此,a是一种不需要标记参数的情况。它们实际上已经是最终的。实际上,编译器在声明记录的字段时禁止使用。record
final
final
public record Employee( String name , LocalDate whenHired ) //