自动装箱/拆箱可能导致NPE
在一次使用redisTemplate去操作的时候,发现IDEA提示UnBoxing of 'redisTemplate.hasKey(key)' may produce 'NullPointerException'
,意思是说自动拆箱可能会引发空指针异常。
因为hasKey
这个方法是返回Boolan类型,而方法的返回值是boolean,直接把Boolean的值返回,就会发生自动拆箱。
自动装箱与自动拆箱
基本数据类型的自动装箱(autoboxing)、拆箱(unboxing)是自J2SE 5.0开始提供的功能。 一般我们要创建一个类的对象实例的时候,我们会这样: Class a = new Class(parameters); 当我们创建一个Integer对象时,却可以这样: Integer i = 100;(注意:和 int i = 100;是有区别的 ) 实际上,执行上面那句代码的时候,系统为我们执行了: Integer i = Integer.valueOf(100)。Integer i = 100
这就是一个自动装箱。
出现空指针的情况
包装器类型赋值给基本类型时,自动拆箱下出现的空指针
当把基本类型作为方法的参数类型,但是方法调用者传参使用的却是包装器类型,所以出现了自动拆箱的情况,此时包装器类型如果传的是NULL,方法中就很有可能出现NULL指针异常
例子
1
2
3
4
5
6
7
8
9
10
public static void main ( String [] args ) {
Map < String , Boolean > map = new HashMap <> ( 16 );
map . put ( "key1" , true );
map . put ( "key2" , false );
map . put ( "key3" , false );
Boolean b = ( map != null ? map . get ( "key" ) : false );
System . out . println ( b );
}
执行结果 :
null
Copy map
不为空,当get()
之后key
不存在,Boolean
类型的b被赋了空值。再看回redisTemplate.hasKey(key)
的操作可能会产生空值,然后拆箱返回,因为这是一个工具类里的方法,调用者可能会直接调用,这时候就有了空指针异常的风险,影响了程序的健壮性了。
使用javap.exe反编译之后,查看
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public static void main ( java . lang . String []);
Code :
0 : new #2 // class java/util/HashMap
3 : dup
4 : bipush 16
6 : invokespecial #3 // Method java/util/HashMap."<init>":(I)V
9 : astore_1
10 : aload_1
11 : ldc #4 // String key1
13 : iconst_1
14 : invokestatic #5 // Method java/lang/Boolean.valueOf:(Z)Ljava/lang/Boolean;
17 : invokeinterface #6, 3 // InterfaceMethod java/util/Map.put:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
22 : pop
23 : aload_1
24 : ldc #7 // String key2
26 : iconst_0
27 : invokestatic #5 // Method java/lang/Boolean.valueOf:(Z)Ljava/lang/Boolean;
30 : invokeinterface #6, 3 // InterfaceMethod java/util/Map.put:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
35 : pop
36 : aload_1
37 : ldc #8 // String key3
39 : iconst_0
40 : invokestatic #5 // Method java/lang/Boolean.valueOf:(Z)Ljava/lang/Boolean;
43 : invokeinterface #6, 3 // InterfaceMethod java/util/Map.put:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
48 : pop
49 : aload_1
50 : ifnull 67
53 : aload_1
54 : ldc #9 // String key
56 : invokeinterface #10, 2 // InterfaceMethod java/util/Map.get:(Ljava/lang/Object;)Ljava/lang/Object;
61 : checkcast #11 // class java/lang/Boolean
64 : goto 71
67 : iconst_0
68 : invokestatic #5 // Method java/lang/Boolean.valueOf:(Z)Ljava/lang/Boolean;
71 : astore_2
72 : getstatic #12 // Field java/lang/System.out:Ljava/io/PrintStream;
75 : aload_2
76 : invokevirtual #13 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
79 : return
Copy 从反编译的结果可以看到Method java/lang/Boolean.valueOf:(Z)Ljava/lang/Boolean;
出现了四次,三次是map.put()
的时候,最后一次是:Boolean b = ( map != null ? map.get("key") : false);
可以看出Boolean
类型自动装箱valueOf()
。如果一个Boolean
引用为空时,在自动拆箱就是一个空值。这就会发生NPE
1
2
3
public static Boolean valueOf ( boolean b ) {
return ( b ? TRUE : FALSE );
}
Copy 总结
包装器类型和基本数据类型都可以的业务场景下,优先考虑使用基本类型
对于不确定的包装器类型,一定要对NULL情况做检验和判断