在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;

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


当前回答

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

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

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

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

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

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

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

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

其他回答

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

    private static final List foo;

    static {
        foo = new ArrayList();
    }

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

我只能回答你的问题,在这种情况下,你不能改变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关键字有很多种用法:

final类不能被子类化。 final方法不能被子类重写 final变量只能初始化一次

其他用途:

在方法体中定义匿名内部类时, 在该方法范围内声明为final的所有变量为 从内部类内部访问

静态类变量将从JVM开始时就存在,并且应该在类中初始化。如果您这样做,错误消息将不会出现。

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

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

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

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

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

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

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

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

如果你让foo是静态的,你必须在类构造函数中初始化它(或者你定义它的内联),就像下面的例子。

类构造函数(不是实例):

private static final List foo;

static
{
   foo = new ArrayList();
}

内联:

private static final List foo = new ArrayList();

这里的问题不是最终修饰符如何工作,而是静态修饰符如何工作。

final修饰符强制在调用构造函数完成之前对引用进行初始化(即必须在构造函数中初始化它)。

当你内联初始化一个属性时,它会在你为构造函数定义的代码运行之前初始化,所以你会得到以下结果:

如果foo是静态的,foo = new ArrayList()将在你为你的类定义的静态{}构造函数执行之前执行 如果foo不是静态的,foo = new ArrayList()将在运行构造函数之前执行

如果不内联初始化属性,final修饰符强制初始化它,并且必须在构造函数中初始化。如果你也有一个静态修饰符,你必须初始化属性的构造函数是类的初始化块:static{}。

您在代码中得到的错误来自于这样一个事实,即静态{}在加载类时运行,在您实例化该类的对象之前。因此,在创建类时,你还没有初始化foo。

可以将静态{}块视为Class类型对象的构造函数。在这里必须初始化静态final类属性(如果不是内联完成的话)。

注:

final修饰符仅对基本类型和引用保证const性。

当你声明一个final对象时,你得到的是该对象的最终引用,但对象本身不是常量。

当你声明一个final属性时,你真正实现的是,一旦你声明了一个特定目的的对象(就像你已经声明的final List),这个对象将被用于该目的:你不能将List foo更改为另一个List,但你仍然可以通过添加/删除项来改变你的List(你正在使用的List将是相同的,只是它的内容被改变了)。