Java空指针异常的正确理解
1. 到底为什么会发生空指针异常?
一开始我简单的认为空指针就是当对象为null
时, 使用这个为null
的对象,调用了该对象的某个属性
或者方法
。
如下代码所示:
public class TestNullPointer {
public static void main(String[] args) {
Student stu = null;
System.out.println(stu.age);
}
}
class Student{
int age;
}
// 执行结果: NullPointerException 控制针异常
可是原因真的是这样吗?
再看下面这段代码
public class TestNullPointer {
public static void main(String[] args) {
Student stu = null;
System.out.println(stu.age);
}
}
class Student{
static int age;
}
// 执行结果: 0
上面这段代码并未发生空指针异常, 执行结果为0
上面两个程序的区别在哪里呢? 仔细发现第二个程序的age
是static
修饰的, 这样有什么区别呢? 又为什么没有发生空指针异常呢?
如果不理解为什么不发生空指针异常的话, 可以通过反编译class文件查看其中的内容:
# 反编译的命令: javap -v class的名称(不要加后缀名)
7: getstatic #3 // Field cn/com/gsfunds/test1/Student.age:I
我们可以看到这么一行内容, 使用stu.age
时, 实际上取的是Student.age
, 原理也很简单, java中静态的成员属性被所有的实例对象共享
, 使用对象.静态属性名
, 实质上就是调用: 类名.静态属性名
这样理解就可以看到为什么不报错了!
2. 简单总结
通过第一部分的两个简单的示例代码, 我们总结出来, 如果对象为空, 调用的是普通的成员变量或者成员属性时, 的确会发生空指针异常, 如果为null
的对象调用的是静态的属性或者静态方法时, 是不会产生空指针异常的!
那么到底什么时候会发生空指针异常呢, 我们总结成一句话:
如果一个为空(null)对象, 调用了它非静态的成员属性或者成员方法时, 会发生空指针异常
3. 再来个例子, 看自己理解的怎么样?
阅读下面这段代码:
public class TestNullPointer {
public static void main(String[] args) {
Student stu = null;
test(stu.age);
}
public static void test(int age) {
System.out.println(age);
}
}
class Student{
static Integer age;
}
这段代码中, stu.age
还是用的静态的成员属性, 等同于Student.age
, 那会不会发生空指针异常呢?
答案是: 肯定会
那这段代码感觉推翻了上面的总结了, 是吗? 其实并没有, 我们认真分析一下代码
代码中age
的类型不再是基本的数据类型, 而是引用数据类型[包装类]
,
那么 当age
为static
修饰时, 它的默认初始值就成了null
。
这个时候特别注意, test()
方法的入参类型是 int
,即基本数据类型。
Java中由包装类 -> 基本数据类型, JDK1.5以后有自动拆箱的功能。那么这个自动拆箱又到底是怎么做的呢?
我们通过查看Integer
的源码可以发现是如下代码:
public int intValue() {
return value;
}
我们看到 intValue()
方法是Integer
的普通成员方法, 而非静态方法
当传入向test()
方法中传入null
, 而 null
又需要拆箱为基本数据类型时, 会调用普通的成员方法intValue()
, 这样就会产生上述我们总结的问题, null.intValue()
, 就会发生空指针异常。
所以代码中的空指针异常不是由stu.age
产生的, 而是由自动拆箱
产生的!
这下我们应该对空指针异常有了一个更加正确的认识了!
4. 留个问题
第三部分中我们提到, Java中Integer
的自动拆箱是调用了intValue()
方法, 那么自动装箱调用的是哪个方法呢?
欢迎大家留言讨论交流!!!
欢迎大家留言一起讨论学习!