【问题标题】:Boolean.valueOf() produces NullPointerException sometimesBoolean.valueOf() 有时会产生 NullPointerException
【发布时间】:2018-03-17 23:40:38
【问题描述】:

我有这个代码:

package tests;

import java.util.Hashtable;

public class Tests {

    public static void main(String[] args) {

        Hashtable<String, Boolean> modifiedItems = new Hashtable<String, Boolean>();

        System.out.println("TEST 1");
        System.out.println(modifiedItems.get("item1")); // Prints null
        System.out.println("TEST 2");
        System.out.println(modifiedItems.get("item1") == null); // Prints true
        System.out.println("TEST 3");
        System.out.println(Boolean.valueOf(null)); // Prints false
        System.out.println("TEST 4");
        System.out.println(Boolean.valueOf(modifiedItems.get("item1"))); // Produces NullPointerException
        System.out.println("FINISHED!"); // Never executed
    }
}

我的问题是我不明白为什么 Test 3 工作正常(它打印 false 并且不产生 NullPointerException)同时 Test 4 抛出NullPointerException。正如您在测试 12 中看到的那样,nullmodifiedItems.get("item1") 等于和 null

Java 7 和 8 中的行为相同。

【问题讨论】:

  • modifiedItems.get("item1") this 为 null,您知道这一点,但您认为将其传递给 valueOf 不会以 NPE 结束?
  • @Stultuske:这是一个有效的问题,因为上面仅两行将文字 null 传递给同一函数不会生成 NPE!这是有充分理由的,但乍一看确实令人困惑:-)
  • 我印象深刻。这是我多年来看到的最有趣的空指针异常问题。
  • @Jeroen 这不是that question 的骗子。虽然拆箱确实是这两个问题的共同点,但这里没有比较。这个问题的关键在于它的发生是因为解决重载的方式;这与 == 的应用方式完全不同。

标签: java nullpointerexception boolean


【解决方案1】:

由于modifiedItems.get 返回Boolean可转换为String),将使用的签名是Boolean.valueOf(boolean)Boolean 被发送到一个原始的boolean。一旦null 在那里返回,发件箱将失败并显示NullPointerException

