java的自动拆箱会发生NPE
平时的小细节,总能在关键时刻酿成线上事故,最近在代码中使用了Integer的自动拆箱功能,结果NPE(NullPointException)了,悲剧啊。。。
一、何为自动拆箱
要说自动拆箱,就必须说自动装箱,当然这里拆箱和装箱不是平时的把一个东西放到纸箱子里进行包装的意思,这里的装箱也有包装的意思,但包装的东西却不是可以看的见的物件。
学过java的都知道,java中的数据类型分为基本类型和引用类型,基本数据类型中有byte,short,int,long,char,folat,double,boolean,每种基本类型又有其包装类Byte,Short,Integer,Character,Float,Double,Boolean,这些包装类也可以称之为引用类型,这里的装箱和拆箱说的就是八种基本数据类型和其包装类之间的故事,自动装箱和自动拆箱有好处也有不好的地方,用不好就会造成很大的伤害。
二、事故复现
1、事故重现
这里计划用简单的代码,复现下自动拆箱的NPE,
这里有一个Person类,里边有以下的属性,注意这里我把其age属性的数据类型设置为了Integer
package com.my.unbox; /**
* @author wangcj5
* @date 2022/4/16 11:09
*/
public class Person {
/**
* 年龄
*/
private Integer age;
/**
* 姓名
*/
private String name;
/**
* 家庭住址
*/
private String address; public Integer getAge() {
return age;
} public void setAge(Integer age) {
this.age = age;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public String getAddress() {
return address;
} public void setAddress(String address) {
this.address = address;
}
}
下面直接上测试类
package com.my.unbox; import java.time.Period;
import java.util.Objects; /**
* @author wangcj5
* @date 2022/4/16 11:11
*/
public class TestPerson { private static final int YONG_MAN=18;
private static final int OLD_MAN=60;
public static void main(String[] args) { //正常情况下
Person person=new Person();
person.setAge(16);
System.out.println(isYoung(person));
//非正常情况下
Person person1=new Person();
System.out.println(isYoung(person1));
} /**
* 通过年龄判断一个人是否为少年,小于18
* @param person
* @return
*/
private static boolean isYoung(Person person){
if(Objects.nonNull(person)){
if(YONG_MAN<person.getAge()){
return true; }
}
return false;
}
}
小伙伴们看,测试类也很简单,里边有个方法,判断一个Person对象是否为年轻人,通过其age属性进行判断,那么测试结果如下,
false
Exception in thread "main" java.lang.NullPointerException
at com.my.unbox.TestPerson.isYoung(TestPerson.java:32)
at com.my.unbox.TestPerson.main(TestPerson.java:22) Process finished with exit code 1
在第32行发生了NPE,第32行处的代码如下,
if(YONG_MAN<person.getAge()){
下面来分析下这行代码,首先person肯定不为null,因为上面已经进行了非空判断,那么就说person.getAge()为null,从调用的地方第22行
System.out.println(isYoung(person1));
也就是说person1不为null,那么就是person1中的age属性为null,由于这里仅仅new了一个person对象未对age赋值,那么对于Integer属性的age默认为null,这里也就不奇怪了,问题回到了比较的地方,一个int类型的值和null进行数学比较,这里就会发生拆箱,即把为null的age进行拆箱,在这里发生了NPE。现在就明白了在进行拆箱的时候如果被拆得对象为null肯定会NPE,那么java是如何拆箱的,继续往下看
2、拆箱的本质
要了解拆箱的本质肯定不能草草了事,通过反编译后的代码看下,把TestPerson进行反编译,使用javap命令,
PS C:\05code\Design\target\classes\com\my\unbox> javap -c -p TestPerson.class
得到下面的结果,重点看第32行拆箱的部分,
看上图红框内的,看后面的注释,第一句是调用getAge()方法得到其值,第二句是调用了Integer.intValue()方法,也就是说拆箱调用的Integer.intValue()方法,现在看下该方法的源码,
public int intValue() {
return value;
}
看到吗,就是直接返回value。回到问题的本质为什么会发生NPE,也就是在拆箱时调用intValue()方法由于到到age为null,即,null.intValue(),这不就发生了NPE。
自动拆箱 实际调用的是intValue()方法
下面看自动装箱,看一个int类型的变量如何称为Integer
public class TestBoxing {
public static void main(String[] args) {
Integer integer=10;
}
}
反编译后,
可以看到调用了Integer的valueOf()方法,且该方法是静态的,如下
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
自动装箱 实际调用的静态方法valueOf(int i)方法
三、避坑
上面通过一个小例子,分享了自动拆箱中可能发生的问题,那么应该如何必坑,
1、在自动拆箱的地方进行为null判断;
2、比较的时候尽量做到比较符合两端数据类型一致;
3、平时勤学苦练;
四、总结
自动拆箱时由于调用的是intValue方法,所以如果调用方本身是null的话,肯定会NPE,所以在发生自动拆箱的地方一定要多注意。
自动装箱调用的是静态方法valueOf,自动装箱其实还隐藏了一个更大的秘密,你知道吗,下期见。
编码处处有bug,生活处处有惊喜。对待代码要有敬畏之心,多总结经验。
java的自动拆箱会发生NPE的更多相关文章
- 空指针异常 自动拆箱 防止 NPE,是程序员的基本修养 本手册明确防止 NPE 是调用者的责任。
空指针异常 空指针异常是指java中的异常类. 中文名 空指针异常 外文名 NullPointerException 当应用程序试图在需要对象的地方使用 null 时,抛出该异常.这种情况包括: ...
- JavaStudy——Java之自动拆箱与自动装箱
java基本类型介绍 java中,基本数据类型一共有8种,详细信息如下表: 类型 大小 范围 默认值 byte 8 -128 - 127 0 short 16 -32768 - 32768 0 int ...
- Java语法糖2:自动装箱和自动拆箱
前言 一开始想学学自动拆箱和自动装箱是被这个名字吸引到,听上去好像很高端的样子,其实自动拆箱.自动装箱是很简单的内容. 自动拆箱和自动装箱 Java为每种基本数据类型都提供了对应的包装器类型.举个例子 ...
- java 自动装箱自动拆箱
1.Java数据类型 在介绍Java的自动装箱和拆箱之前,我们先来了解一下Java的基本数据类型. 在Java中,数据类型可以分为两大种,Primitive Type(基本类型)和Reference ...
- 转!!Java学习之自动装箱和自动拆箱源码分析
自动装箱(boxing)和自动拆箱(unboxing) 首先了解下Java的四类八种基本数据类型 基本类型 占用空间(Byte) 表示范围 包装器类型 boolean 1/8 true|fal ...
- java 自动装箱和自动拆箱
自动装箱 java执行Integer i = 100:编译器编译成Integer i = Integer.valueOf(100); Integer i = 100; //编译器编译成Integer ...
- Java中的自动拆箱装箱(Autoboxing&Unboxing)
一.基本类型打包器 1.基本类型:long.int.double.float.boolean 2.类类型:Long.Integer.Double.Float.Boolean 区别:基本类型效率更高,类 ...
- 别说你不知道java中的包装类,wrapper type,以及容易在自动拆箱中出现的问题
很多时候,会有人问你,你知道什么是包装类吗? 或者高端一点问你你知道,wrapper type,是什么吗? 然后你就懵逼了,学了java很多时候都不知道这是啥. 其实问你的人,可能只是想问你,java ...
- JAVA进阶之旅(一)——增强for循环,基本数据类型的自动拆箱与装箱,享元设计模式,枚举的概述,枚举的应用,枚举的构造方法,枚举的抽象方法
JAVA进阶之旅(一)--增强for循环,基本数据类型的自动拆箱与装箱,享元设计模式,枚举的概述,枚举的应用,枚举的构造方法,枚举的抽象方法 学完我们的java之旅,其实收获还是很多的,但是依然还有很 ...
- java基础40 可变参数、自动装箱和自动拆箱
一.可变参数 可变参数是jdk1.5新特性 1.1.可变参数的格式 数据类型...变量名 // 数据类型...变量名public static void sum(int...arr){ } 1.2.可 ...
随机推荐
- Go 指针逃逸分析
引用 https://my.oschina.net/renhc/blog/2222104
- NOIP模拟54
我觉得,不改变也很好. 前言 这题太难了,场上竟然无人切题..(听说别的学校切题的人不少.. T1 选择 解题思路 范围比较小,并且每个边的度也比较小,因此考虑 树形DP+状压 . 大概就是对于每一个 ...
- .NET程序对接 OpenTelemetry logs
OpenTelemetry 简介 OpenTelemetry 是一个由 CNCF(Cloud Native Computing Foundation)托管的开源项目,旨在为观察性(Observabil ...
- k8s中查看pod的yaml文件的案例
在Kubernetes (K8s) 中,Pod 的 YAML 文件定义了 Pod 的配置和规格.当你想要查看 Pod 的 YAML 文件参数参考时,通常是为了了解可以配置哪些字段以及这些字段的含义. ...
- 头条abogus与Js补环境代理Upgrade!
声明 本文章中所有内容仅供学习交流,抓包内容.敏感网址.数据接口均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关,若有侵权,请联系我立即删除! 目标网站 aHR0cHM6 ...
- SELinux(一) 简介
首发公号:Rand_cs 前段时间的工作遇到了一些关于 SELinux 的问题,初次接触不熟悉此概念,导致当时配置策略时束手束脚,焦头烂额,为此去系统的学习了下 SELinux 的东西.聊 SELin ...
- kettle从入门到精通 第六十一课 ETL之kettle 任务调度器,轻松使用xxl-job调用kettle中的job和trans
1.大家都知道kettle设计的job流程文件有个缺点:只能设置简单的定时任务,无法设置复杂的如支持cron表达式的job. 今天给大家分享一个使用xxl-job调度carte的流程文件的示例.整个调 ...
- Stable Diffusion 解析:探寻 AI 绘画背后的科技神秘
AI 绘画发展史 在谈论 Stable Diffusion 之前,有必要先了解 AI 绘画的发展历程. 早在 2012 年,华人科学家吴恩达领导的团队训练出了当时世界上最大的深度学习网络.这个网络能够 ...
- CRP关键渲染路径笔记
关键渲染路径CRP笔记 关键渲染路径(Critical Render Process)是浏览器将HTML.CSS和JavaScript代码转换为屏幕上像素的步骤序列,它包含了DOM(Document ...
- format( )函数
在Python中,DETAIL_URL.format(id=id) 是一个字符串格式化的表达式.它通常用于根据一个已定义的字符串模板 DETAIL_URL 来生成一个新的字符串.在这个模板中,会包含一 ...