当前位置: 首页 > news >正文

Java 包装类:自动拆箱 / 装箱与 128 陷阱

一、为什么需要包装类?

Java 作为面向对象编程语言,万物皆对象的设计理念深入人心。但 8 种基本数据类型(byteshortintlongfloatdoublecharboolean)却不具备对象特性,这在很多场景下会带来不便:

  • 集合框架(如ArrayListHashMap)只能存储对象,不能直接存储基本数据类型
  • 面向对象编程中,需要将数据以对象形式传递和处理
  • 一些类库方法只接收对象作为参数

为了解决这个矛盾,Java 为每种基本数据类型都提供了对应的包装类(Wrapper Class):

基本数据类型包装类父类
byteByteNumber
shortShortNumber
intIntegerNumber
longLongNumber
floatFloatNumber
doubleDoubleNumber
charCharacterObject
booleanBooleanObject

包装类的主要作用是将基本数据类型封装为对象,使其具备对象的特性,同时提供了大量处理数据的实用方法。

// 包装类的基本使用
public class WrapperDemo {public static void main(String[] args) {// 将基本类型封装为对象Integer num = new Integer(100);// 获取包装类中的基本类型值int value = num.intValue();// 包装类提供的实用方法System.out.println(Integer.MAX_VALUE); // 2147483647System.out.println(Integer.MIN_VALUE); // -2147483648System.out.println(Integer.parseInt("123")); // 字符串转整数System.out.println(Integer.toBinaryString(10)); // 1010}
}

二、自动装箱与拆箱机制

Java 5 引入了自动装箱(Autoboxing)和自动拆箱(Unboxing)机制,极大简化了基本类型与包装类之间的转换操作。

2.1 自动装箱(Autoboxing)

自动装箱指的是 Java 编译器自动将基本数据类型转换为对应的包装类对象的过程。

// 自动装箱:int -> Integer
Integer a = 100; // 等价于 Integer a = Integer.valueOf(100);// 集合中的自动装箱
List<Integer> list = new ArrayList<>();
list.add(200); // 自动将int类型的200装箱为Integer对象

2.2 自动拆箱(Unboxing)

自动拆箱则是指 Java 编译器自动将包装类对象转换为对应的基本数据类型的过程。

// 自动拆箱:Integer -> int
Integer b = new Integer(300);
int c = b; // 等价于 int c = b.intValue();// 运算中的自动拆箱
Integer d = 400;
int e = d + 100; // d自动拆箱为int,与100相加

2.3 自动装箱 / 拆箱的实现原理

通过反编译 class 文件可以发现,自动装箱其实是编译器在后台调用了valueOf()方法,而自动拆箱则是调用了xxxValue()方法(如intValue()doubleValue()等)。

// 反编译前
Integer a = 100;
int b = a;// 反编译后
Integer a = Integer.valueOf(100);
int b = a.intValue();

理解这一点对于我们后面分析 "128 陷阱" 至关重要。

2.4 自动装箱 / 拆箱的使用场景

  1. 赋值操作:基本类型与包装类之间直接赋值
  2. 方法调用:实参与形参类型不匹配时(基本类型与包装类)
  3. 运算操作:包装类对象参与算术运算时会自动拆箱
  4. 集合操作:向集合中添加基本类型元素时自动装箱
    // 方法调用中的自动装箱/拆箱
    public class BoxDemo {public static void print(Integer num) {System.out.println(num);}public static int calculate(int a, int b) {return a + b;}public static void main(String[] args) {int x = 10;print(x); // 自动装箱:int -> IntegerInteger y = 20;Integer z = 30;int result = calculate(y, z); // 自动拆箱:Integer -> int}
    }

    三、深入理解 128 陷阱

    在使用包装类时,最容易遇到的问题就是 "128 陷阱",也称为 Integer 缓存机制导致的比较问题。

    3.1 什么是 128 陷阱?

    先看一个经典的示例

    public class IntegerTrap {public static void main(String[] args) {Integer a = 127;Integer b = 127;System.out.println(a == b); // trueInteger c = 128;Integer d = 128;System.out.println(c == d); // falseInteger e = new Integer(127);Integer f = new Integer(127);System.out.println(e == f); // false}
    }

    为什么同样是赋值,127 时==比较返回true,而 128 时返回false?为什么使用new关键字创建的对象即使值相同,==比较也返回false

    3.2 陷阱背后的原因:Integer 缓存机制

    要理解这个现象,我们需要查看Integer.valueOf()方法的源码(基于 JDK 8):

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

    可以看到,valueOf()方法并非总是创建新的Integer对象,而是先检查该值是否在缓存范围内。如果在范围内,则直接返回缓存中的对象;否则,才创建新对象。

    再看IntegerCache的源码:

    private static class IntegerCache {static final int low = -128;static final int high;static final Integer cache[];static {// 高值可以通过属性配置int h = 127;String integerCacheHighPropValue =sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");if (integerCacheHighPropValue != null) {try {int i = parseInt(integerCacheHighPropValue);i = Math.max(i, 127);// 最大不超过Integer.MAX_VALUE - (-low)h = Math.min(i, Integer.MAX_VALUE - (-low) -1);} catch( NumberFormatException nfe) {// 如果配置无效,则使用默认值}}high = h;cache = new Integer[(high - low) + 1];int j = low;for(int k = 0; k < cache.length; k++)cache[k] = new Integer(j++);// 断言缓存范围正确assert IntegerCache.high >= 127;}private IntegerCache() {}
    }

从源码可知:

