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

其他回答

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

由于final变量是非静态的,所以可以在构造函数中初始化它。但是如果你让它是静态的,它就不能被构造函数初始化(因为构造函数不是静态的)。 对列表的添加不期望通过使列表最终停止。Final只是将引用绑定到特定对象。你可以自由地改变对象的“状态”,但不能改变对象本身。

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关键字可以在几个地方使用。

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缓存,并从主存中进行时间到时间的加载。所以这也是一种性能提升。

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