当前位置: 首页 > 知识库问答 >
问题:

在Java中,FINAL变量包含两个不同的值

韶和璧
2023-03-14

下面我有两个箱子。

class A{
    A(){
        System.out.println("A's Constructor");
        f();
    }
    void f() {System.out.println("A");}
}

class B extends A{
    B(){
        System.out.println("B's Constructor");
        f();
    }
    final String x = "B";
    void f() {System.out.println(x);}
}

public class JavaPOCSamples {
    public static void main(String[] args) {
        new B();
    }
}
class A{
    A(){
        System.out.println("A's Constructor");
        f();
    }
    void f() {System.out.println("A");}
}

class B extends A{
    B(){
        System.out.println("B's Constructor");
        f();
    }
    final String x = "B".trim();
    void f() {System.out.println(x);}
}

public class JavaPOCSamples {
    public static void main(String[] args) {
        new B();
    }
}

案例1和案例2之间的区别在于案例2具有最终字符串x=“B”。trim()类别B中,但案例1在类别B中具有最终字符串x=“B”

A's Constructor
B // Note that a final variable X is "B"
B's Constructor
B // Note that a final variable X is still "B"
A's Constructor
null // Note that a final variable X is null
B's Constructor
B // Note that a final variable X is changed to "B"

问题是关于案例2的输出:final String x为什么包含两个不同的值,一个是null,另一个是字符串“B”?

共有2个答案

景元忠
2023-03-14

最终的应该总是包含一个,而不是对象级计算,除非状态已经建立。

您可以设置final int x=2。它可以工作,但调用方法需要状态,而B还没有状态。

您可以通过在类A中移动最终int x="B". trim()来验证这一点。当调用f()时,它将作为A已经具有state

阎声
2023-03-14

这是一个很好的谜题,涉及Java的final字段语义和实例创建语义的细节。与任何其他字段一样,创建对象时,final字段初始化为默认值(JLS§4.12.5):

程序中的每个变量在使用其值之前必须有一个值:

  • 每个类变量、实例变量或数组组件在创建时都会使用默认值进行初始化(§15.9、§15.10.2):

最终代码只是一个编译时检查,以确保在创建对象时,该字段将被分配一次值。在运行时,字段在被分配新值之前仍然有其默认初始值。但是,对于基元类型或类型为String最终变量有一种特殊情况,其初始化器是一个常量表达式(§4.12.4):

常量变量是基元类型或String类型的最终变量,它用常量表达式初始化(§15.28)。变量是否是常量变量可能会影响到类初始化(§12.4.1)、二进制兼容性(§13.1,§13.4.9)和确定赋值(§16(定义赋值))。

常量表达式在(§15.28)中定义为包括文字(包括String文字),如"B"。表达式"B". trim()是一个方法调用表达式,因此它不是一个常量表达式。

剩下的细节是,当创建B的实例时,B()构造函数隐式调用超类构造函数A(),这发生在执行B()构造函数的其余部分或子类B中的任何初始值设定项之前(§12.5,重点):

在返回对新创建对象的引用作为结果之前,将使用以下过程处理指示的构造函数以初始化新对象:。。。

此构造函数不会以同一类中另一个构造函数的显式构造函数调用开始(使用此函数)。如果此构造函数用于对象以外的类,则此构造函数将以超类构造函数的显式或隐式调用开始(使用super)。。。

执行此类的实例初始化器和实例变量初始化器,将实例变量初始化器的值分配给相应的实例变量,按它们在类的源代码中文本显示的从左到右的顺序。

所以在情况2中,因为赋值x="B". trim()在子类B中,所以直到A()构造函数完成后才执行。由于A()构造函数调用f(),该方法打印出一个null值,因为这是字段的默认值,然后才分配给它任何其他值。

但是,在案例1中,使用常量表达式初始化的字符串类型的final字段的特殊规则适用,因此x在调用a()构造函数之前已经是“B”

 类似资料:
  • 输出如下:http://imgur.com/a/nu3n6

  • 我有一个问题。 我想做的是迭代tmpList,找到身高和体重的总和,并在名称为空时添加“No Name”。 我已经工作到目前为止,这似乎是不对的。我是说我必须重复每个案例。 有没有办法把它们结合在一起?或者有什么更好的建议?

  • 我在类SpawnManager中声明了一个私有int tree_count。void Start()和void Update()按照预期使用了该变量,但另一个方法public void tree_destroy似乎使用了不同的tree_count。 这是我的代码。

  • 我正在使用go-redis/redis和go-redis/cache来缓存Go对象。 其中obj是一个具有go映射(键值对)的结构,通过使用上面的代码,我正在设置一个键并将值保存到其中。这是package Common。现在我想在不同的包中访问它,比如GetRedis_pkg,而不导入pkg。我有什么办法可以做到。并且我可以通过任何方式访问该结构内部的特定映射吗?使用redis key imort

  • 问题内容: 让抽象类定义实例变量是否是一种好习惯? 然后,子类ExternalJavaScript.class会自动获取源变量,但我认为,如果所有子类本身都定义了源而不是继承,则读取代码会更容易。 你有什么建议? /亚当 问题答案: 我本以为这样的话会更好,因为您要添加一个变量,所以为什么不限制访问并使它更整洁呢?您的吸气器/装夹器应该按照罐子上的说明去做。 再次提到这个问题,您在阅读时是否会费心

  • 问题内容: 所以我有两个包含主键’User’的模型 现在,我想查找也在中的用户的所有评论。例如,假设 用户“ 1”* 为 用户“ 3” 和 用户“ 5” 分别撰写了一条评论 * 现在,我输出的应该是对应于“ LoremIpsum1”的评论对象,即其subject_user也存在于中的评论对象。我尝试了以下 但这给了我所有的评语 编辑: 这是我插入评论的方式: 问题答案: 如果我对您的理解正确,这应