  1. IntegerCacheInteger类的私有静态内部类
  2. 默认缓存范围是-128127
  3. 缓存中的对象在类加载时就已创建并存储在cache数组中
  4. 缓存的上限high可以通过 JVM 参数-XX:AutoBoxCacheMax=<size>进行调整

这解释了前面的示例:

  • ab都是 127,在缓存范围内,所以引用的是同一个对象,a == b返回true
  • cd都是 128,超出默认缓存范围,所以是两个不同的对象,c == d返回false
  • ef是通过new关键字创建的,即使值在缓存范围内,也会创建新对象,所以e == f返回false

3.3 其他包装类的缓存机制

不仅Integer有缓存机制,其他包装类也有类似的实现,但缓存范围有所不同:

  1. Byte:缓存范围固定为-128127,没有配置选项
  2. Short:缓存范围固定为-128127
  3. Long:缓存范围固定为-128127
  4. Character:缓存范围为0127
  5. Boolean:缓存TRUEFALSE两个对象
    // 其他包装类的缓存示例
    public class WrapperCache {public static void main(String[] args) {// Byte缓存测试Byte b1 = 127;Byte b2 = 127;System.out.println(b1 == b2); // true// Character缓存测试Character c1 = 127;Character c2 = 127;System.out.println(c1 == c2); // trueCharacter c3 = 128;Character c4 = 128;System.out.println(c3 == c4); // false// Boolean缓存测试Boolean bool1 = true;Boolean bool2 = true;System.out.println(bool1 == bool2); // true}
    }

    FloatDouble没有实现缓存机制,因为它们是浮点类型,可能的取值范围太大,缓存不切实际。

    // Float和Double没有缓存
    public class FloatDoubleCache {public static void main(String[] args) {Float f1 = 1.0f;Float f2 = 1.0f;System.out.println(f1 == f2); // falseDouble d1 = 1.0;Double d2 = 1.0;System.out.println(d1 == d2); // false}
    }

    四、包装类比较的正确方式

    了解了缓存机制后,我们应该明确:包装类对象比较应该使用equals()方法,而不是==运算符

    ==运算符比较的是对象的引用(内存地址),而equals()方法比较的是对象的值。

    public class WrapperComparison {public static void main(String[] args) {Integer a = 128;Integer b = 128;// 错误:使用==比较包装类System.out.println(a == b); // false// 正确:使用equals()比较包装类System.out.println(a.equals(b)); // true// 注意:基本类型比较可以安全使用==int c = 128;int d = 128;System.out.println(c == d); // true// 混合比较:包装类会自动拆箱System.out.println(a == c); // true}
    }
    

    当包装类与基本类型进行==比较时,包装类会自动拆箱为基本类型,然后比较值的大小,这时候使用==是安全的。

http://www.dtcms.com/a/478392.html

相关文章:

  • 行业 观察
  • 59网站一起做网店淘宝网站咋做
  • 那个公司做的外贸网站好wordpress可视化函数
  • LabelImg和Labelme:目标检测和图像分割的标注工具
  • 国外素材网站推荐linux主机上传网站
  • 开源php公司网站wordpress语音搜索
  • UE 如何迁移 DerivedDataCache 路径,避免 C 盘因海量模型缓存爆盘
  • 支付宝小程序 SEO 实战:鲜花送达类小程序抢占搜索流量指南
  • 小吉快检BL-08plus:推动动物疫病早发现、早防控的科技引擎
  • 个人网站如何搭建上海工商网官网登录
  • wordpress做小说网站网站建设 sql 模版
  • 从HIDL到AIDL:Android HAL架构的演进与抉择
  • Redis-stream、bitfield类型
  • [Python脚本]快速检测两个文件夹之间的视觉重复图片
  • 【Postgresql】PG版本升级,PG14到PG15,且数据迁移
  • 数据库原理与设计
  • iis7配置thinkphp网站做海报的网站有哪些内容
  • 【开题答辩全过程】以 博客网站为例,包含答辩的问题和答案
  • 解决 MySQL `MAX(IF())` 中 `table.column` 有值但显示 `‘default‘` 的问题
  • 如何加快门户网站建设wordpress 资源下载插件
  • 视频播放器 v12.1.357 | 4K顶级播放器,内置了视频下载器,可下载推特~脸书等国外视频
  • 路径总和---超全详细解
  • 【深入浅出PyTorch】--6.2.PyTorch进阶训练技巧2
  • JS - 运算符
  • Ethical use of recommender systems|推荐系统的道德使用
  • 网站建设 全网推广建网站的域名
  • MySQL——表操作
  • 基于MATLAB的海图快速临近值插值地图构建方法
  • 深度解析“rgss102e.dll丢失”问题:原因、影响与完整解决指南
  • 正规的媒体发稿网有哪些