Java自动拆箱装箱/实例化顺序/缓存使用/原理/实例
在 Java 编程体系中,基本数据类型与包装类紧密关联,它们各自有着独特的特性和应用场景。理解两者之间的关系,特别是涉及到拆箱与装箱、实例化顺序、区域问题、缓存问题以及效率问题。
一、为什么基本类型需要包装类
泛型与集合的需求
Java 的泛型机制要求类型参数必须是引用类型。例如,当我们创建一个ArrayList来存储整数时,如果使用基本数据类型int,编译器会报错。因为集合框架中的ArrayList、HashMap等类都需要存储对象,这就促使我们使用Integer这样的包装类。
如下示例:
// 错误示范,泛型不支持基本数据类型
// ArrayList<int> intList = new ArrayList<>(); // 正确方式,使用包装类Integer
ArrayList<Integer> integerList = new ArrayList<>();
面向对象编程的完整性
基本数据类型虽然简单高效,但在纯粹的面向对象编程思维中,它们显得格格不入。包装类将基本数据类型封装成对象,使其能够融入面向对象的体系中。这意味着可以像操作其他对象一样,对包装类对象进行传递、多态处理等操作,符合 Java 的面向对象编程范式。
丰富的方法支持
包装类为我们提供了大量实用的方法。以Integer类为例,toHexString(int i)方法可以将一个整数转换为十六进制字符串表示,parseInt(String s)方法则能将字符串解析为整数。这些方法极大地方便了我们对数据的处理和转换,而基本数据类型本身并不具备这样的功能。例如:
int num = 255;
String hexString = Integer.toHexString(num); // 输出 "ff"String numberStr = "123";
int parsedInt = Integer.parseInt(numberStr); // parsedInt 为 123
二、Long 或 Integer 如何比较大小
错误的比较方法
使用==
由于Long和Integer是包装类,属于对象类型。使用==比较时,比较的是对象的引用地址,而非对象所包含的值。例如:
Integer a = new Integer(10);
Integer b = new Integer(10);
System.out.println(a == b); // 输出 false,因为a和b是不同的对象引用
使用equals方法
equals方法在默认情况下也是比较对象的引用。虽然Integer和Long等包装类重写了equals方法,使其比较对象的值,但equals方法要求比较的两个对象类型必须相同。例如:
Integer intObj = 10;
Long longObj = 10L;
// 以下代码会编译错误,因为equals方法要求参数类型与调用对象类型一致
// System.out.println(intObj.equals(longObj));
正确的比较方法
可以先使用longValue()(对于Long类型)或intValue()(对于Integer类型)方法将包装类对象转换为基本数据类型,然后再使用==进行比较。示例如下:
Integer intA = 15;
Integer intB = 20;
boolean result = intA.intValue() == intB.intValue(); // 比较值Long longA = 100L;
Long longB = 100L;
boolean longResult = longA.longValue() == longB.longValue(); // 比较值
三、拆箱与装箱原理
装箱
装箱是将基本数据类型转换为包装类对象的过程。例如,当我们编写Integer num = 5;时,Java 编译器会自动将其转换为Integer num = Integer.valueOf(5);。Integer.valueOf方法内部有一定的逻辑,对于在特定范围内的值,会从缓存中获取对象,而不是创建新对象(后面会详细介绍缓存问题)。
拆箱
拆箱则是将包装类对象转换为基本数据类型的过程。如int value = num;,编译器会将其转换为int value = num.intValue();,即调用包装类对象的intValue方法来获取基本数据类型的值。
实例:查看反汇编文件
package org.example;public class BoxingUnboxingDemo {public static void main(String[] args) {// 自动装箱,底层执行Integer a = Integer.valueOf(10);Integer a = 10;// 自动拆箱,底层执行int b = a.intValue();int b = a;}
}
生成并查看反汇编文件,依次执行如下命令:
javac -d. org/example/BoxingUnboxingDemo.java
javap -c org.example.BoxingUnboxingDemo
输出结果如下:
Compiled from "BoxingUnboxingDemo.java"
public class org.example.BoxingUnboxingDemo {public org.example.BoxingUnboxingDemo();Code:0: aload_01: invokespecial #1 // Method java/lang/Object."<init>":()V4: returnpublic static void main(java.lang.String[]);Code:0: bipush 102: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;5: astore_16: aload_17: invokevirtual #3 // Method java/lang/Integer.intValue:()I10: istore_211: return
}
从反汇编代码可以清晰地看到,Integer a = 10;这行代码被编译成了Integer a = Integer.valueOf(10);,而int b = a;被编译成了int b = a.intValue();,这就是自动装箱和拆箱在编译器层面的实现。
四、实例化顺序
package org.example;public class InstantiationOrderDemo {public static void main(String[] args) {int intValue1 = 12;Integer integerValue1 = new Integer(12);Integer integerValue2 = new Integer(34);int intValue2 = 34;System.out.println("intValue1 == integerValue1 : " + (intValue1 == integerValue1));System.out.println("intValue2 == integerValue2 : " + (intValue2 == integerValue2));}
}
在这个示例中,当基本数据类型与包装类对象使用==进行比较时,会发生自动拆箱。所以intValue1 == integerValue1和intValue2 == integerValue2实际上都是基本数据类型之间的比较,结果都为true。这表明在这种比较操作中,实例化顺序并不会影响比较结果,因为自动拆箱机制会将包装类对象转换为基本数据类型后再进行比较。
五、区域问题
基本数据类型与栈
基本数据类型(如int、double、char等)存储在栈内存中。栈内存的特点是数据存储和访问速度快,并且数据的生命周期与方法调用紧密相关。例如:
int age = 25;
这里的age变量存储在栈内存中,直接保存整数值25。当方法执行结束,age变量所占用的栈空间会被自动释放。
包装类对象与堆
包装类对象(如Integer、Double、Character等)是在堆内存中实例化的。堆内存用于存储对象,对象的生命周期由垃圾回收机制管理。当我们创建一个包装类对象时,例如:
Integer number = 10;
number变量存储在栈中,它指向堆中创建的Integer对象,该对象内部封装了int值10。自动装箱时,基本数据类型从栈转移到堆中包装类对象里;自动拆箱时,则是从堆中的包装类对象获取值并存储到栈中的基本数据类型变量中。
实例
package org.example;public class MemoryRegionDemo {public static void main(String[] args) {Integer integer1 = new Integer(12);Integer integer2 = 12;System.out.println("integer1 == integer2 : " + (integer1 == integer2));}
}
在这个例子中,integer1是通过new关键字在堆中创建的新对象,而integer2是通过自动装箱创建的对象。由于integer2的值在缓存范围内(后面会介绍缓存),它引用的是缓存中的对象。所以integer1 == integer2比较的是两个不同的对象引用,结果为false。这体现了不同的实例化方式(直接new和自动装箱)在内存区域上的差异以及对对象比较的影响。
六、缓存问题
缓存机制原理
Java 为了提高自动装箱的性能,对于部分包装类(如Integer、Byte、Short、Long、Character)实现了缓存机制。以Integer为例,其缓存范围是 -128 到 127。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);
}
当进行自动装箱且值在 -128 到 127 之间时,会直接从缓存中获取对象,而不是创建新对象。
缓存的影响
示例
package org.example;public class CacheDemo {public static void main(String[] args) {Integer a = 127;Integer b = 127;Integer c = 128;Integer d = 128;System.out.println(a == b);System.out.println(c == d);}
}
执行结果为:
true
false
因为a和b的值都在缓存范围内,它们引用的是同一个缓存对象,所以a == b返回true。而c和d的值超出了缓存范围,它们是通过new Integer()创建的不同对象,所以c == d返回false。
其他包装类的缓存情况
- Byte:由于byte的取值范围是 [-128, 127],所以相同值的Byte比较永远返回true,因为所有可能的值都在缓存范围内。
- Short、Integer、Long:相同值在 [-128, 127] 范围内则返回true,不在此范围则返回false。
- Character:只要char值小于等于 127,相同值比较就返回true,因为char的最小值为 0,本身就大于等于 -128。
- Float、Double:永远返回false,因为浮点数的取值范围广泛且小数数量无限,无法进行缓存,每次装箱都会创建新对象。
- Boolean:只有true和false两个对象,只要boolean的值相同,对应的Boolean对象就相等。
七、效率问题
装箱和拆箱的性能开销
自动装箱和拆箱虽然带来了编程的便利性,但也存在性能开销。装箱过程涉及对象的创建,包括在堆中分配内存、初始化对象等操作;拆箱过程需要调用对象的实例方法。这些操作相较于直接操作基本数据类型,效率较低。例如:
package org.example;public class PerformanceDemo {public static void main(String[] args) {long startTime = System.currentTimeMillis();Long sum = 0L;for (int i = 0; i < Integer.MAX_VALUE; i++) {sum += i;}long endTime = System.currentTimeMillis();System.out.println("使用Long类型耗时: " + (endTime - startTime) + " 毫秒");startTime = System.currentTimeMillis();long basicSum = 0L;for (int i = 0; i < Integer.MAX_VALUE; i++) {basicSum += i;}endTime = System.currentTimeMillis();System.out.println("使用long类型耗时: " + (endTime - startTime) + " 毫秒");}
}
在上述代码中,使用Long类型进行累加操作时,每次循环都会发生自动装箱和拆箱,导致性能较低。而使用基本数据类型long进行累加,直接操作基本数据,性能明显提升。实际运行结果显示,使用Long类型耗时远远大于使用long类型。
优化策略
在性能敏感的代码段中,应尽量减少不必要的装箱和拆箱操作。例如,在集合操作中,如果可以使用基本数据类型数组代替包装类对象的集合,就能够避免装箱和拆箱的开销。另外,对于需要频繁使用的小整数,可以利用缓存机制,确保值在缓存范围内,以提高效率。比如在循环中需要频繁使用固定范围内的整数时,提前将这些整数装箱并缓存起来,避免重复装箱操作。
相关文章:

