在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的不同上下文。

最终变量一个最终变量只能赋值一次。如果变量是引用,这意味着不能将该变量重新绑定到引用另一个对象。

class Main {
   public static void main(String args[]){
      final int i = 20;
      i = 30; //Compiler Error:cannot assign a value to final variable i twice
   }
}

Final变量可以稍后赋值(声明时不强制赋值),但只能赋一次。

Final类不能扩展(继承)

final class Base { }
class Derived extends Base { } //Compiler Error:cannot inherit from final Base

public class Main {
   public static void main(String args[]) {
   }
}

Final方法不能被子类覆盖。

//Error in following program as we are trying to override a final method.
class Base {
  public final void show() {
       System.out.println("Base::show() called");
    }
}     
class Derived extends Base {
    public void show() {  //Compiler Error: show() in Derived cannot override
       System.out.println("Derived::show() called");
    }
}     
public class Main {
    public static void main(String[] args) {
        Base b = new Derived();;
        b.show();
    }
}

其他回答

我只能回答你的问题,在这种情况下,你不能改变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只与引用本身有关,而与被引用对象的内容无关。

Java没有对象不变性的概念;这是通过仔细设计对象来实现的,这是一个远不是微不足道的努力。

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

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

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

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

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

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

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

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

我想在这里写一个更新的和深入的回答。

Final关键字可以在几个地方使用。

final类意味着没有其他类可以扩展这个final类。当Java运行时(JRE)知道一个对象引用的类型是final类(比如F)时,它就知道该引用的值只能是F类型。

Ex:

F myF;
myF = new F();    //ok
myF = someOther;  //someOther cannot be in type of a child class of F.
                  //because F cannot be extended.

因此,当它执行该对象的任何方法时,该方法不需要在运行时使用虚拟表进行解析。即不能应用运行时多态性。所以运行时不会担心这个。这意味着它节省了处理时间,从而提高了性能。

方法

任何类的final方法意味着任何扩展该类的子类都不能重写该final方法。因此,此场景中的运行时行为也与前面提到的类的行为完全相同。

字段,局部变量,方法参数

如果将上述任何类型指定为final,则意味着该值已经最终确定,因此不能更改该值。

Ex:

对于字段,为本地参数

final FinalClass fc = someFC; //need to assign straight away. otherwise compile error.
final FinalClass fc; //compile error, need assignment (initialization inside a constructor Ok, constructor can be called only once)
final FinalClass fc = new FinalClass(); //ok
fc = someOtherFC; //compile error
fc.someMethod(); //no problem
someOtherFC.someMethod(); //no problem

对于方法参数

void someMethod(final String s){
    s = someOtherString; //compile error
}

这仅仅意味着不能更改最终参考值的值。也就是说,只允许一次初始化。在这个场景中,在运行时,由于JRE知道值不能更改,它将所有这些最终确定的值(最终引用的值)加载到L1缓存中。因为它不需要一次又一次地从主存中加载。否则它会加载到L2缓存,并从主存中进行时间到时间的加载。所以这也是一种性能提升。

因此,在上述三种情况下,当我们没有在可以使用的地方指定最后的关键字时,我们不需要担心,编译器优化会为我们做这件事。编译器优化还为我们做了很多其他的事情。:)

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

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

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

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