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

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

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

public static final String hello = "Hello";

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

例如:

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

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

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

如果你让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将是相同的,只是它的内容被改变了)。

你总是可以初始化一个final变量。编译器确保你只能做一次。

注意,调用存储在final变量中的对象的方法与final的语义无关。换句话说:final只与引用本身有关,而与被引用对象的内容无关。

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