平时的小细节,总能在关键时刻酿成线上事故,最近在代码中使用了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. Uni-app极速入门(二) - 登录demo

    需求 背景 1.进入小程序,默认页面判断用户是否已经登录,已经登录则进入首页,没有登录则进入登录页面 2.首页为tabbar,包括首页和设置页,设置页可以退出登录,回到登录页面 页面流转 graph ...

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

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

  3. 搭建k8s集群完整版本

    搭建k8s集群完整版 基础设置 设置主机ip nmcli con add ifname ens33 con-name ens33 autoconnect yes type ethernet nmcli ...

  4. 绘图与可视化--matplotlib API入门

    matplotlib API函数都位于matplotlib.pyplot模块中. 本节代码中引入的约定为:import matplotlib.pyplot as plt 另外,numpy库也会用到,约 ...

  5. OPA Gatekeeper:Kubernetes的策略和管理

    目录 一.系统环境 二.前言 三.OPA Gatekeeper简介 四.在kubernetes上安装OPA Gatekeeper 五.gatekeeper规则 5.1 使用gatekeeper禁止某些 ...

  6. YNOI 做题记

    YNOI 做题记 偶然有一天做到了其中的一道题,于是便开始做相关的题了-- [Ynoi2015] 我回来了 - 洛谷 这之一场联考搬过来的题--于是考场上写了一个 \(O((n + m)\log^2 ...

  7. MyBatis 关于查询语句上配置的详细内容

    1. MyBatis 关于查询语句上配置的详细内容 @ 目录 1. MyBatis 关于查询语句上配置的详细内容 2. 准备工作 3. SQL查询结果,返回为POJO实体类型 4. SQL查询结果,返 ...

  8. apache开源 国内镜像地址

    https://mirrors.tuna.tsinghua.edu.cn/apache/kylin/apache-kylin-3.1.1/

  9. 支撑阻力指标,庄家成本价是可靠的支撑位(无未来,DLL加密)

    本指标依据庄家的成本价设计的,庄家成本价是可靠的支撑位.底层逻辑:庄家是有内幕的, 庄家能在价格低位时抄底,庄家控股时,庄家不会让散户获取低价的筹码,所以当股价到达到支撑位时,会有比较大的反弹.庄家也 ...

  10. linux下安装oracle 11g(静默安装)

    关闭selinux 关闭防火墙 检查安装依赖包 yum -y install binutils compat-libcap1 vsftpd gcc gcc-c++ glibc-devel glibc ...