Java自动拆箱装箱/实例化顺序/缓存使用/原理/实例
在 Java 编程体系中,基本数据类型与包装类紧密关联,它们各自有着独特的特性和应用场景。理解两者之间的关系,特别是涉及到拆箱与装箱、实例化顺序、区域问题、缓存问题以及效率问题。 一、为什么基本类型需要包装类 泛型与集合的需求 Java…...
软件工程---基于构件的软件工程
基于构件的软件工程(CBSE)是一种软件开发方法,通过重用现有的软件构件来构建系统,从而提高开发效率和软件质量。这种方法强调软件系统的模块化设计和构建复用,使得软件开发过程更加高效和灵活。 企业软件开发…...
AMD RDNA3 GPU架构解析
本文会通过把AMD的RDNA3架构为例比喻为**“施工公司”**工作模式,深入理解GPU如何高效处理顶点着色、像素计算等任务。 一、施工公司的组织架构 1. 施工公司(WGP)与施工队(CU) WGP(Work Group Processor&…...
docker关闭mysql端口映射的使用
需求 项目中的数据库为mysql,如果将端口映射到宿主机上,容易被工具扫描出,且随着国产化的进程推进,mysql将不被允许。为了提高安全性与满足项目需求,这里采用隐藏mysql端口方式,不映射宿主机端口ÿ…...

关于对机器中的人工智能进行基准测试
大家读完觉得有帮助记得及时关注和点赞!!! 抽象 最近的基准研究声称,AI 在各种认知任务上的表现已经接近甚至超过人类的“水平”。然而,本立场文件认为,当前的 AI 评估范式不足以评估类似人类的认知能力。我…...
CSS - 妙用Sass
官方文档:https://www.sass.hk/docs/ 1.例1: each $theme in $themeList {$themeKey: map-get($theme, key);media screen and (weex-theme: $themeKey) {.btnText {max-width: 150px;include font(map-get($theme, medFont),map-get($theme, subFontS…...
MS模块创新
1. 动态分支权重融合 创新思路:引入通道注意力机制,自动学习高频/低频分支的融合权重 class DynamicMS(nn.Module):def __init__(self, in_channels1):super().__init__()# 原高频/低频分支保持不变self.high_freq ... # 与原MS模块相同self.low_freq…...

私有化部署DeepSeek并SpringBoot集成使用(附UI界面使用教程-支持语音、图片)
私有化部署DeepSeek并SpringBoot集成使用(附UI界面使用教程-支持语音、图片) windows部署ollama Ollama 是一个开源框架,专为在本地机器上便捷部署和运行大型语言模型(LLM)而设计 下载ollama 下载地址(…...
MFC中CMutex类和CSingleLock类,配合使用疑惑
在使用CMutex过程中,看到别人使用了CSingleLock类,想着明明CMutex已经可以实现线程同步了,为什么还有使用CSingleLock类呢? 在MFC中,虽然CMutex类本身可以实现线程同步,但通常会与CSingleLock类一起使用&am…...
残差收缩模块
1. 多尺度阈值生成 创新思路:融合不同尺度的统计信息(如平均池化最大池化)生成更鲁棒的阈值。 class MultiScaleShrinkage(nn.Module):def __init__(self, channel, reduction4):super().__init__()# 多尺度池化分支self.avg_pool nn.Adap…...
HOW - 在Windows浏览器中模拟MacOS的滚动条
目录 一、原生 CSS 代码实现模拟 macOS 滚动条额外优化应用到某个特定容器 二、Antd table中的滚动条场景三、使用第三方工具/扩展 如果你想让 Windows 里的滚动条 模拟 macOS 的效果(细窄、圆角、隐藏默认轨道)。 可以使用以下几种方案: 一…...

Unity 打包后EXE运行出现Field to Load il2cpp的一种情况
Unity版本2021.3.13f1c1 #if DEVELOPMENT_BUILDA1 A1 10600;#else#endif 使用 #if DEVELOPMENT_BUILD然后在下面面板使用Development Build。打包后会运行游戏EXE出现Field to Load il2cpp。 解决办法是换成IF ELSE,自己代码设置个开关、 文心一言: …...
Windows 环境下 Nginx、PHP 与 ThinkPHP 开发环境搭建
Windows 环境下 Nginx、PHP 与 ThinkPHP 开发环境搭建 目录 安装 Nginx 和 PHP配置 Nginx配置 PHP启动服务ThinkPHP 配置常见问题排查 1. 安装 Nginx 和 PHP 安装 Nginx 访问 Nginx 官网 下载 Windows 版本解压到指定目录,如 C:\nginx 安装 PHP 访问 PHP 官网…...
Redis100道高频面试题
一、Redis基础 Redis是什么?主要应用场景有哪些? Redis 是一个开源的、基于内存的数据结构存储系统,支持多种数据结构(如字符串、哈希、列表、集合等),可以用作数据库、缓存和消息中间件。 主要应用场景&…...
登录服务器后如何找到对应的drupal所在的文件夹
在服务器上找不到 Drupal 安装目录的原因可能有以下几种: 多站点配置: Drupal 支持多站点设置,即在同一安装中托管多个网站。在这种配置下,每个站点都有自己的设置和文件夹,通常位于 sites 目录下。例如,站…...

win32汇编环境,窗口程序中使控件子类化的示例一
;运行效果 ;win32汇编环境,窗口程序中使编辑框控件子类化的示例一 ;窗口子类化,就是把某种控件,自已再打造一遍,加入自已的功能。比如弄个特殊形状的按钮,或只能输入特殊字符的编辑框 ;当然,一般来说,这都是…...

专业工具,杜绝一切垃圾残留!
在安装大多数软件时均会在系统注册表中创建相应的条目。如果卸载后仍然存在注册表残留,可能会导致再次安装时出现失败,同时也会对系统性能和存储空间产生负面影响。常见的卸载残留包括注册表项、程序文件夹、用户数据文件夹、临时文件以及相关插件等。 …...
java 实现简易基于Dledger 的选举
java 实现简易基于Dledger 的选举 1. 定义 Dledger 节点类,包含节点的状态、日志存储、选举和日志复制逻辑 import java.util.ArrayList; import java.util.List; import java.util.Random; import java.util.concurrent.Executors; import java.util.concurrent.S…...

大数据“调味“ ,智慧“添香“,看永洪科技助力绝味食品数字化新征程
近年来,随着国家数字化政策不断出台、新兴技术不断进步、企业内生需求持续释放,数字化转型逐步成为企业实现高质量发展的必由之路,成为企业实现可持续发展乃至弯道超车的重要途径。 在全国数字化浪潮驱动下,以人工智能、互联网、…...

【嵌入式】MQTT
MQTT 文章目录 MQTT安装简介MQTT客户端代码 安装 安装Paho MQTT C库: sudo apt-get install libpaho-mqtt3-dev头文件包含: #include "MQTTClient.h"编译选项: gcc -o $ $^ -lpaho-mqtt3c简介 MQTT协议全称是(Message Queuing…...

深入剖析AI大模型:大模型时代的 Prompt 工程全解析
今天聊的内容,我认为是AI开发里面非常重要的内容。它在AI开发里无处不在,当你对 AI 助手说 "用李白的风格写一首关于人工智能的诗",或者让翻译模型 "将这段合同翻译成商务日语" 时,输入的这句话就是 Prompt。…...
C++:std::is_convertible
C++标志库中提供is_convertible,可以测试一种类型是否可以转换为另一只类型: template <class From, class To> struct is_convertible; 使用举例: #include <iostream> #include <string>using namespace std;struct A { }; struct B : A { };int main…...
FFmpeg 低延迟同屏方案
引言 在实时互动需求激增的当下,无论是在线教育中的师生同屏演示、远程办公的屏幕共享协作,还是游戏直播的画面实时传输,低延迟同屏已成为保障用户体验的核心指标。FFmpeg 作为一款功能强大的多媒体框架,凭借其灵活的编解码、数据…...

visual studio 2022更改主题为深色
visual studio 2022更改主题为深色 点击visual studio 上方的 工具-> 选项 在选项窗口中,选择 环境 -> 常规 ,将其中的颜色主题改成深色 点击确定,更改完成...

DAY 47
三、通道注意力 3.1 通道注意力的定义 # 新增:通道注意力模块(SE模块) class ChannelAttention(nn.Module):"""通道注意力模块(Squeeze-and-Excitation)"""def __init__(self, in_channels, reduction_rat…...

CentOS下的分布式内存计算Spark环境部署
一、Spark 核心架构与应用场景 1.1 分布式计算引擎的核心优势 Spark 是基于内存的分布式计算框架,相比 MapReduce 具有以下核心优势: 内存计算:数据可常驻内存,迭代计算性能提升 10-100 倍(文档段落:3-79…...

376. Wiggle Subsequence
376. Wiggle Subsequence 代码 class Solution { public:int wiggleMaxLength(vector<int>& nums) {int n nums.size();int res 1;int prediff 0;int curdiff 0;for(int i 0;i < n-1;i){curdiff nums[i1] - nums[i];if( (prediff > 0 && curdif…...
【Java学习笔记】BigInteger 和 BigDecimal 类
BigInteger 和 BigDecimal 类 二者共有的常见方法 方法功能add加subtract减multiply乘divide除 注意点:传参类型必须是类对象 一、BigInteger 1. 作用:适合保存比较大的整型数 2. 使用说明 创建BigInteger对象 传入字符串 3. 代码示例 import j…...
scikit-learn机器学习
# 同时添加如下代码, 这样每次环境(kernel)启动的时候只要运行下方代码即可: # Also add the following code, # so that every time the environment (kernel) starts, # just run the following code: import sys sys.path.append(/home/aistudio/external-libraries)机…...

AD学习(3)
1 PCB封装元素组成及简单的PCB封装创建 封装的组成部分: (1)PCB焊盘:表层的铜 ,top层的铜 (2)管脚序号:用来关联原理图中的管脚的序号,原理图的序号需要和PCB封装一一…...