首页 > 编程语言 >[Java基础]自动装箱与自动拆箱--为什么整型比较必须用equals?

[Java基础]自动装箱与自动拆箱--为什么整型比较必须用equals?

时间:2023-02-21 20:59:23浏览次数:34  
标签:拆箱 Java int valueOf 自动 127 Integer 装箱

偶然在项目里看到了下面这行代码,大家觉得这个if判断会存在什么问题吗?

if (129 == StatusEnum.OK.getCode()) {//其中OK是Integer code =129
    System.out.println("ok");
}

枚举定义如下:

@Getter
public enum StatusEnum {
    OK(129, "ok"),
    ERROR(0, "error"),
    ;
    private Integer code;
    private String desc;

    StatusEnum(Integer code, String desc) {
        this.code = code;
        this.desc = desc;
    }
}

在看这个问题前,我们先了解一下 自动装箱、自动拆箱

自动装箱、自动拆箱

1.定义

  • 自动装箱:自动将基本数据类型转换为封装类型
  • 自动拆箱:自动将封装类型转换为基本数据类型

关于数据类型,Java中定义了4大类基础的数据类型及其对应的封装类型

  • 整数:byte、int、short、long
  • 浮点数:float、double
  • 字符类型:char
  • 布尔类型:boolean

其对应关系如下:

基础类型 封装类型
byte Byte
int Integer
short Short
long Long
float Float
double Double
char Char
boolean Boolean

2.如何使用

使用起来非常简单,当=两端的类型分别是基础类型和其对应的封装类型时就会进行自动装箱或拆箱。

// 自动装箱
Integer a = 100;
// 自动拆箱
int b = a;

3.实现原理

  • 自动装箱:Java编译器为我们自动执行了 Integer.valueOf(int i),这是Integer提供的静态方法(Static Method);
/**
 * Returns an {@code Integer} instance representing the specified
 * {@code int} value.  If a new {@code Integer} instance is not
 * required, this method should generally be used in preference to
 * the constructor {@link #Integer(int)}, as this method is likely
 * to yield significantly better space and time performance by
 * caching frequently requested values.
 *
 * This method will always cache values in the range -128 to 127,
 * inclusive, and may cache other values outside of this range.
 *
 * @param  i an {@code int} value.
 * @return an {@code Integer} instance representing {@code I}.
 * @since  1.5
 */
public static Integer valueOf(int i) {
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}
  • 自动拆箱:Java编译器为我们自动执行了Integer.intValue(int i),这是Integer提供的实例方法方法(Instance Method);
/**
 * Returns the value of this {@code Integer} as an
 * {@code int}.
 */
public int intValue() {
    return value;
}

4.优点和缺点

通常引入新的解决方案的同时,也会引入新的问题,自动装箱、自动拆箱也不例外。

1.优点

基础数据类型只能满足最基础的数据使用,封装类型为我们提供了许多高级的使用场景。以String为例它为我们提供了很多字符串的查找、拼接、大小写转换等方法(见下图)。而自动装箱、自动拆箱在不影响我们日常使用的情况下让我们更灵活的在基础和封装类型中切换。
String提供的方法

2.缺点

1. NullPointException
由于自动拆箱时使用的Integer.intValue(int i)是实例方法,使用时需要Integer的实例存在才行,否则会产生NullPointException。例子如下:

Integer i = null;
boolean eq = 5 == i;//此处会抛出NullPointException

2. 缓存
自动装箱时使用了Integer.valueOf(int i),而在待装箱值iIntegerCache缓存范围[-128, 127]内时会优先使用缓存的Integer对象。具体如下:

public static Integer valueOf(int i) {
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}

我们看下下面这个例子:

int i = 127;
Integer one = I;
Integer two = I;
System.out.println(one == two);//进行地址比较
System.out.println(one.equals(two));//进行值比较

i=127时输出结果如下:

true
true

i=128时输出结果如下:

false
true

为什么不同的int装箱后的==比较结果不一样?
自动装箱的出来的对象,一般会默认为new构造出来的,两次自动装箱的对象应该是不同的对象,而整型的比较一般默认是进行值比较。

由于int在经过自动装箱时会使用[-128, 127]范围内的缓存对象,导致结果超出预期。

所以在使用自动装箱和自动拆箱时,大家要注意其背后的原理。

在了解了Integer Cache后,我们再看下文章开头提到的整型比较:

if (0 == StatusEnum.OK.getCode()) {//其中OK是Integer
    System.out.println("ok");
}

可以简化为下面的例子

System.out.println(127 == Integer.valueOf(127));
System.out.println(Integer.valueOf(127) == Integer.valueOf(127));

System.out.println(128 == Integer.valueOf(128));
System.out.println(Integer.valueOf(128) == Integer.valueOf(128));

