NullPointerException底层源码分析

server/2024/10/8 22:36:57/

NullPointerException 是 Java 编程中一个常见的运行时异常,通常在尝试访问或操作一个为 null 的对象引用时抛出。下面是一些常见原因及解决方法:

常见原因

  1. 未初始化的对象:如果对象在使用前没有被实例化,就会引发 NullPointerException。

    String str = null;
    System.out.println(str.length()); // 这里会抛出 NullPointerException
    
  2. 返回值可能为 null 的方法调用:某些方法可能返回 null 值,没有对这些返回值进行检查就直接使用了。

    String str = getString(); // getString() 返回 null
    System.out.println(str.length()); // 这里会抛出 NullPointerException
    
  3. 集合中的元素为 null:对集合中的元素进行操作时,如果元素为 null,也会抛出 NullPointerException。

    List<String> list = new ArrayList<>();
    list.add(null);
    System.out.println(list.get(0).length()); // 这里会抛出 NullPointerException
    

解决方法

  1. 初始化对象

    String str = "";
    System.out.println(str.length()); // 输出 0
    
  2. 在使用前进行 null 检查

    String str = getString();
    if (str != null) {System.out.println(str.length());
    } else {System.out.println("String is null");
    }
    
  3. 使用 Optional 类(Java 8 及以上)

    Optional<String> optionalStr = Optional.ofNullable(getString());
    optionalStr.ifPresent(s -> System.out.println(s.length()));
    
  4. 防御性编程:在设计 API 时,尽量避免返回 null,可以返回空集合、空字符串等。

    public List<String> getList() {return Collections.emptyList(); // 而不是返回 null
    }
    

NullPointerException底层源码分析

NullPointerException 是 Java 中一个常见的运行时异常,属于 java.lang 包。

NullPointerException 类源码

首先,让我们看看 NullPointerException 类的源代码:

package java.lang;public class NullPointerException extends RuntimeException {private static final long serialVersionUID = 5162710183389028792L;/*** Constructs a NullPointerException with no detail message.*/public NullPointerException() {super();}/*** Constructs a NullPointerException with the specified detail message.** @param s the detail message.*/public NullPointerException(String s) {super(s);}
}

从上面的源码可以看出,NullPointerException 类继承自 RuntimeException,并提供了两个构造方法:

  • 无参构造方法:创建没有详细消息的异常对象。
  • 有参构造方法:创建带有详细消息的异常对象。

JVM 如何抛出 NullPointerException

NullPointerException 的抛出过程是由 JVM 在运行时自动完成的,而不是通过显式调用 new NullPointerException() 来实现的。

字节码分析

对于下面这段代码:

String str = null;
str.length();

编译后生成的字节码如下(使用 javap -c):

0: aconst_null
1: astore_1
2: aload_1
3: invokevirtual #1   // Method java/lang/String.length:()I
6: pop
7: return

解释每条指令:

  • 0: aconst_null 将 null 压入栈顶。
  • 1: astore_1 将栈顶的 null 存储到局部变量表的索引为1的位置(即 str)。
  • 2: aload_1 将局部变量表中索引为1的值(即 str)压入栈顶。
  • 3: invokevirtual #1 尝试调用栈顶对象的 length() 方法。

在执行 invokevirtual 指令时,如果栈顶的对象引用是 null,JVM 就会抛出 NullPointerException

JVM 源码分析

在 OpenJDK 中,invokevirtual 指令的实现位于 bytecodeInterpreter.cpp 文件中。以下是简化后的伪代码描述:

case Bytecodes::_invokevirtual: {oop receiver = STACK_OBJECT(-number_of_arguments); if (receiver == NULL) {THROW(vmSymbols::java_lang_NullPointerException());}// 继续方法调用流程
}
  • oop receiver = STACK_OBJECT(-number_of_arguments); 获取调用对象的引用。
  • if (receiver == NULL) 检查引用是否为 null
  • THROW(vmSymbols::java_lang_NullPointerException()); 抛出 NullPointerException

总结

NullPointerException 是由 JVM 在检测到空引用操作时自动抛出的,它继承自 RuntimeException。通过查看底层字节码和 JVM 源码,我们可以更清楚地理解 NPE 的抛出机制。


http://www.ppmy.cn/server/126385.html

相关文章

Linux之进程概念

作者主页&#xff1a; 作者主页 本篇博客专栏&#xff1a;Linux专栏 创作时间 &#xff1a;2024年9月28日 基本概念&#xff1a; 进程说白了其实就是一个程序的执行实例&#xff0c;正在执行的程序。 在内核层面来说&#xff0c;就是一个担当分配资源&#xff08;CPU时间…

cfg80211是怎么配置无线设备的AP的?

cfg80211 概念 cfg80211 是 Linux 内核中一个用于无线网络的配置和管理的子系统&#xff0c;它为多个无线网络驱动提供了一个统一的接口&#xff0c;以便于无线设备的配置和操作。cfg80211 的设计目标是提供一个简化的管理和配置无线设备的框架&#xff0c;同时支持多种无线设…

OpenCV视频I/O(3)视频采集类VideoCapture之获取当前使用的视频捕获 API 后端的名称函数getBackendName()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 getBackendName 函数是 OpenCV 中 VideoCapture 类的一个方法&#xff0c;用于获取当前使用的视频捕获 API 后端的名称。这可以帮助开发者了解当…

awd基础学习

一、常用防御手段 1、改ssh密码 passwd [user] 2、改数据库密码 进入数据库 mysql -uroot -proot 改密码 update mysql.user set passwordpassword(新密码) where userroot; 查看用户信息密码 select host,user,password from mysql.user; 改配置文件 &#xff08;否则会宕机…

Qt 中的 QListWidget、QTreeWidget 和 QTableWidget:简化的数据展示控件

Qt 中的 QListWidget、QTreeWidget 和 QTableWidget&#xff1a;简化的数据展示控件 在 Qt 的用户界面开发中&#xff0c;展示和管理数据是常见的需求。Qt 提供了丰富的控件供开发者选择&#xff0c;其中 QListWidget、QTreeWidget 和 QTableWidget 是三个高层封装控件&#x…

VS与VSCode的区别

文章目录 1. 什么是 Visual Studio 和 Visual Studio Code&#xff1f;Visual Studio&#xff08;VS&#xff09;Visual Studio Code&#xff08;VS Code&#xff09; 2. 主要区别详解性能和资源占用功能和复杂性扩展和自定义适用场景价格 3. 详细对比总结4. 如何选择适合自己的…

docker export/import 和 docker save/load 的区别

Docker export/import 和 docker save/load 都是用于容器和镜像的备份和迁移&#xff0c;但它们有一些关键的区别&#xff1a; docker export/import: export 作用于容器&#xff0c;import 创建镜像导出的是容器的文件系统&#xff0c;不包含镜像的元数据丢失了镜像的层级结构…

数据结构-单链表

题目描述 输入n个整数&#xff0c;逐个读入建立一个单链表&#xff0c;然后将该单链表拆分成两个子链表&#xff0c;第一个子链表存放所有的偶数&#xff0c;第二个子链表存放所有的奇数&#xff0c;两个子链表中数据的相对次序与原链表一致。 测试 测试用例1 输入: 输入分…