解决一个诡异的java空指针问题的案例

news2024/11/19 6:43:34

最近在看java类加载器的资料,于是写了一个自定义类加载器测试一下,结果就悲剧了,直接报空指针!

 跟着报错指引看代码37行是什么东东?

 就是一个inputStream, 然后看看它的定义:

 这玩意就是从classpath读取class字节码文件,然后转换成IO流,并没有什么问题啊。

打个断点看看,发现inputStream是有值的:

 这他妈是什么问题呢,一时间觉得很迷茫,不知从何入手。

而且最诡异的是一旦加上下面一行return的代码,空指针报错就没了!

if (clazz !=null) return clazz; 

 这是不是很诡异,看来只能重新再次debug了。

再次调试发现inputStream仍然是有值的,但是走完这段代码以后发现该代码又会再来一遍:

 此时的类名变成了Object.class!但是之前的读取的类名应该是ClassData.class, 如下所示:

 看来这就是问题的关键了! 可以看到此时的inputStream真的是空值null  

正是因为从classpath读取不到java.lang.Object的字节码文件,导致读取IO时生成了null,然后在创建字节数组时就引发了空指针异常

那为什么加了if判断的return代码就不报空指针错误了呢?

那是因为此时clazz是有值的:class java.lang.Object, 为什么有值呢?因为Object.class虽然从classPath的物理路径读取不到字节码文件,但是通过super.load(name)的父类方法(累加载器应该是AppClassLoader),jvm能够从内存中读取到java.lang.Object.class的字节码文件!

然后它会根据下面的if判断直接return clazz,而不会执行后面创建字节数组的代码,从而避免了空指针的发生。

 以上分析完毕,可以看到关于类加载器的知识,里面的水还是很深的,需要仔细分析才能挖掘到深层次的东西

然后附上完整代码:

public class ClassLoaderTest2 {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        ClassLoader myClassLoader = new ClassLoader() {
            // 自定义类加载器
            @Override // 重写loadClass方法
            public Class<?> loadClass(String name) throws ClassNotFoundException {
                Class<?> clazz = null;
                // 下面的代码getSystemClassLoader使用了$AppClassLoader来加载,这样就仍然保持了双亲委派机制
//                ClassLoader loader = getSystemClassLoader();
//                try {
//                    clazz = loader.loadClass(name);
//                } catch (ClassNotFoundException e) {
//                    e.printStackTrace();
//                }
//
//                if (clazz != null) {
//                    return clazz;
//                }

                // 读取classPath下编译生成的class字节码文件,然后转化成byte字节流
                String fileName = name.substring(name.lastIndexOf(".") + 1) + ".class";
                InputStream inputStream = getClass().getResourceAsStream(fileName);
                if ( inputStream == null) {
                    clazz = super.loadClass(name);
                }

                // 当name入参为Object时,直接return clazz,跳过后面代码的执行,避免产生空指针问题。
              //  if (clazz !=null) return clazz;

                // 下面的代码实际打破了双亲委托机制,使得自定义classLoader生效,从而跟AppClassLoader发生冲突
                // 导致同名Class的实例对象转换异常,因为类加载器不一样了
                try {
                    byte[] bytes = new byte[inputStream.available()];
                    inputStream.read(bytes);
                    // defineClass方法将byte字节流转换成jvm能识别的Class对象
                    clazz = defineClass(name, bytes, 0, bytes.length);

                } catch (IOException exception) {
                    throw new ClassNotFoundException(name);
                }
                return clazz;
            }
        };


