深入理解Java包装类:自动装箱拆箱与缓存池机制

news2025/4/21 21:29:45

深入理解Java包装类:自动装箱拆箱与缓存池机制

对象包装器

Java中的数据类型可以分为两类:基本类型引用类型。作为一门面向对象编程语言, 一切皆对象是Java语言的设计理念之一。但基本类型不是对象,无法直接参与面向对象操作,为了解决这个问题,Java让每个基本类型都有一个与之对应的包装器类型。

Java中有8种不可变的基本类型,分别为:

  • 整型byteshortintlong
  • 浮点类型floatdouble
  • 字符类型char
  • 布尔类型boolean
类型大小默认值示例
byte1字节0byte b = 10
short2字节0short s = 200
int4字节0int i = 1000
long8字节0Llong l = 5000L
float4字节0.0ffloat f = 3.14f
double8字节0.0ddouble d = 2.718
char2字节‘\u0000’char c = 'A'
boolean未明确定义falseboolean flag = true

这八种基本类型都有对应的包装类分别为:ByteShortIntegerLongFloatDoubleCharacterBoolean (前6个派生于公共的超类Number)。包装器类是不可变的,即一旦构造了包装器,就不允许更改包装在其中的值。同时,包装器类还是final,因此不能派生它们的子类。

Java不是“一切皆对象”吗,为什么还要保留基本数据类型?

包装类是引用类型,对象的引用存储在栈中,对象本身存储在堆中;而对于基本数据类型,变量对应的内存块直接存储数据本身(栈中)。因此,基本数据类型读写效率更高效。在64位JVM上,在开启引用压缩的情况下,一个Integer对象占用16个字节的内存空间,而一个int类型数据只占用4字节的内存空间,前者对空间的占用是后者的4倍。也就是说,不管是读写效率还是存储效率,基本类型都更高效。尽管Java强调面向对象,但为了性能做了妥协。

自动装箱与拆箱

装箱拆箱是实现基本数据类型与包装类之间相互转换的特性。Java 5引入自动装箱/拆箱功能,进一步简化了包装类的使用。

  • 装箱:将基本数据类型转化为对应的包装类对象。
  • 拆箱:将包装类对象转化为对应的基本数据类型值。

示例

Integer a = 100; // 自动装箱 -> Integer.valueOf(100)

int b = a; // 自动拆箱 -> a.intValue()

// 自动装箱和拆箱也适用于算术表达式
Integer n = 3;
n++; // 编译器将自动插入一条对象拆箱的指令,然后进行自增运算,最后再将结果装箱

装箱其实就是调用了包装类的valueOf()方法,拆箱其实就是调用了 xxxValue()方法。

API java.lang.Integer

  • int intValue()

    将这个Integer对象的值作为一个int返回(覆盖Number类中的intValue方法)。

  • static Integer valueOf(String s)

    返回一个新的Integer对象,用字符串s表示的整数初始化。指定字符串必须表示一个十进制整数。

关于自动装箱还有几点需要注意:

  • 高频装箱拆箱(如循环)会产生大量临时对象,消耗内存和GC资源:

    // 错误示例:每次循环触发装箱
    Long sum = 0L;
    for (long i = 0; i < 1e6; i++) {
        sum += i; // sum = Long.valueOf(sum.longValue() + i)
    }
    
    // 正确优化:使用基本类型
    long sum = 0L;
    for (long i = 0; i < 1e6; i++) {
        sum += i;
    }
    
  • 由于包装器类引用可以为null,所以自动装箱有可能会抛出一个NullPointerException异常:

    Integer n = null;
    System.out.println(2 * n) // throws NullPointerException
    
  • 如果在一个表达式中混合使用IntegerDouble类型,Integer值就会拆箱,提升为double,再装箱为Double

    Integer n = 1;
    Double x = 2.0;
    System.out.println(true ? n : x); // 1.0
    
  • 装箱和拆箱是编译器要做的工作,而不是虚拟机。编译器在生成类的字节码时会插入必要的方法调用。虚拟机只是执行这些字节码。

