引言
三目运算符大家都应该了解吧。是一种简化代码的操作。是很多编程语言都支持的一个写法。用法也很简单。
condition ? 表达式1 : 表达式2
- 当condition为
true的时候,执行表达式1。 - 当condition为
false的时候,执行表达式2。
自动拆装箱大家应该也都熟悉。Java中推出了基本类型相对应的包装类。
- byte <--> Byte
- short <--> Short
- char <--> Character
- int <--> Integer
- long <--> Long
- float <--> Float
- double <--> Double
- boolean <--> Boolean
自动装箱就是Java自动将原始类型值转换成对应的对象,比如将int的变量转换成Integer对象,这个过程叫做装箱,反之将Integer对象转换成int类型值,这个过程叫做拆箱。因为这里的装箱和拆箱是自动进行的非人为转换,所以就称作为自动装箱和拆箱。
装箱过程是通过调用包装器的valueOf方法实现的,而拆箱过程是通过调用包装器的 xxxValue方法实现的。
倘若我拿出三目运算符+自动拆装箱,阁下又该如何应对呢?
这就是阿里巴巴特别强调的一个地方。
条件触发
这里描述的是因为表达式1和2在类型对齐时,可能抛出因自动拆箱导致的NPE异常。并且是有两种场景会触发类型对齐的拆箱操作:
- 表达式1或表达式2的值只要有一个是原始类型。
- 表达式1或表达式2的值的类型不一致,会强制拆箱升级成表示范围更大的那个类型。
不过这里的代码大家可能没看懂。为啥a*b的结果是int类型?而C为啥又会被强制拆箱成int类型?
NPE异常
这是因为在Java中,当进行两个整数(Integer)的数学运算时,结果通常会是int类型,而不是Integer类型。这是因为自动拆箱和装箱操作会导致Integer被拆箱为int进行运算,然后再装箱为Integer。这种自动拆箱和装箱的操作在Java中被广泛应用,以便将基本数据类型和包装类之间进行转换。
public class ThreeOperate {
public static void main(String[] args) {
Integer a = 1;
Integer b = 2;
Integer c = null;
Boolean flag = false;
// a*b 的结果是 int 类型,那么 c 会强制拆箱成 int 类型,抛出 NPE 异常
Integer result = flag ? a * b : c;
}
}
点击运行。出现NPE。
Exception in thread "main" java.lang.NullPointerException
at ThreeOperate.main(ThreeOperate.java:9)
结合我们前面所说的,我们将这段代码进行反编译。
public class ThreeOperate {
public static void main(String[] args) {
// 自动装箱
Integer a = Integer.valueOf(1);
Integer b = Integer.valueOf(2);
Object c = null;
Boolean flag = Boolean.valueOf(false);
//a*b拆箱计算
//c类型对齐,进行拆箱
//对结果进行自动装箱
Integer result = Integer.valueOf(flag.booleanValue()?a.intValue() * b.intValue():((Integer)c).intValue());
}
}
我们可以发现的确是两个Integer进行数学运算时,是首先会调用intValue()拆箱成int类型进行计算。然后将结果通过Integer.valueOf()包装成包装类。
同时我们也可以清楚的知道了为啥会出现NPE。原因是获取Integer类型C对象的intValue()时,C为null。所以就NPE了。
场景验证
前面场景1:表达式1或表达式2的值只要有一个是原始类型。就可能出现NPE。而给出的例子也是属于场景1的。不过是更加隐晦了。两个Integer对象进行了数据运算是通过int来计算的。
场景1
我们来看看比较直白的例子:
public class ThreeOperate {
public static void main(String[] args) {
int a = 1;
Integer c = null;
Boolean flag = false;
// a 的结果是 int 类型,那么 c 会强制拆箱成 int 类型,抛出 NPE 异常
Integer result = flag ? a : c;
}
}
反编译
public class ThreeOperate {
public static void main(String[] args) {
byte a = 1;
Object c = null;
Boolean flag = Boolean.valueOf(false);
Integer result = Integer.valueOf(flag.booleanValue()?a:((Integer)c).intValue());
}
}
同样是c自动拆箱导致NPE异常。
接着我们看这个场景的反例:两个条件都没有基本数据类型
public class ThreeOperate {
public static void main(String[] args) {
Integer a = 1;
Integer c = null;
Boolean flag = false;
Integer result = flag ? a : c;
}
}
运行正常。反编译之后。
public class ThreeOperate {
public static void main(String[] args) {
// 自动装箱
Integer a = Integer.valueOf(1);
Object c = null;
Boolean flag = Boolean.valueOf(false);
Integer var10000 = (Integer)(flag.booleanValue()?a:c);
}
}
没有进行自动拆箱操作。
场景2
表达式1或表达式2的值的类型不一致,会强制拆箱升级成表示范围更大的那个类型。
byte->short->int->long->float->doublechar->int
public class ThreeOperate {
public static void main(String[] args) {
Double a = 2.0;
Integer c = null;
Boolean flag = false;
Double result = flag ? a: c;
}
}
运行。又是熟悉的NPE。
Exception in thread "main" java.lang.NullPointerException
at ThreeOperate.main(ThreeOperate.java:9)
反编译
public class ThreeOperate {
public static void main(String[] args) {
Double a = Double.valueOf(2.0D);
Object c = null;
Boolean flag = Boolean.valueOf(false);
Double result = Double.valueOf(flag.booleanValue()?a.doubleValue():(double)((Integer)c).intValue());
}
}
c首先会进行拆箱操作,然后强转为Double对象。由于c为null,所以出现空指针。
总结
- 装箱过程是通过调用包装器的valueOf方法实现的,而拆箱过程是通过调用包装器的 xxxValue方法实现的。
- 当包装类进行数学运算时会自动拆箱使用基本数据类型进行计算。
有两种场景会触发类型对齐的拆箱操作:
- 表达式1或表达式2的值只要有一个是原始类型。
- 表达式1或表达式2的值的类型不一致,会强制拆箱升级成表示范围更大的那个类型。