java的 condition ? statement1 : statement2 ,相信大家在日常写代码的时候或多或少都会使用,这个操作可以在一定程度上减少代码量。但是你知道如果使用不当,这个操作会得到意想不到的的结果。我们来看一下下面的这个例子,
public class Test2 {
public static void main(String[] args) {
Long number1 = null;
long number2 = -100;
int condition = 1;
Long number = condition == 1? number1 : number2;
System.out.println(number);
}
}
多数人看到上面的代码第一反应应该是输出null,但是真的是这样吗?运行上面的代码后,结果令人大跌眼镜。。
Exception in thread "main" java.lang.NullPointerException
at Test2.main(Test2.java:7)
上面这么简单的代码竟然抛出了空指针异常,根据异常栈可以大概猜出空指针的原因是number1为null。按照我们的常识上面的代码应该是按照下面的方式来运行
- condition == 1 为true
- number = number1
- 打印出null
但是很显然并不是如此。为了一探究竟,我们来看一下编译好的字节码究竟是怎样的,使用
javap -verbose Test2.class
将上面的代码反编译成字节码形式,
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=6, args_size=1
0: aconst_null
1: astore_1
2: ldc2_w #2 // long -100l
5: lstore_2
6: iconst_1
7: istore 4
9: iload 4
11: iconst_1
12: if_icmpne 22
15: aload_1
16: invokevirtual #4 // Method java/lang/Long.longValue:()J
19: goto 23
22: lload_2
23: invokestatic #5 // Method java/lang/Long.valueOf:(J)Ljava/lang/Long;
26: astore 5
28: getstatic #6 // Field java/lang/System.out:Ljava/io/PrintStream;
31: aload 5
33: invokevirtual #7 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
36: return
LineNumberTable:
line 3: 0
line 4: 2
line 5: 6
line 7: 9
line 8: 28
line 9: 36
LocalVariableTable:
Start Length Slot Name Signature
0 37 0 args [Ljava/lang/String;
2 35 1 number1 Ljava/lang/Long;
6 31 2 number2 J
9 28 4 condition I
28 9 5 number Ljava/lang/Long;
StackMapTable: number_of_entries = 2
frame_type = 254 /* append */
offset_delta = 22
locals = [ class java/lang/Long, long, int ]
frame_type = 64 /* same_locals_1_stack_item */
stack = [ long ]
}
SourceFile: "Test2.java"
我们来看看上面的字节码干了什么,
- 0-7行分别是给number1, number2,condition赋值
- 9-12将condition和1进行对比,如果不等则跳转到22,否则继续执行
- 15-16调用number1的longValue()方法
- 其他操作
在第三步的时候因为number1是null,所以抛出了空指针。到这,空指针的原因找到了,那么为什么要调用number1的longValue()方法呢?猜测是因为number1的类型是Long, 而number2的类型是long, java在处理这种情况的时候先将包装类型Long进行拆装,然后再调用Long.valueOf()重新获取包装类型。
为了验证上面的猜想,稍微修改一下代码,将number2的类型修改为Long,
public class Test2 {
public static void main(String[] args) {
Long number1 = null;
Long number2 = -100L;
int condition = 1;
Long number = condition == 1? number1 : number2;
System.out.println(number);
}
}
运行代码后可以看到打印null,使用javap查看字节码可以看到没有调用longValue()方法。
基本上印证了我们的猜想。。
总结
在使用condition ? statement1 : statement2的时候,你需要仔细检查是否会出现condition=true,statement1为封装类型,statement2为原始类型的情况,如果出现了这种情况,statement1必须不能为空,否则相当于埋下了一个定时炸弹。