缓存池机制

缓存池是 Java 为优化包装类对象创建和内存消耗而设计的核心机制,通过预创建和复用常用数值的包装类对象,减少重复对象创建的开销。Java 基本数据类型的包装类型的大部分都用到了缓存机制来提升性能。

Byte,Short,Integer,Long 这 4 种包装类默认创建了数值 [-128,127] 的相应类型的缓存数据,Character 创建了数值在 [0,127] 范围的缓存数据,Boolean 直接返回 TRUE or FALSE

示例

Integer a = 100; // Integer.valueOf(100)
Integer b = 100; // Integer.valueOf(100) 
Integer c = 200; // Integer.valueOf(200)
Integer d = 200; // Integer.valueOf(200)

System.out.println(a == b); // true
System.out.println(c == d); // false
System.out.println(c.equals(d)); //true 

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); // 超出缓存范围时创建新对象
}

缓存池机制Java对-128 ~ 127范围内的Integer对象预先生成并缓存ab指向同一个缓存对象,a == b比较对象地址,返回true200超出默认缓存范围(-128 ~ 127),Integer.valueOf(200)每次会创建新对象,cd指向不同对象,c == d比较对象地址,返回false

对于 Integer,可以通过 JVM 参数 -XX:AutoBoxCacheMax=<size> 修改缓存上限,但不能修改下限 -128。实际使用时,并不建议设置过大的值,避免浪费内存,甚至是 OOM(全称Out Of Memory, 即内存溢出)

  • 内存溢出:申请的内存超出了JVM能提供的内存大小,此时称之为溢出。
  • 内存泄漏:申请使用完的内存没有释放,导致虚拟机不能再次使用该内存,此时这段内存就泄露了,因为申请者不用了,而又不能被虚拟机分配给别人用。

对于Byte,Short,Long ,Character 没有类似 -XX:AutoBoxCacheMax 参数可以修改,因此缓存范围是固定的,无法通过 JVM 参数调整。Boolean 则直接返回预定义的 TRUEFALSE 实例,没有缓存范围的概念。

Character的缓存逻辑

public static Character valueOf(char c) {
    if (c <= 127) { // must cache
      return CharacterCache.cache[(int)c];
    }
    return new Character(c);
}

Boolean的缓存逻辑

public static Boolean valueOf(boolean b) {
    return (b ? TRUE : FALSE);
}

两种浮点数类型的包装类 Float,Double 并没有实现缓存机制

Float a = 3f;
Float b = 3f;
System.out.println(a == b);// 输出 false

Double c = 1.2;
Double d = 1.2;
System.out.println(c == d);// 输出 false

下面这段代码的输出结果是什么?

Integer a = 40;
Integer b = new Integer(40);
System.out.println(a == b);

Integer a = 40自动装箱等价于Integer a = Integer.valueOf(40)40在默认缓存范围内,所以a直接使用的是缓存中的对象,而Integer b = new Integer(40)会直接创建新的对象。因此,答案是false

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2339677.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

如何使用Node-RED采集西门子PLC数据通过MQTT协议实现数据交互并WEB组态显示

需求概述 本章节主要实现一个流程&#xff1a;使用纵横智控的EG网关通过Node-red&#xff08;可视化编程&#xff09;采集PLC数据&#xff0c;并通过MQTT协议和VISION&#xff08;WEB组态&#xff09;实现数据交互。 以采集西门子PLC为例&#xff0c;要采集的PLC的IP、端口和点…

【cocos creator 3.x】速通3d模型导入, 模型创建,阴影,材质使用,模型贴图绑定

