为什么Java最终变量不能在setter方法中赋值?

问题描述

非静态最终变量只能赋值一次。

但是为什么这个赋值只能在声明或构造函数中发生?

解决方法

final 变量被定义为不可变的,即分配给它的值将是该变量 x 的唯一值。从 JLS(§4.12.4.)

中可以看出

一个变量可以声明为final。最终变量只能是 分配给一次。

现在构造函数就像任何其他方法一样,除了当从类创建对象(非静态)时,它首先被执行。

因此,final 变量可以通过构造函数赋值。

以下面的代码为例:

public class Test {

    public final int x;

    public Test(int x) {
        this.x = x;
    }
}

编译器接受这个调用,因为它保证对于那个特定对象,它的类的构造函数首先被调用并且不会再次被调用(即构造函数在对象的整个生命周期中被调用一次并且只调用一次。)

但是以下代码抛出错误:Non-static field 'x' cannot be referenced from a static context

public class Test {

    public final int x;

    static {
        x = 5;
    }

    public Test(int x) {
        this.x = x;
    }
}

由于 x 不是静态字段,因此无法在静态块中启动。

此代码也会抛出错误:Cannot assign a value to final variable 'x'

public class Test {

    public final int x;
    
    public Test(int x) {
        this.x = x;
    }

    public void setX(int x) {
        this.x = x;
    }
}

这是因为不能保证此对象的方法 setX 将首先运行且仅运行一次。程序员可以多次调用这个方法。因此,编译器会抛出错误。

所以没有办法让变量“初始化”一次(例如, 如果之前已经分配了变量,则 setter 将阻塞)单独 用java语法?我认为 final 可能会这样,但现在我明白了 不是。

对于您的问题,您可以简单地将变量设为私有并将条件添加到 setter 方法中,以便仅在变量为 null 时添加值。

例如:

public class Test {

    private Integer x;

    public Test() {
    }

    public Test(int x) {
        this.x = x;
    }

    public void setX(int x) {
        if (null == this.x) this.x = x;
    }

    public static void main(String[] args) {
        Test y = new Test(5);
        System.out.println(y.x);

        y.setX(20);
        System.out.println(y.x);
    }
}

顺便说一下,这不是线程安全的。我只是添加了一个简单的例子。

,

关键字 final 在 Java 中是什么意思?

  1. 在类声明中使用时,表示该类不能被扩展。
  2. 在方法中使用时,意味着该方法不能被覆盖。
  3. 在方法参数中使用时,表示该参数的值不能在方法内部更改。 (局部常量)
  4. 在类字段(“变量”)中使用时,表示它是一个全局常量

常量的值必须在编译时解析。而且,顾名思义,常量字段不能改变值。因此,编译器不允许从 setter (mutator) 方法设置值。

与许多人认为的相反,对于一个字段是常量,它不必声明为 staticfinal。也就是说,由于常量的值无法更改,因此每个类实例将共享相同的值。因此,明确地使它们 static 强化了这一概念。

关键字 final 有第五种用法,这是在声明局部变量时使用的。这是一个更冗长的解释。

编译代码时会发生什么?

我更新了我的答案,因为我认为问题的一部分是一些开发人员不太了解编译代码时会发生什么。正如我之前提到的,常量值在编译时间解析。要理解这个概念,请考虑以下示例:

public class MyClass {
   private final double PI = 3.14159;
   // rest of class left out intentionally
}

如果我在笔记本电脑上编译这个类,然后将代码部署到某个远程服务器,服务器如何知道全局常量字段 PI 的赋值为 3.14159?这是因为当我编译这段代码时,这个值会与字节码一起打包。在这种情况下,类构造函数根本不起作用。但是,如果常量字段被初始化为其默认值,则可以通过构造函数分配永久(常量)值

public class MyClass {
   private final double PI; // default value of 0.0
   public MyClass(double value) {
      PI = value;
   }
// rest of code omitted intentionally
}

此处将常量声明为 static 会有所不同。如果常量也是 static,则不能执行上述操作,因为调用构造函数意味着您可以拥有 MyClass 的多个实例,并且每个实例可以设置不同的值。这显然违反了 static 成员的定义。因此,如果您必须将字段声明为 staticfinal,请了解您不能使用第二种方法分配值。只允许我展示的第一个。

,

最终停止是一个变量的重新分配

简单的回答:

当您希望编译器阻止将变量重新分配给不同的对象时,请使用关键字 final。

无论变量是静态变量、成员变量、局部变量,还是实参/参量变量,效果完全一样。

希望这对朋友有帮助 =) #StaySafe