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

深入理解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 cachereturn 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);// 输出 falseDouble 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.mzph.cn/bicheng/78120.shtml

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

相关文章

uniapp自定义拖拽排列

uniapp自定义拖拽排列并改变下标 <!-- 页面模板 --> <template><view class"container"><view v-for"(item, index) in list" :key"item.id" class"drag-item" :style"{transform: translate(${activeInde…

基于SpringBoot的课程管理系统

前言 今天给大家分享一个基于SpringBoot的课程管理系统。 1 系统介绍 课程管理系统是一种专门为学校设计的软件系统&#xff0c;旨在帮助学校高效地管理和组织各类课程信息。 该系统通常包括学生、教师和管理员三大角色。 他们可以通过系统进行选课、查看课程表、考试、进…

max31865典型电路

PT100读取有很多种方案&#xff0c;常用的惠斯通电桥&#xff0c;和专用IC max31865 。 电阻温度检测器(RTD)是一种阻值随温度变化的电阻。铂是最常见、精度最高的测温金属丝材料。铂RTD称为PT-RTD&#xff0c;镍、铜和其它金属亦可用来制造RTD。RTD具有较宽的测温范围&#x…

飞算 JavaAI 与 Spring Boot:如何实现微服务开发效率翻倍?

微服务架构凭借其高内聚、低耦合的特性&#xff0c;成为企业构建复杂应用系统的首选方案。然而&#xff0c;传统微服务开发流程中&#xff0c;从服务拆分、接口设计到代码编写、调试部署&#xff0c;往往需要耗费大量时间与人力成本。Spring Boot 作为 Java 领域最受欢迎的微服…

(2)Docker 常用命令

文章目录 Docker 服务器Docker 镜像Docker 容器本地 RegistryRUN vs CMD vs ENTRYPOINTRUNCMDENTRYPOINT 限制容器对内存、CPU 和 IO 资源的使用内存CPUBlock IO设置权重bps 和 iops cgroup 和 namespacecgroupnamespacMount namespaceUTS namespaceIPC namespacePID namespace…

Django 实现电影推荐系统:从搭建到功能完善(附源码)

前言&#xff1a;本文将详细介绍如何使用 Django 构建一个电影推荐系统&#xff0c;涵盖项目的搭建、数据库设计、视图函数编写、模板渲染以及用户认证等多个方面。&#x1f517;软件安装、环境准备 ❤ 【作者主页—&#x1f4da;阅读更多优质文章、获取更多优质源码】 目录 一…

C#进阶学习(七)常见的泛型数据结构类(2)HashSet和SortedSet

目录 using System.Collections.Generic; // 核心命名空间 一、 HashSet 核心特性 常用方法 属性 二、SortedSet 核心特性 1、整型&#xff08;int、long 等&#xff09; 2、字符串型&#xff08;string&#xff09; 3、字符型&#xff08;char&#xff09; 4、自定义…

SQL之DML(查询语句:select、where)

&#x1f3af; 本文专栏&#xff1a;MySQL深入浅出 &#x1f680; 作者主页&#xff1a;小度爱学习 select查询语句 在开发中&#xff0c;查询语句是使用最多&#xff0c;也是CRUD中&#xff0c;复杂度最高的sql语句。 查询的语法结构 select *|字段1 [, 字段2 ……] from 表…

vue | 不同 vue 版本对复杂泛型的支持情况 · vue3.2 VS vue3.5

省流总结&#xff1a;defineProps 的泛型能力&#xff0c;来直接推导第三方组件的 props 类型 引入第三方库的类型&#xff0c;并直接在 <script setup> 中作为 props 使用。这种类型一般是复杂泛型&#xff08;包含联合类型、可选属性、交叉类型、条件类型等&#xff0…

Unity-无限滚动列表实现Timer时间管理实现

今天我们来做一个UI里经常做的东西&#xff1a;无限滚动列表。 首先我们得写清楚实现的基本思路&#xff1a; 所谓的无限滚动当然不是真的无限滚动&#xff0c;我们只要把离开列表的框再丢到列表的后面就行&#xff0c;核心理念和对象池是类似的。 我们来一点一点实现&#x…