        // 通过自定义类加载器myClassLoader来加载类ClassData并完成实例化
        Object clazz = myClassLoader.loadClass("com.mybest.ClassLoaderStudy.ClassData").newInstance();
        System.out.println("clazz self is ======>>>" + clazz.getClass());
        System.out.println("clazz's classLoader is " + clazz.getClass().getClassLoader());
        // 判断通过自定义类加载器myClassLoader生成的clazz是否还是ClassData类型
        // 从这里可以看到如果类加载器不同,即便Class类型相同,JVM仍然认为是两个不同的类
        System.out.println("clazz instanceof ClassData==> " + (clazz instanceof ClassData));
        System.out.println("ClassData's classLoader is " + ClassData.class.getClassLoader());
        // 所以这里会发生ClassCastException,因为类加载器不同:一个是自定义的类加载器myClassLoader,
        // 一个是系统类加载器:AppClassLoader 
        ClassData clazz2 = (ClassData) clazz;
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/541595.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

html实现一个一闪一闪的按钮,CSS实现一个一闪一闪的按钮,Css闪烁点标,css设置按钮层次感,css按钮美化,CSS按钮动画过渡,CSS按钮添加阴影

效果 动态 静态 实现 底部多加了几个过渡按钮 <!DOCTYPE html> <html><head><meta charset"UTF-8"><title></title><style>#app {margin: 2% auto;text-align: center;}.lay-btn-box {position: relative;display: …

【达梦数据库】达梦数据库windows安装

目录 1.选择语言与时区 2.安装向导 3.许可证协议 4.验证 Key 文件 5.选择安装组件 6.选择安装目录 7.目录确认 8.开始安装 9.安装过程 10.安装完成 11.创建数据库实例 12.创建数据库模板 13.数据库目录 14.数据库标识 15.数据库文件 16.初始化参数 17.口令管理…

VoxelNeXt:用于3D检测和跟踪的纯稀疏体素网络

VoxelNeXt:Fully Sparse VoxelNet for 3D Object Detection and Tracking 目前自动驾驶场景的3D检测框架大多依赖于dense head&#xff0c;而3D点云数据本身是稀疏的&#xff0c;这无疑是一种低效和浪费计算量的做法。我们提出了一种纯稀疏的3D 检测框架 VoxelNeXt。该方法可以…

电脑断电后无法正常启动怎么办?

电脑断电后无法正常启动是一个很常见的问题&#xff0c;其实除断电外&#xff0c;电脑强制关机后无法正常启动也很常见&#xff0c;出现这个问题一般是由硬件导致&#xff0c;可能是内存、电源、主板、显卡、硬盘等硬件出现问题&#xff0c;尤其是一瞬间断电再来电&#xff0c;…

全网最牛最全面的接口自动化接口关联的三个层次

一、&#xff08;接口查询的条件分析&#xff09; 1.一般来说&#xff0c;在所有平台中&#xff0c;凡是往数据库里增加接口&#xff0c;必然有相应的查询接口和修改操作的接口 2.接口的后台服务除了要把数据返回给我们之外&#xff0c;还要把真正对数据的修改操作写入数据库…

linux系统学习

本文建立于Linux的课堂学习 文章目录 Linux基础1. Linux操作环境1.1 简述Linux文件类型有哪些1.2 简述Linux的文件访问权限1.3 简述shell的功能&#xff0c;常见的shell有几种1.4 列举几个常用的Shell环境变量以及用途 2. Linux Shell命令操作2.1 简述在Linux Shell中获取帮助…

数据结构总结6:八大排序

后续会有补充 排序 排序&#xff1a;按照某个或某些关键字的大小&#xff0c;递增或递减排列起来的操作。 稳定性&#xff1a;假定在待排序的记录序列中&#xff0c;存在多个具有相同的关键字的记录&#xff0c;若经过排序&#xff0c;这些记录的相对次序保持不变&#xff0c…

如何完美卸载VS2015(2023年5月份实测有效)

使用控制面板卸载VS2015&#xff0c;出现正在配置您的系统&#xff0c;这可能需要一些时间&#xff0c;然后就出现卡住半个小时第二行的条都没有动的问题&#xff0c;这里提供vs2015以及以前版本的卸载方式 问题产生原因:他需要下载一些东西&#xff0c;然后由于你懂的网络原因…

基于yolov3训练自己的数据集

训练数据集的教学视频链接 42. 第六章&#xff1a;基于YOLO-V3训练自己的数据集与任务_哔哩哔哩_bilibili 数据打标签 下载labelme标注工具 通过pip install labelme下载&#xff0c;打开anaconda prompt&#xff0c;切换到下载labelme的环境&#xff08;我的是pytorch&…

torch显存分析——如何在不关闭进程的情况下释放显存

torch显存分析——如何在不关闭进程的情况下释放显存 1. 基本概念——allocator和block2. torch.cuda的三大常用方法3. 可以释放的显存4. 无法释放的显存&#xff1f;5. 清理“显存钉子户” 一直以来&#xff0c;对于torch的显存管理&#xff0c;我都没有特别注意&#xff0c;只…

ffmpeg mkv 文件解析

一、mkv的文件组织 1. EBML基本单元 EBML组成mkv文件最基本的单元&#xff0c; 也是解析文件最小的一个粒度。EBML基本元素结构&#xff1a; ID&#xff1a;标志着这个EMBL 是一个什么类型的&#xff0c;类型决定了后面data中存储的是什么类型的数据如是int&#xff0c;string…

腾讯云备案限制条件说明(必看)

腾讯云网站备案要求首先你有一个需要备案的域名&#xff0c;域名实名认证信息和备案主体相同&#xff1b;在腾讯云有一台符合备案条件的云服务器、轻量应用服务器等云产品&#xff1b;然后根据备案主体所在省份地区&#xff0c;符合当地的通信管理局要求。下面腾讯云百科来详细…

Centos7系统常用命令

一、防火墙firewalld、sestatus 1 查看防火墙状态&#xff1a;systemctl status firewalld 2 关闭运行的防火墙&#xff1a;systemctl stop firewalld.service 开启运行的防火墙&#xff1a;systemctl start firewalld.service 3 禁止防火墙服务器&#xff1a;systemctl di…

如何一行代码实现 OpenAI 可观测,大幅提升使用体验

作者&#xff5c;观测云 徐季秋 现在基于 OpenAI 的 Chat 应用井喷&#xff0c;但给开发者带来了两个难点&#xff0c;一是因为 OpenAI 基于 tokens 的计费机制导致不容易规划消费&#xff1b;另一是 OpenAI 提供的调用本身不稳定&#xff0c;很难分辨是传参错误或是访问失败。…

flume 进阶 Ganglia 部署(十二)

规划安装 hadoop100: web gmetad gmod epel-release hadoop101: gmod epel-release hadoop102: gmod epel-release 安装 三台都安装 sudo yum -y install epel-releasesudo yum -y install ganglia-gmond在hadoop100安装 sudo yum -y install ganglia-gmetadsudo yum -y i…

RT-Thread 2. GD32在 RT-Thread Nano上添加控制台与 FinSH

本篇文档分为两部分&#xff1a; 第一部分是添加 UART 控制台&#xff08;实现打印&#xff09;&#xff1a;用来向控制台对接的终端输出打印信息&#xff1b;该部分只需要实现两个函数&#xff0c;串口初始化和系统输出函数&#xff0c;即可完成 UART 控制台打印功能。 第二部…

sonarqube上的webAPI如何调用?-暴躁了一天调通了

首先吐槽一句&#xff0c;官方API文档给了个寂寞&#xff0c;调不通啊&#xff0c;然后查各种搞了一天&#xff0c;竟然没有一篇文章能把这件事写清楚&#xff0c;给我暴躁的。 结论竟然是原来是我不会调用接口。。。我今天非要把这篇文章写好。 web接口入口 通过sonarqube下…

QT开发实战-动态壁纸软件

动态壁纸软件开发 项目源代码在下面链接获取: ----------------------------- 开发者:CodeSharkSJ 希望此项目能加强你对Qt的应用 文章目录 项目图与开发环境核心技术原理自定义窗口程序UI布局背景绘制样式表基本实现QWebEngineQMedia使用系统托盘隐藏记忆功能应用程序打包 …

四年巨亏49亿,第四范式四闯IPO

深陷亏损的AI公司第四范式&#xff0c;四闯IPO&#xff01; 4月24日&#xff0c;决策类AI独角兽北京第四范式智能技术股份有限公司&#xff08;下称“第四范式”&#xff09;再次更新招股书&#xff0c;继续向港交所发起上市冲击。 第四范式是一家专注于提供以平台为中心的人…

(9) 线性回归

文章目录 1 多元线性回归LinearRegression1.1 基本原理1.2 linear_model.LinearRegression 2 回归类的模型评估指标2.1 是否预测了正确的数值2.2 是否拟合了足够的信息 3 岭回归与Lasso回归3.1 多重共线性3.2 岭回归3.2.1 linear_model.Ridge3.2.2 选取最佳的正则化参数 α \al…