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

行为差异:“null”初始化了final static成员,“null”初始化了最终局部变量

仲法
2023-03-14

我在后续代码中遇到了一个我之前不知道的行为。

考虑1st情况:

public static void main(String[] args) {    
    final String str = null;
    System.out.println(str.length());  // Compiler Warning: NullPointerAccess
}

正如预期的那样,编译器在str为null-null指针访问时向我显示以下警告:变量str只能在此位置为null。

现在,当我移动该变量时,静态final字段初始化为null:

class Demo {
    static final String str = null;

    public static void main(String[] args) {
        System.out.println(str.length());  // No Compiler Warning
    }
}

现在,编译器没有显示任何警告。好了,编译器应该知道,str是最终的,在代码的任何一点上都不会改变它的值。假设它是空的,那么以后肯定会导致空点异常。

尽管编译器在第一种情况下成功地警告了我这一点,但为什么它在第二种情况下无法识别这一点。为什么会发生这种行为变化?如果我将静态字段更改为实例字段,并使用演示的实例访问它,行为是相同的。

我认为这种行为可能已经在JLS中指定,所以我阅读了主题明确的作业,但没有找到任何与此问题相关的内容。有人能解释行为的变化吗?如果可能的话,我正在寻找一些与JLS链接的优点?

除此之外,为什么编译器一开始只显示警告,正如我所想的那样,由于我上面提到的相同原因,方法调用肯定会在运行时抛出NPE,因为字段不能更改?为什么它不告诉我一个编译器错误?很明显,我对编译器的期望太高了吗。length()不能大于NPE?

很抱歉之前错过了:

我正在使用Eclipse Juno,在带有OpenJDK 7的Ubuntu 12.04上。

共有3个答案

诸葛品
2023-03-14
public static void main(String[] args) {    
    final String str = null;
    System.out.println(str.length());  // Compiler Warning: NullPointerAccess
}

在这种情况下,str是一个局部变量,如果您在初始化它之前尝试对其执行任何操作,编译器将不会编译。流分析完全不同,它将检查代码流,在其中检测到在执行length()操作时局部变量str只能为null。

class Demo {
    static final String str = null;

    public static void main(String[] args) {
        System.out.println(str.length());  // No Compiler Warning
    }
}

在这种情况下,str是一个实例变量,即使您没有显式分配,它也将为空。

为什么这里没有警告?

您可以在构造函数中初始化实例变量。或者可以在对其调用lenght()操作之前对其调用setter方法。因此,它从流分析中逃脱(编译器不确定实例变量在该点是否为null,但在第一种情况下,编译器确定局部变量始终为null)。

乐正意智
2023-03-14

哇!事实证明,这是特定于eclipse的问题。当我编译代码时使用:

javac -Xlint:all Demo.java

在任何情况下都没有显示任何警告。所以,我回到eclipse,检查为本例启用的任何设置,并找到了一个。

在Windows中-

现在看来这是一个完全愚蠢的问题。我感到羞耻。:(

蒋硕
2023-03-14

我不能百分之百确定,但在第二种情况下,您有针对局部变量的最终字段,可以直接在静态(或实例块,取决于变量是否为静态)初始化块中为该最终字段分配一些值:

class Demo {
...
static {
 str = "some text";
}
...
}

所以编译器不会给你警告。

 类似资料:
  • 在Kotlin中,如果变量在应用程序启动时为,并且在创建后不能再次赋值为,那么有什么方法可以对其进行注释吗? 我不能将field设置为例如,因为它可以在某个时候被置为空,或者,因为它在使用之前被初始化(或者这可能是解决我的情况的正确方法?)。

  • 下面的示例类无法编译: 此代码的编译错误消息是: 但是,对于包含以下方法的类,Java不会生成任何错误消息: 关于初始化及其要求,为什么Java对最终实例变量和最终局部变量的处理不同?谢谢

  • 我对python相当陌生,我想知道局部变量是如何工作的。让我们从一个简单方法的示例开始: 让我们假设local_dict像一种常量变量一样使用。这里有一个问题:它是在每次调用do_sth()时创建的,还是创建一次并保存在do_sth()内部的某个地方?

  • 问题内容: 我有这个代码: 这会导致编译错误:可能尚未初始化,这很公平。 现在,我将代码更改为: 我得到同样的编译错误!我必须初始化为null: 那么,不初始化对象和初始化为null有什么区别?如果我声明一个没有初始化的对象,它是否为null? 谢谢 问题答案: 字段(成员变量)被初始化为(或初始化为默认的原始值,如果它们是原始的) 局部变量未初始化,您有责任设置初始值。

  • 我想了解各种情况下类实例的初始化 在JLS-7第12.5节中,没有提到如何以及何时初始化最终实例变量?如果实例变量被声明为final,是否有人能给我一点参考来理解其行为? 给出的输出如下 在哪里作为 是否将输出作为

  • 问题内容: 我在用Java工作。 我通常会这样设置一些对象: 问题是:在此示例中是否等于,按原样我可以假定对未初始化的对象进行空检查将是准确的? 问题答案: 正确,未显式初始化的引用类型的静态成员和实例成员都由Java 设置为。相同的规则适用于数组成员。 根据Java语言规范的第4.12.5节: 变量的初始值 程序中的每个变量在使用值之前都必须具有一个值: 每个类变量,实例变量或数组组件在创建时均