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
上面两个程序的区别在哪里呢? 仔细发现第二个程序的agestatic修饰的, 这样有什么区别呢? 又为什么没有发生空指针异常呢?
如果不理解为什么不发生空指针异常的话, 可以通过反编译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的类型不再是基本的数据类型, 而是引用数据类型[包装类],
那么 当agestatic修饰时, 它的默认初始值就成了null
这个时候特别注意, test()方法的入参类型是 int,即基本数据类型。
Java中由包装类 -> 基本数据类型, JDK1.5以后有自动拆箱的功能。那么这个自动拆箱又到底是怎么做的呢?
我们通过查看Integer的源码可以发现是如下代码:

扫描二维码关注公众号,回复: 14615392 查看本文章
public int intValue() {
    
    
        return value;
    }

我们看到 intValue()方法是Integer的普通成员方法, 而非静态方法
当传入向test()方法中传入null, 而 null又需要拆箱为基本数据类型时, 会调用普通的成员方法intValue(), 这样就会产生上述我们总结的问题, null.intValue(), 就会发生空指针异常。
所以代码中的空指针异常不是由stu.age产生的, 而是由自动拆箱产生的!

这下我们应该对空指针异常有了一个更加正确的认识了!

4. 留个问题

第三部分中我们提到, Java中Integer的自动拆箱是调用了intValue()方法, 那么自动装箱调用的是哪个方法呢?
欢迎大家留言讨论交流!!!

欢迎大家留言一起讨论学习!

猜你喜欢

转载自blog.csdn.net/Steven_Start/article/details/125526198