在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引用中添加值。这个问题发生在你不能很好地理解参考值和原始值之间的区别。引用值也是在堆内存中存储对象地址(即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);
}

其他回答

以上都是正确的。此外,如果您不希望其他人从您的类中创建子类,则将您的类声明为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关键字表示变量只能初始化一次。在你的代码中,你只执行了一次final的初始化,所以条件是满足的。该语句执行foo的单独初始化。注意final != immutable,它只意味着引用不能改变。

foo = new ArrayList();

当你将foo声明为static final时,变量必须在类加载时初始化,并且不能依赖实例化(也就是调用构造函数)来初始化foo,因为静态字段必须在没有类实例的情况下可用。不能保证在使用静态字段之前已经调用了构造函数。

当你在静态final场景下执行你的方法时,Test类在实例化t之前被加载,此时foo没有实例化,这意味着它还没有初始化,所以foo被设置为所有对象的默认值,即null。此时,我假设您的代码在尝试向列表中添加项时抛出NullPointerException。

final是Java中限制用户的保留关键字,它可以应用于成员变量、方法、类和局部变量。Final变量通常在Java中使用static关键字声明,并被视为常量。例如:

public static final String hello = "Hello";

当我们在变量声明中使用final关键字时,存储在该变量中的值不能在后面更改。

例如:

public class ClassDemo {
  private final int var1 = 3;
  public ClassDemo() {
    ...
  }
}

注意:声明为final的类不能扩展或继承(也就是说,不能有超类的子类)。同样值得注意的是,声明为final的方法不能被子类重写。

使用final关键字的好处将在本文中讨论。

这是一个非常好的面试问题。有时他们甚至会问你final对象和immutable对象的区别是什么。

1)当有人提到一个最终对象时,这意味着引用不能改变,但它的状态(实例变量)可以改变。

2)不可变对象的状态不能改变,但其引用可以改变。 例:

    String x = new String("abc"); 
    x = "BCG";

可以修改Ref变量x指向另一个字符串,但是不能修改abc的值。

3)实例变量(非静态字段)在调用构造函数时初始化。所以你可以在构造函数中初始化变量的值。

4)“但是我看到你可以改变类的构造函数/方法的值”。—您不能在方法中更改它。

5)静态变量在类加载过程中初始化。所以你不能在构造函数内部初始化,它甚至必须在构造函数之前完成。因此,需要在声明过程中为静态变量赋值。