Docker的基本概念和一些运用场景

Docker 是一种开源的容器化平台&#xff0c;可以帮助开发人员更加高效地打包、发布和运行应用程序。以下是 Docker 的基本概念和优势&#xff1a; 基本概念&#xff1a; 容器&#xff1a;Docker 使用容器来打包应用程序及其依赖项&#xff0c;容器是一个独立且可移植的运行环境…

Unity中基于第三方插件扩展的对于文件流处理的工具脚本

在Unity的项目中对应文件处理,在很多地方用到,常见的功能,就是保存文件,加载文件,判断文件或者文件夹是否存在,删除文件等。 在之前已经写过通过C#的IO实现的这些功能,可查看《Unity C# 使用IO流对文件的常用操作》,但是不能保证所有平台都可以使用 现在基于第三方跨…

Flink介绍——实时计算核心论文之MillWheel论文详解

引入 通过前面的文章&#xff0c;我们从S4到Storm&#xff0c;再到Storm结合Kafka成为当时的实时处理最佳实践&#xff1a; S4论文详解S4论文总结Storm论文详解Storm论文总结Kafka论文详解Kafka论文总结 然而KafkaStorm的第一代流式数据处理组合&#xff0c;还面临的三个核心…

python异步协程async调用过程图解

1.背景&#xff1a; 项目中有用到协程&#xff0c;但是对于协程&#xff0c;线程&#xff0c;进程的区别还不是特别了解&#xff0c;所以用图示的方式画了出来&#xff0c;用于理清三者的概念。 2.概念理解&#xff1a; 2.1协程&#xff0c;线程&#xff0c;进程包含关系 一…

【React】获取元素距离页面顶部的距离

文章目录 代码实现 代码实现 import { useEffect, useRef, useState } from react;const DynamicPositionTracker () > {const [distance, setDistance] useState(0);const divRef useRef(null);useEffect(() > {const targetDiv divRef.current;if (!targetDiv) re…

26.OpenCV形态学操作

OpenCV形态学操作 形态学操作&#xff08;Morphological Operations&#xff09;源自二值图像处理&#xff0c;主要用于分析和处理图像中的结构元素&#xff0c;对图像进行去噪、提取边缘、分割等预处理步骤。OpenCV库中提供了丰富的形态学函数&#xff0c;常见的包括&#xf…

逻辑回归:损失和正则化技术的深入研究

逻辑回归&#xff1a;损失和正则化技术的深入研究 引言 逻辑回归是一种广泛应用于分类问题的统计模型&#xff0c;尤其在机器学习领域中占据着重要的地位。尽管其名称中包含"回归"&#xff0c;但逻辑回归本质上是一种分类算法。它的核心思想是在线性回归的基础上添…

大模型面经 | 介绍一下CLIP和BLIP

大家好,我是皮先生!! 今天给大家分享一些关于大模型面试常见的面试题,希望对大家的面试有所帮助。 往期回顾: 大模型面经 | 春招、秋招算法面试常考八股文附答案(RAG专题一) 大模型面经 | 春招、秋招算法面试常考八股文附答案(RAG专题二) 大模型面经 | 春招、秋招算法…

【MCP】第二篇:IDE革命——用MCP构建下一代智能工具链

【MCP】第二篇&#xff1a;IDE革命——用MCP构建下一代智能工具链 一、引言二、IDE集成MCP2.1 VSCode2.1.1 安装VSCode2.1.2 安装Cline2.1.3 配置Cline2.1.4 环境准备2.1.5 安装MCP服务器2.1.5.1 自动安装2.1.5.2 手动安装 2.2 Trae CN2.2.1 安装Trae CN2.2.2 Cline使用2.2.3 内…

【新能源科学与技术】MATALB/Simulink小白教程(一)实验文档【新能源电力转换与控制仿真】

DP读书&#xff1a;新能源科学与工程——专业课「新能源发电系统」 2025a 版本 MATLAB下面进入正题 仿真一&#xff1a;Buck 电路一、仿真目的二、仿真内容&#xff08;一&#xff09;Buck电路基本构成及工作原理&#xff08;二&#xff09;Buck电路仿真模型及元件连接&#xf…