平时的小细节,总能在关键时刻酿成线上事故,最近在代码中使用了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的更多相关文章

  1. 空指针异常 自动拆箱 防止 NPE,是程序员的基本修养 本手册明确防止 NPE 是调用者的责任。

    空指针异常 空指针异常是指java中的异常类.   中文名 空指针异常 外文名 NullPointerException 当应用程序试图在需要对象的地方使用 null 时,抛出该异常.这种情况包括: ...

  2. JavaStudy——Java之自动拆箱与自动装箱

    java基本类型介绍 java中,基本数据类型一共有8种,详细信息如下表: 类型 大小 范围 默认值 byte 8 -128 - 127 0 short 16 -32768 - 32768 0 int ...

  3. Java语法糖2:自动装箱和自动拆箱

    前言 一开始想学学自动拆箱和自动装箱是被这个名字吸引到,听上去好像很高端的样子,其实自动拆箱.自动装箱是很简单的内容. 自动拆箱和自动装箱 Java为每种基本数据类型都提供了对应的包装器类型.举个例子 ...

  4. java 自动装箱自动拆箱

    1.Java数据类型 在介绍Java的自动装箱和拆箱之前,我们先来了解一下Java的基本数据类型. 在Java中,数据类型可以分为两大种,Primitive Type(基本类型)和Reference ...

  5. 转!!Java学习之自动装箱和自动拆箱源码分析

    自动装箱(boxing)和自动拆箱(unboxing)   首先了解下Java的四类八种基本数据类型   基本类型 占用空间(Byte) 表示范围 包装器类型 boolean 1/8 true|fal ...

  6. java 自动装箱和自动拆箱

    自动装箱 java执行Integer i = 100:编译器编译成Integer i = Integer.valueOf(100); Integer i = 100; //编译器编译成Integer ...

  7. Java中的自动拆箱装箱(Autoboxing&Unboxing)

    一.基本类型打包器 1.基本类型:long.int.double.float.boolean 2.类类型:Long.Integer.Double.Float.Boolean 区别:基本类型效率更高,类 ...

  8. 别说你不知道java中的包装类,wrapper type,以及容易在自动拆箱中出现的问题

    很多时候,会有人问你,你知道什么是包装类吗? 或者高端一点问你你知道,wrapper type,是什么吗? 然后你就懵逼了,学了java很多时候都不知道这是啥. 其实问你的人,可能只是想问你,java ...

  9. JAVA进阶之旅(一)——增强for循环,基本数据类型的自动拆箱与装箱,享元设计模式,枚举的概述,枚举的应用,枚举的构造方法,枚举的抽象方法

    JAVA进阶之旅(一)--增强for循环,基本数据类型的自动拆箱与装箱,享元设计模式,枚举的概述,枚举的应用,枚举的构造方法,枚举的抽象方法 学完我们的java之旅,其实收获还是很多的,但是依然还有很 ...

  10. java基础40 可变参数、自动装箱和自动拆箱

    一.可变参数 可变参数是jdk1.5新特性 1.1.可变参数的格式 数据类型...变量名 // 数据类型...变量名public static void sum(int...arr){ } 1.2.可 ...

随机推荐

  1. CSS——选择器的优先级

    所谓CSS优先级,即是指CSS样式在浏览器中被解析的先后顺序.样式表中的特殊性描述了不同规则的相对权重. !important > 行内样式>ID选择器 > 类选择器 > 标签 ...

  2. 利用instruments工具查看其它app的性能

    1.随便建立一个新的工程文件,profile,进入instruments 2.选中activity,可以看到所有进程,以及其它进程的CPU占用情况

  3. LLM 大模型学习必知必会系列(十二):VLLM性能飞跃部署实践:从推理加速到高效部署的全方位优化[更多内容:XInference/FastChat等框架]

    LLM 大模型学习必知必会系列(十二):VLLM性能飞跃部署实践:从推理加速到高效部署的全方位优化[更多内容:XInference/FastChat等框架] 训练后的模型会用于推理或者部署.推理即使用 ...

  4. node child_process模块exec

    child_process是Node.js自带的核心模块之一,无需额外安装即可使用. child_process模块提供了创建子进程的功能,可以在Node.js中执行外部命令.脚本文件等,并与其进行交 ...

  5. JVM垃圾回收器(详解)

    引言 垃圾回收(GC,Garbage Collection) 在笔者上一篇文章中(JVM内存模型),介绍了JVM内存模型以及JVM运行时的数据区,堆是JVM内存区域里面最大的一块区域,用于存放实例数据 ...

  6. python logger 打印日志错误行数

    python logger 打印日志错误行数 import logging app = Flask(__name__) # 配置日志 handler = logging.FileHandler('ap ...

  7. 剖析 Kafka 消息丢失的原因

    目录 前言 一.生产者导致消息丢失的场景 场景1:消息体太大 解决方案 : 1.减少生产者发送消息体体积 2.调整参数max.request.size 场景2:异步发送机制 解决方案 : 1.使用带回 ...

  8. python 使用pandas修改数据到excel,报“SettingwithCopyWarning A value is trying to be set on a copy of a slice from a DataFrame”的解决方法

    场景: 通过pandas模块,将测试数据回写到excel,测试数据有写到excel文件,但控制台输出警告信息如下 警告: SettingwithCopyWarning A value is tryin ...

  9. 【论文阅读】Learning to drive from a world on rails

    引用与参考 代码地址:https://github.com/dotchen/WorldOnRails 论文地址:https://arxiv.org/abs/2105.00636 论文部分 已看完 写在 ...

  10. Java反射与Fastjson的危险反序列化

    Preface 在前文中,我们介绍了 Java 的基础语法和特性和 fastjson 的基础用法,本文我们将深入学习fastjson的危险反序列化以及预期相关的 Java 概念. 什么是Java反射? ...