在java开发中,我们遇到最多的异常莫过于java.lang.NullPointerException异常了。这个异常我们做好防卫性校验基本就可以避免,比如类似下面
- public void processData(String dataParam){
- if(StringUtils.isEmpty(dataParam)){
- return;
- }
- //代码省略...
- }
但是有一种空指针异常确实非常隐蔽,那就是java的自动拆箱。说道拆箱必须先从装箱开始说起。
java语言有7中数值类型(还有一个布尔类型,一共8个基本类型),下图是它们的数值类型合法转换图:
6个实心箭头表示无信息丢失的转换,有3个虚箭头,表示可能有精度损失的转换。那么我们为什么还需要包装类型呢,8种基本类型分别对应8中包装类型(基本数据类型是在栈中直接分配内存,而包装类型是数据的引用在栈中,但它的对象在堆中,基本类型和包装类型之间的性能比较不在本文论述之内),如下。
上面说了很多铺垫,回到我们的题目上面来,现在我们看下面代码,空指针如何报出来的
java语言是一个面向对象的语言,但是我们在使用基本类型的时候如何面向对象呢,在实际使用中有许多不方便的地方,比如我们在jdk1.5之前java集合是不能放基本类型的。为了解决这方面的不足,就设计了一个包装类,分别与基本类型想对应。在《深入理解java虚拟机》一书第2版,在第10章 “早期(编译期)优化“这章中对装箱和拆箱有语法糖的描述,糖是甜甜的味道,语法糖嘛,就是让这种语法增强功能,^_^。
方法:Long getVenderIdByOrderId(long var1)
line 103:long id = orderService.getVenderIdByOrderId(45767004381L);
java.lang.NullPointerException
line 103能报空指针的地方,一眼能够看出的两点:参数为空和orderService为空,现在明确告知,这两个都不为空,但是这行代码还是报出来空指针,没错,自动拆箱下潜藏了隐式的空指针错误。因为getVenderIdByOrderId返回的是Long,在赋值给id的时候,如果对方接口返回的是null,Long-->long。见下面转换关系,当obj空的时候,必然就有了java.lang.NullPointerException
基本类型------>包装器类
Long obj=new Long(10L);
包装器类------>基本类型
long num=obj.longValue();
我们在单独学习这种基本语法的时候,心里一般都会明白的很,当发生在实际应用中的时候,却经常被忽略掉这种隐式的空指针异常。
另外,还有一些自动装箱的陷阱,比如下面这段代码,结果可以输出看看。
public static void man(String[] args){ Integer a = 1; Integer b = 2; Integer c = 3; Integer d = 3; System.out.println(c == d); System.out.println(c == (a+b)); System.out.println(c.equals((a+b))); }
包装类的”==“运算符在没有遇到算术运算符的情况下不会自动拆箱,还有equals()方法不处理数据转换的关系。所以我们尽量避免这样的使用。