Java中的“final”关键字如何工作? (我仍然可以修改一个对象。)

本文翻译自: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楼

  1. 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). 但是如果你使它静态,它就不能被构造函数初始化(因为构造函数不是静态的)。
  2. 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 is static , foo = new ArrayList() will be executed before the static{} constructor you have defined for your class is executed 如果foostatic ,则foo = new ArrayList()将在为您的类定义的static{}构造函数执行之前执行
  • if foo is not static , 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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值