本文翻译自:How does the “final” keyword in Java work? (I can still modify an object.)
In Java we use final
keyword with variables to specify its values are not to be changed. 在Java中,我们使用带有变量的final
关键字来指定其值不被更改。 But I see that you can change the value in the constructor / methods of the class. 但我发现你可以改变类的构造函数/方法中的值。 Again, if the variable is static
then it is a compilation error. 同样,如果变量是static
那么它就是编译错误。
Here is the code: 这是代码:
import java.util.ArrayList;
import java.util.List;
class Test {
private final List foo;
public Test()
{
foo = new ArrayList();
foo.add("foo"); // Modification-1
}
public static void main(String[] args)
{
Test t = new Test();
t.foo.add("bar"); // Modification-2
System.out.println("print - " + t.foo);
}
}
Above code works fine and no errors. 上面的代码工作正常,没有错误。
Now change the variable as static
: 现在将变量更改为static
:
private static final List foo;
Now it is a compilation error. 现在是编译错误。 How does this final
really work? 这个final
真的有用吗?
#1楼
参考:https://stackoom.com/question/13gaC/Java中的-final-关键字如何工作-我仍然可以修改一个对象
#2楼
You are always allowed to initialize a final
variable. 始终允许您初始化 final
变量。 The compiler makes sure that you can do it only once. 编译器确保您只能执行一次。
Note that calling methods on an object stored in a final
variable has nothing to do with the semantics of final
. 请注意,对存储在final
变量中的对象的调用方法与final
的语义无关。 In other words: final
is only about the reference itself, and not about the contents of the referenced object. 换句话说: final
只是关于引用本身,而不是引用对象的内容。
Java has no concept of object immutability; Java没有对象不变性的概念; this is achieved by carefully designing the object, and is a far-from-trivial endeavor. 这是通过精心设计物体来实现的,并且是一项非常重要的工作。
#3楼
- Since the final variable is non-static, it can be initialized in constructor. 由于final变量是非静态的,因此可以在构造函数中初始化。 But if you make it static it can not be initialized by constructor (because constructors are not static). 但是如果你使它静态,它就不能被构造函数初始化(因为构造函数不是静态的)。
- Addition to list is not expected to stop by making list final. 预计列表最终不会停止添加到列表中。
final
just binds the reference to particular object.final
只是绑定对特定对象的引用。 You are free to change the 'state' of that object, but not the object itself. 您可以自由更改该对象的“状态”,但不能更改对象本身。
#4楼
If you make foo
static, you must initialize it in the class constructor (or inline where you define it) like the following examples. 如果使foo
静态,则必须在类构造函数(或在其中定义它的内联)中初始化它,如下面的示例所示。
Class constructor (not instance): 类构造函数(不是实例):
private static final List foo;
static
{
foo = new ArrayList();
}
Inline: 排队:
private static final List foo = new ArrayList();
The problem here is not how the final
modifier works, but rather how the static
modifier works. 这里的问题不是final
修饰符的工作原理,而是static
修饰符的工作原理。
The final
modifier enforces an initialization of your reference by the time the call to your constructor completes (ie you must initialize it in the constructor). final
修饰符在对构造函数的调用完成时强制执行引用的初始化(即必须在构造函数中初始化它)。
When you initialize an attribute in-line, it gets initialized before the code you have defined for the constructor is run, so you get the following outcomes: 当您在线初始化属性时,它会在您为构造函数定义的代码运行之前初始化,因此您将获得以下结果:
- if
foo
isstatic
,foo = new ArrayList()
will be executed before thestatic{}
constructor you have defined for your class is executed 如果foo
是static
,则foo = new ArrayList()
将在为您的类定义的static{}
构造函数执行之前执行 - if
foo
is notstatic
,foo = new ArrayList()
will be executed before your constructor is run 如果foo
不是static
,则foo = new ArrayList()
将在构造函数运行之前执行
When you do not initilize an attribute in-line, the final
modifier enforces that you initialize it and that you must do so in the constructor. 如果不在线初始化属性,则final
修饰符会强制您初始化它,并且必须在构造函数中执行此操作。 If you also have a static
modifier, the constructor you will have to initialize the attribute in is the class' initialization block : static{}
. 如果你还有一个static
修饰符,那么你必须初始化属性的构造函数是类'initialization block: static{}
。
The error you get in your code is from the fact that static{}
is run when the class is loaded, before the time you instantiate an object of that class. 您在代码中得到的错误来自于在加载类时运行static{}
的事实,在实例化该类的对象之前。 Thus, you will have not initialized foo
when the class is created. 因此,在创建类时,您将没有初始化foo
。
Think of the static{}
block as a constructor for an object of type Class
. 可以将static{}
块看作Class
类型对象的构造函数。 This is where you must do the initialization of your static final
class attributes (if not done inline). 这是您必须初始化static final
类属性的地方(如果没有内联完成)。
Side note: 边注:
The final
modifier assures const-ness only for primitive types and references. final
修饰符仅为原始类型和引用确保常量。
When you declare a final
object, what you get is a final
reference to that object, but the object itself is not constant. 声明final
对象时,获得的是对该对象的final
引用 ,但对象本身不是常量。
What you are really achieving when declaring a final
attribute is that, once you declare an object for your specific purpose (like the final List
that you have declared), that and only that object will be used for that purpose: you will not be able to change List foo
to another List
, but you can still alter your List
by adding/removing items (the List
you are using will be the same, only with its contents altered). 在声明final
属性时,您真正实现的是,一旦您为特定目的声明了一个对象(比如您已声明的final List
),那么该对象将仅用于此目的:您将无法使用更改List foo
到另一个List
,但你仍然可以改变你的List
中添加/删除项目(该List
使用的是将是相同的,只有它的内容修改)。
#5楼
When you make it static final it should be initialized in a static initialization block 当你使它成为静态final时,它应该在静态初始化块中初始化
private static final List foo;
static {
foo = new ArrayList();
}
public Test()
{
// foo = new ArrayList();
foo.add("foo"); // Modification-1
}
#6楼
The final
keyword indicates that a variable may only be initialized once. final
关键字表示变量只能初始化一次。 In your code you are only performing one initialization of final so the terms are satisfied. 在您的代码中,您只执行一次final的初始化,以满足条款。 This statement performs the lone initialization of foo
. 该语句执行foo
的单独初始化。 Note that final
!= immutable, it only means that the reference cannot change. 注意, final
!= immutable,它只表示引用不能改变。
foo = new ArrayList();
When you declare foo
as static final
the variable must be initialized when the class is loaded and cannot rely on instantiation (aka call to constructor) to initialize foo
since static fields must be available without an instance of a class. 当你将foo
声明为static final
,必须在加载类时初始化变量,并且不能依赖实例化(也就是调用构造函数)来初始化foo
因为静态字段必须在没有类实例的情况下可用。 There is no guarantee that the constructor will have been called prior to using the static field. 无法保证在使用静态字段之前调用构造函数。
When you execute your method under the static final
scenario the Test
class is loaded prior to instantiating t
at this time there is no instantiation of foo
meaning it has not been initialized so foo
is set to the default for all objects which is null
. 当你在static final
场景下执行你的方法时,在实例化t
之前加载Test
类,此时没有foo
实例化意味着它还没有被初始化,所以foo
被设置为所有null
对象的默认null
。 At this point I assume your code throws a NullPointerException
when you attempt to add an item to the list. 此时,我假设当您尝试将项添加到列表时,您的代码会抛出NullPointerException
。