System.out.println(129 == Integer.valueOf(129));//I比较Integer
System.out.println(Integer.valueOf(129) == Integer.valueOf(129));//Integer比较Integer

结果如下:

true
true
true
false

具体分析如下:

System.out.println(127 == Integer.valueOf(127));
// true,int 127的自动装箱会使用缓存,但是最终拆箱成int和int比较

System.out.println(Integer.valueOf(127) == Integer.valueOf(127));
// true,int 127的自动装箱会使用缓存对象,缓存对象相同

System.out.println(128 == Integer.valueOf(128));
// true,int 128的自动装箱会使用缓存,但是最终拆箱成int和int比较

System.out.println(Integer.valueOf(128) == Integer.valueOf(128));
// false,int 128的自动装箱不使用缓存,对象和对象比较不一样

大家可以看出以下结论:

  1. 当基本数据类型与封装类型进行==比较时,会进行拆箱比较;
  2. 封装类型与封装类型进行``==```比较时,等同于对象比较对象,会进行对象地址比较;
  3. Integer的自动装箱会为[-128, 127]范围内的值优先使用缓存对象

所以整型在进行比较时,大家不要使用==进行比较,参考下面Java开发手册(嵩山版)2020.08修订1.7.0截取内容:

  1. 【强制】所有整型包装类对象之间值的比较,全部使用 equals 方法比较。
    说明:对于 Integer var = ? 在-128 至 127 之间的赋值,Integer 对象是在 IntegerCache.cache 产生,
    会复用已有对象,这个区间内的 Integer 值可以直接使用==进行判断,但是这个区间之外的所有数据,都
    会在堆上产生,并不会复用已有对象,这是一个大坑,推荐使用 equals 方法进行判断。

5. 延伸问题:

为什么JDK设计时,Integer的自动装箱会为[-128, 127]范围内的值优先使用缓存对象?

标签:拆箱,Java,int,valueOf,自动,127,Integer,装箱
From: https://www.cnblogs.com/bingshuang/p/17142357.html

相关文章

  • 智能文本自动处理(Intelligent text automatic processing)(二)
    AutoText智能文本自动处理工具(Intelligenttextautomaticprocessingtool)。项目地址:https://github.com/jiangnanboy/AutoTextAutoText的功能主要有文本纠错,图片ocr以......
  • 如何利用javaweb实现数据的可视化
    描述之前一直使用html进行网页版的数据库查询啥的,没有图片的参与,也没有将一条条数据变成较为直观的图画形式,这就是来实现以下数据的图画形式了解及基础说明通过查阅资料......
  • Java多线程技能-线程的启动
    java多线程技能技术点:线程的启动如何使线程暂停如何使线程停止线程的优先级线程安全相关的问题进程和线程的定义及多线程的优点进程:进程是受操作系统管理的基本......
  • Java IO模型
    什么是IOIO是输入input输出output的首字母缩写形式,直观意思是计算机输入输出,它描述的是计算机的数据流动的过程;应用程序的IO操作分为两种动作:IO调用和IO执行。IO调用是......
  • 【java 基础】代码在jvm的内存运行流程分析总结
    堆:存储new出来的对象(包括成员变量、数组、方法的地址)栈:正在调用的方法中的局部变量(包括方法的参数)方法区/元空间:.class字节码文件(包括所有方法)publicclassStudentTe......
  • 继承Thread开启多线程下载图片(不推荐,java的单一继承性)
    packagecom.Java;importorg.apache.commons.io.FileUtils;importjava.io.File;importjava.io.IOException;importjava.net.URL;//练习多线程Thread,实现多线程下载图片......
  • vscode中html和vue没有自动补全,需要怎么配置
    先安装HTML Snippets插件 点击 文件-首选项-设置,然后根据以下操作  然后在setting.json中加入以下代码  "files.associations":{"*.vue":"htm......
  • KingbaseES数据库自动故障转移失败(Automatic Database Failover Failed)
    KingbaseESV8R6版本数据库自动故障转移失败(AutomaticDatabaseFailoverFailed)适用于:KingbaseESV8R6版本。repmgr配置信息:首先检查repmgr.conf配置文件,确任数据......
  • 2月21日javaweb学习之MyBatis
    MyBatis是一款优秀的持久层框架,所谓持久层就是负责将数据保存到数据库的那一层代码。(1)MyBatis快速入门,查询user表中所有的数据1.创建user表,添加数据2.创建模块,导入坐标......
  • Java+Jquer实现趋势图
    这一篇主要介绍的是电商网站的统计功能,后台使用的是Java语言,springMvc框架结合前端Jquer,前端趋势展示组件使用的是百度开源框架Echarts,这个应该大家或多或少的都有了解......