1、右键创建平面&#xff0c;立方体 2、点击场景根节点&#xff0c;shadows勾选enabled3、点击灯光&#xff0c;shadow enabled勾选 4、点击模型&#xff0c;勾选接收阴影&#xff0c;投射阴影&#xff08;按照需要勾选&#xff09; 5、材质创建 6、选中节点&#xff0c;找…

驱动开发硬核特训 · Day 15:电源管理核心知识与实战解析

在嵌入式系统中&#xff0c;电源管理&#xff08;Power Management&#xff09;并不是“可选项”&#xff0c;而是实际部署中影响系统稳定性、功耗、安全性的重要一环。今天我们将以 Linux 电源管理框架 为基础&#xff0c;从理论结构、内核架构&#xff0c;再到典型驱动实战&a…

【零基础】基于DeepSeek-R1与Qwen2.5Max的行业洞察自动化平台

自动生成行业报告,通过调用两个不同的大模型(DeepSeek 和 Qwen),完成从行业趋势分析到结构化报告生成的全过程。 完整代码:https://mp.weixin.qq.com/s/6pHi_aIDBcJKw1U61n1uUg 🧠 1. 整体目的与功能 该脚本实现了一个名为 ReportGenerator 的类,用于: 调用 DeepSe…

C 语言联合与枚举:自定义类型的核心解析

目录 1.联合体 1.1联合体的声明与创建 1.2联合体在内存中的存储 1.3相同成员的结构体与内存比较 1.4联合体内存空间大小的计算 1.5联合体的应用 2.枚举类型 2.1枚举变量的声明 2.2枚举变量的优点 2.3枚举的使用 上篇博客中&#xff0c;我们通过学习了解了C语言中一种自…

基于Canal+Spring Boot+Kafka的MySQL数据变更实时监听实战指南

前期知识背景 binlog 什么是binlog 它记录了所有的DDL和DML(除 了数据查询语句)语句&#xff0c;以事件形式记录&#xff0c;还包含语句所执行的消耗的时间&#xff0c;MySQL 的二进制日志是事务安全型的。一般来说开启二进制日志大概会有1%的性能损耗。 binlog分类 MySQL Bi…

MySQL运维三部曲初级篇:从零开始打造稳定高效的数据库环境

文章目录 一、服务器选型——给数据库一个舒适的家二、系统调优——打造高性能跑道三、MySQL配置——让数据库火力全开四、监控体系——数据库的体检中心五、备份恢复——数据安全的最后防线六、主从复制——数据同步的艺术七、安全加固——守护数据长城 引言&#xff1a;从小白…

【MySQL】MySQL的基础语法及其语句的介绍

1、基础语法 mysql -h【主机名】 -u【用户名】 -p //登录MySQL exit或quit; //退出MySQL show database; //查看MySQL下的所有数据库 use 【数据库名】; //进入数据库 show tables; //查看数据库下的所有表名 *MySQL的启动和关闭 &am…

【计算机视觉】三维视觉项目 - Colmap二维图像重建三维场景

COLMAP 3D重建 项目概述项目功能项目运行方式1. 环境准备2. 编译 COLMAP3. 数据准备4. 运行 COLMAP 常见问题及解决方法1. **编译问题**2. **运行问题**3. **数据问题** 项目实战建议项目参考文献 项目概述 COLMAP 是一个开源的三维重建软件&#xff0c;专注于 Structure-from…

ALSA架构学习2(驱动MAX98357A)

1 前言和环境 之前其实写过两篇&#xff0c;一篇是讲ALSA&#xff0c;一篇是I2S。 ALSA架构学习1&#xff08;框架&#xff09;_alsa框架学习-CSDN博客 总线学习5--I2S_max98357接喇叭教程-CSDN博客 在ALSA那篇的结尾&#xff0c;也提了几个小练习。比如&#xff1a; ### 4…

数据结构*集合框架顺序表-ArrayList