【讨论】:

    【解决方案2】:

    您必须仔细查看调用了哪个重载:

    • Boolean.valueOf(null) 正在调用 Boolean.valueOf(String)。即使提供了 null 参数,这也不会引发 NPE
    • Boolean.valueOf(modifiedItems.get("item1")) 正在调用Boolean.valueOf(boolean),因为modifiedItems 的值属于Boolean 类型,这需要取消装箱转换。由于modifiedItems.get("item1")null,因此该值的拆箱 - 而不是Boolean.valueOf(...) - 会引发 NPE。

    确定调用哪个重载的规则是pretty hairy,但大致是这样的:

    • 在第一遍中,搜索方法匹配,但不允许装箱/拆箱(也不允许可变数量方法)。

      • 因为nullString 的可接受值,而不是boolean,所以Boolean.valueOf(null) 在此过程中与Boolean.valueOf(String) 匹配;
      • Boolean 对于Boolean.valueOf(String)Boolean.valueOf(boolean) 都不可接受,因此在此传递中没有匹配Boolean.valueOf(modifiedItems.get("item1")) 的方法。
    • 在第二遍中,搜索方法匹配,允许装箱/拆箱(但仍然不能使用可变的方法)。

      • 可以将Boolean 拆箱为boolean,因此Boolean.valueOf(boolean) 在此过程中与Boolean.valueOf(modifiedItems.get("item1")) 匹配;但是编译器必须插入拆箱转换才能调用它:Boolean.valueOf(modifiedItems.get("item1").booleanValue())
    • (第三遍允许使用可变的arity方法,但这在这里不相关,因为前两遍与这些情况相匹配)

    【讨论】:

    • 如果我们在源代码中使用Boolean.valueOf(modifiedItems.get("item1").booleanValue())而不是Boolean.valueOf(modifiedItems.get("item1")),代码会更清晰吗?
    • @CausingUnderflowsEverywhere 不是真的 - 真的很难看到 .booleanValue() 隐藏在表达式中。两个观察结果: 1)自动(取消)装箱是 Java 的一个有意的功能,以消除语法上的垃圾;自己做是可能的,但不是惯用的; 2)这对您根本没有帮助-它当然不会阻止问题的发生,也不会在确实发生故障时提供任何额外的信息(堆栈跟踪将是相同的,因为执行的代码是相同的)。
    • @CausingUnderflowsEverywhere 最好使用工具来突出问题,例如intellij 会在这里为您赢得关于潜在 NPE 的信息。
    【解决方案3】:

    方法签名

    方法Boolean.valueOf(...)有两个签名:

    1. public static Boolean valueOf(boolean b)
    2. public static Boolean valueOf(String s)

    您的modifiedItems 值为Boolean。您不能将Boolean 转换为String,因此将选择第一个签名

    布尔拆箱

    在你的声明中

    Boolean.valueOf(modifiedItems.get("item1"))
    

    可以读作

    Boolean.valueOf(modifiedItems.get("item1").booleanValue())   
    

    但是,modifiedItems.get("item1") 返回 null 所以你基本上会拥有

    null.booleanValue()
    

    这显然会导致NullPointerException

    【讨论】:

    • 措辞不正确,感谢您的指点,并根据您的反馈更新答案。抱歉,我在写的时候没有看到你的答案,我发现我的看起来像你的。我应该删除我的答案以避免对 OP 造成混淆吗?
    • 不要在我的帐户上删除它。请记住,这不是零和游戏:人们可以(并且确实)支持多个答案。
    【解决方案4】:

    正如安迪已经很好地描述了NullPointerException的原因:

    这是由于布尔取消装箱:

    Boolean.valueOf(modifiedItems.get("item1"))
    

    转换成:

    Boolean.valueOf(modifiedItems.get("item1").booleanValue())
    

    在运行时,如果 modifiedItems.get("item1") 为空,则抛出 NullPointerException

    现在我想在这里再补充一点,如果将以下类取消装箱到它们各自的原语中,如果它们对应的返回对象为空,也会产生NullPointerException 异常。

    1. 字节 - 字节
    2. char - 字符
    3. 浮动 - 浮动
    4. int - 整数
    5. 长 - 长
    6. 短 - 短
    7. 双 - 双

    代码如下:

        Hashtable<String, Boolean> modifiedItems1 = new Hashtable<String, Boolean>();
        System.out.println(Boolean.valueOf(modifiedItems1.get("item1")));//Exception in thread "main" java.lang.NullPointerException
    
        Hashtable<String, Byte> modifiedItems2 = new Hashtable<String, Byte>();
        System.out.println(Byte.valueOf(modifiedItems2.get("item1")));//Exception in thread "main" java.lang.NullPointerException
    
        Hashtable<String, Character> modifiedItems3 = new Hashtable<String, Character>();
        System.out.println(Character.valueOf(modifiedItems3.get("item1")));//Exception in thread "main" java.lang.NullPointerException
    
        Hashtable<String, Float> modifiedItems4 = new Hashtable<String, Float>();
        System.out.println(Float.valueOf(modifiedItems4.get("item1")));//Exception in thread "main" java.lang.NullPointerException
    
        Hashtable<String, Integer> modifiedItems5 = new Hashtable<String, Integer>();
        System.out.println(Integer.valueOf(modifiedItems5.get("item1")));//Exception in thread "main" java.lang.NullPointerException
    
        Hashtable<String, Long> modifiedItems6 = new Hashtable<String, Long>();
        System.out.println(Long.valueOf(modifiedItems6.get("item1")));//Exception in thread "main" java.lang.NullPointerException
    
        Hashtable<String, Short> modifiedItems7 = new Hashtable<String, Short>();
        System.out.println(Short.valueOf(modifiedItems7.get("item1")));//Exception in thread "main" java.lang.NullPointerException
    
        Hashtable<String, Double> modifiedItems8 = new Hashtable<String, Double>();
        System.out.println(Double.valueOf(modifiedItems8.get("item1")));//Exception in thread "main" java.lang.NullPointerException
    

    【讨论】:

    • "Converted into ... at runtime" 它在编译时转换成那个。
    【解决方案5】:

    理解它的一种方式是,当调用Boolean.valueOf(null) 时,java 被准确地告知评估 null。

    但是,当调用Boolean.valueOf(modifiedItems.get("item1")) 时,java 被告知从对象类型 Boolean 的 HashTable 中获取一个值,但它没有找到 Boolean 类型,而是找到了一个死胡同(null),即使它期望 Boolean .抛出 NullPointerException 异常是因为这部分 java 的创建者认为这种情况是程序出错的一个实例,需要程序员注意。 (发生了一些意想不到的事情。)

    在这种情况下,更多的区别在于故意声明您希望 null 存在,以及 java 查找对要找到对象的对象 (null) 的缺失引用。

    在此答案中查看有关 NullPointerException 的更多信息: https://stackoverflow.com/a/25721181/4425643

    【讨论】:

    • 如果有人可以帮助改进这个答案,我在想一个词,指程序员写的东西意图明确,没有歧义
    猜你喜欢
    • 2016-02-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-06
    • 2020-11-05
    • 1970-01-01
    • 2020-06-19
    相关资源
    最近更新 更多