在Java中,我们使用带有变量的final关键字来指定其不被更改的值。 但我看到你可以改变类的构造函数/方法的值。同样,如果变量是静态的,那么这是一个编译错误。

代码如下:

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);
  }
}

以上代码工作正常,没有错误。

现在将变量更改为静态:

private static final List foo;

现在它是一个编译错误。期末考试是怎么进行的呢?


当前回答

首先,在你的代码中初始化(即第一次赋值)foo的地方在这里:

foo = new ArrayList();

foo是一个对象(类型为List),因此它是一个引用类型,而不是一个值类型(如int)。因此,它持有对存储List元素的内存位置(例如0xA7D2A834)的引用。像这样的线

foo.add("foo"); // Modification-1

不要更改foo的值(同样,它只是一个对内存位置的引用)。相反,它们只是在引用的内存位置中添加元素。要违反final关键字,你必须尝试重新分配foo,如下所示:

foo = new ArrayList();

这将导致编译错误。


现在,考虑一下当您添加static关键字时会发生什么。

当你没有static关键字时,实例化类的每个对象都有自己的foo副本。因此,构造函数将一个值赋给foo变量的一个空白的新副本,这是完全没问题的。

然而,当你有static关键字时,内存中只有一个foo与类相关联。如果要创建两个或多个对象,构造函数每次都会尝试重新分配那个foo,这违反了final关键字。

其他回答

当你让它成为静态final时,它应该在静态初始化块中初始化

    private static final List foo;

    static {
        foo = new ArrayList();
    }

    public Test()
    {
//      foo = new ArrayList();
        foo.add("foo"); // Modification-1
    }

以上都是正确的。此外,如果您不希望其他人从您的类中创建子类,则将您的类声明为final。然后它就变成了类树层次结构的叶级,没有人可以进一步扩展它。避免庞大的类层次结构是一个很好的实践。

首先,在你的代码中初始化(即第一次赋值)foo的地方在这里:

foo = new ArrayList();

foo是一个对象(类型为List),因此它是一个引用类型,而不是一个值类型(如int)。因此,它持有对存储List元素的内存位置(例如0xA7D2A834)的引用。像这样的线

foo.add("foo"); // Modification-1

不要更改foo的值(同样,它只是一个对内存位置的引用)。相反,它们只是在引用的内存位置中添加元素。要违反final关键字,你必须尝试重新分配foo,如下所示:

foo = new ArrayList();

这将导致编译错误。


现在,考虑一下当您添加static关键字时会发生什么。

当你没有static关键字时,实例化类的每个对象都有自己的foo副本。因此,构造函数将一个值赋给foo变量的一个空白的新副本,这是完全没问题的。

然而,当你有static关键字时,内存中只有一个foo与类相关联。如果要创建两个或多个对象,构造函数每次都会尝试重新分配那个foo,这违反了final关键字。

final关键字可以有两种不同的解释方式,这取决于它的使用对象:

值类型:对于int型,double型等,它将确保值不能改变,

引用类型:对于对象的引用,final确保引用永远不会改变,这意味着它总是引用同一个对象。它不能保证对象中被引用的值保持不变。

因此,final List<Whatever> foo;确保foo总是指向相同的列表,但该列表的内容可能会随着时间的推移而改变。

我只能回答你的问题,在这种情况下,你不能改变foo的参考值。你只需要在同一个引用中放入值,这就是为什么你可以在foo引用中添加值。这个问题发生在你不能很好地理解参考值和原始值之间的区别。引用值也是在堆内存中存储对象地址(即value)的值。

public static void main(String[] args) 
  {
      Test t = new Test();
      t.foo.add("bar"); // Modification-2
      System.out.println("print - " + t.foo);
  }

但是在这种情况下,您可以看到,如果尝试编写以下代码,将会出现编译时错误。

public static void main(String[] args)
{
    Main main = new Main();
    main.foo=new ArrayList<>();//Cannot assign a value to final variable 'foo'
    System.out.println("print - " + main.foo);
}