集合框架 常见的集合框架 什么是顺序表 顺序表是一种线性表数据结构&#xff0c;它借助一组连续的存储单元来依次存储线性表中的数据元素。一般情况下采用数组存储。 在数组上完成数据的增删查改。 自定义简易版的顺序表 代码展示&#xff1a; public interface IArray…

VMware Workstation 保姆级 Linux(CentOS) 创建教程(附 iso)

文章目录 一、下载二、创建 一、下载 CentOS-7.9-x86_64-DVD-2009.iso 二、创建 VMware Workstation 保姆级安装教程(附安装包) VMware Workstation 保姆级安装教程(附安装包) VMware Workstation 保姆级安装教程(附安装包)

软考-信息系统项目管理师-2 信息技术发展

总结思维导图 云计算(掌握) (3)多租户和访问控制管理访问控制管理是云计算应用的核心问题之一云计算访问控制的研究主要集中在云计算访问控制模型、基于ABE密码体制的云计算访问控制、云中多租户及虚拟化访问控制研究云中多租户及虚拟化访问控制是云计算的典型特征。 大数据(…

Spring Boot JPA 开发之Not an entity血案

项目状况介绍 项目环境 JDK 21Spring Boot 3.4.3Hibernate: 6.6.13.Final项目描述 因为是微服务架构,项目层级如下 project-parent project-com project-A … project-X 其中: project-parent定义依赖库的版本project-com 定义了一些公用的方法和配置,包括持久层的配置。…

HTMLCSS实现轮播图效果

这段代码实现了一个具有自动轮播、手动切换功能的图片轮播图&#xff0c;并且配有指示器&#xff08;小圆点&#xff09;来显示当前图片位置。轮播图可通过左右箭头按钮进行手动切换&#xff0c;也能自动定时切换&#xff0c;当鼠标悬停在轮播图上时&#xff0c;自动轮播会暂停…

嵌入式学习——opencv图像库编程

环境配置 OpenCV&#xff08;Open Source Computer Vision Library&#xff09;是一个开源的计算机视觉和图像处理库&#xff0c;广泛用于各种计算机视觉任务&#xff0c;如图像处理、视频分析、人脸识别、物体检测、机器学习等。它提供了丰富的函数和工具&#xff0c;用于处理…

【每日八股】复习 MySQL Day1:事务

文章目录 复习 MySQL Day1&#xff1a;事务MySQL 事务的四大特性&#xff1f;并发事务会出现什么问题&#xff1f;MySQL 事务的隔离级别&#xff1f;不同事务隔离级别下会发生什么问题&#xff1f;MVCC 的实现原理&#xff1f;核心数据结构版本链构建示例可见性判断算法MVCC 可…

外接键盘与笔记本命令键键位不同解决方案(MacOS)

文章目录 修改键位第一步&#xff1a;打开设置第二步&#xff1a;进入键盘快捷键第三步&#xff1a;修改修饰键设置第四步&#xff1a;调整键位第五步&#xff1a;保存设置tips ikbc c87键盘win键盘没反应的解决亲测的方法这是百度的答案标题常规组合键尝试‌&#xff1a;型号差…

kotlin知识体系(五) :Android 协程全解析,从作用域到异常处理的全面指南

1. 什么是协程 协程(Coroutine)是轻量级的线程&#xff0c;支持挂起和恢复&#xff0c;从而避免阻塞线程。 2. 协程的优势 协程通过结构化并发和简洁的语法&#xff0c;显著提升了异步编程的效率与代码质量。 2.1 资源占用低&#xff08;一个线程可运行多个协程&#xff09;…

vscode stm32 variable uint32_t is not a type name 问题修复

问题 在使用vscodekeil开发stm32程序时&#xff0c;发现有时候vscode的自动补全功能失效&#xff0c;且problem窗口一直在报错。variable “uint32_t” is not a type name uint32_t 定义位置 uint32_t 实际是在D:/Keil_v5/ARM/ARMCC/include/stdint.h中定义的。将D:/Keil_v5…