Java缓存String(字符串常量池)、Integer (-128 到 127 )
对问题的解释
1. “字符串常量池存储的是string对象的直接引用,而不是直接存放的对象,是一张string table” 的含义
这句话可以从以下几个方面理解:
(1) 字符串常量池的存储内容
- 直接引用:字符串常量池中存储的是指向实际
String对象的引用(即内存地址),而不是直接存储String对象本身。 - 不是直接存放对象:实际的
String对象仍然存在于堆内存中,字符串常量池仅保存这些对象的引用(类似“索引表”)。
(2) “string table” 的作用
- 结构:字符串常量池内部通常以哈希表(Hash Table) 的形式实现,称为
string table。 - 功能:通过哈希表快速查找是否存在与某个字符串内容相同的对象:
- 当程序中出现字符串字面量(如
"hello")时,JVM 会检查string table中是否存在该字符串的引用。 - 如果存在,则直接返回该引用;如果不存在,则创建新对象并将其引用存入
string table。
- 当程序中出现字符串字面量(如
(3) 示例说明
String s1 = "hello"; // 1. 检查常量池是否有"hello",没有则创建并存入
String s2 = "hello"; // 2. 直接返回常量池中的引用,s1和s2指向同一个对象
- 内存分布:
- 常量池中存储的是
"hello"的引用(如地址0x1000)。 - 实际的
"hello"对象在堆内存中,内容包含字符数组{'h', 'e', 'l', 'l', 'o'}。
- 常量池中存储的是
2. 与缓存的关系
字符串常量池本质上是一种缓存机制,其核心目的是复用已有对象,从而节省内存和提升性能:
(1) 缓存的实现原理
- 避免重复创建对象:当多个相同字符串被使用时,通过常量池共享同一个对象,减少内存占用。
String s3 = new String("world"); // 1. 在堆中创建新对象,不放入常量池 String s4 = new String("world"); // 2. 再次创建新对象,导致内存浪费 System.out.println(s3 == s4); // false(两个不同对象) - 通过
intern()手动缓存:若希望new String()创建的对象也进入常量池,需调用intern()方法:String s5 = new String("test").intern(); // 将对象加入常量池 String s6 = "test"; // 直接指向常量池中的对象 System.out.println(s5 == s6); // true(同一个对象)
(2) 缓存的优势
- 节省内存:相同内容的字符串只占用一份内存空间。
- 提升性能:
- 快速查找:通过哈希表(
string table)快速判断字符串是否存在。 - 避免垃圾回收压力:减少短命对象的频繁创建和销毁。
- 快速查找:通过哈希表(
(3) 与 JVM 内存区域的关系
- JDK 7 之前:字符串常量池位于方法区(Method Area)。
- JDK 7 及以后:字符串常量池移至堆内存(Heap),与普通对象存储在同一区域,但通过
string table管理引用。
3. 总结
- 字符串常量池的本质:通过存储对象的引用(而非对象本身)实现一种缓存机制,利用哈希表(
string table)快速复用相同内容的字符串对象。 - 与缓存的关系:它是一种内存优化策略,通过减少对象重复创建来节省内存和提升性能,类似于其他缓存机制(如
Integer的缓存区间)。
常见疑问解答
-
为什么
new String("abc")不进入常量池?- 因为
new总是创建新对象,且不自动调用intern()。若需缓存,需显式调用intern()。
- 因为
-
intern()的作用是什么?- 将字符串对象加入常量池,确保相同内容的字符串共享同一引用。
-
字符串拼接是否会影响常量池?
- 字符串拼接(如
"a" + "b")会生成新对象,但若结果是编译期可知的字面量,则会被自动加入常量池:String s = "a" + "b"; // 编译时合并为"ab",直接使用常量池中的对象
- 字符串拼接(如
通过理解字符串常量池的设计,可以更好地优化代码中字符串的使用,避免内存浪费并提升性能。
例子解释
1. 缓存范围内的值(-128 到 127)
Integer a = 100; // 自动装箱,调用 Integer.valueOf(100)
Integer b = 100; // 再次调用 Integer.valueOf(100),返回缓存中的同一个对象
System.out.println(a == b); // 输出 true(指向同一对象)
- 原因:
a和b的值100在缓存范围内(-128 到 127)。
Integer.valueOf(100)会直接从缓存数组中获取已存在的Integer对象,因此a和b指向同一个对象,==比较返回true。
2. 超出缓存范围的值(如 128 或 -129)
Integer c = 128; // 自动装箱,调用 Integer.valueOf(128)
Integer d = 128; // 调用 Integer.valueOf(128),超出默认缓存范围
System.out.println(c == d); // 输出 false(指向不同对象)
- 原因:
128超出默认缓存范围(-128 到 127)。
Integer.valueOf(128)会新建一个Integer对象,因此c和d是两个不同的对象,==比较返回false。
3. 使用 new 创建对象(绕过缓存)
Integer e = new Integer(100); // 显式 new 对象,绕过缓存
Integer f = new Integer(100); // 再次 new,创建新对象
System.out.println(e == f); // 输出 false(即使值相同,也是不同对象)
- 原因:
使用new关键字会直接创建新对象,完全绕过缓存机制。因此e和f是不同的对象,==返回false。
关键点总结
| 场景 | 行为 | == 结果 | 原因 |
|---|---|---|---|
| 值在缓存范围内(如 100) | 自动装箱调用 Integer.valueOf(),返回缓存中的同一个对象。 | true | a 和 b 指向同一对象。 |
| 值超出范围(如 128) | 自动装箱调用 Integer.valueOf(),但超出缓存范围,因此每次新建对象。 | false | c 和 d 是不同的对象。 |
使用 new 创建对象 | 显式调用构造函数,绕过缓存,每次创建新对象。 | false | e 和 f 是不同的对象。 |
补充说明
1. 如何比较值的大小?
- 使用
equals()方法:System.out.println(c.equals(d)); // 输出 true(比较值,而非引用) System.out.println(e.equals(f)); // 输出 true(值相同) - 避免
==比较:
==比较的是对象的引用(内存地址),而equals()比较的是对象的值。
2. 缓存范围的调整
- 通过 JVM 参数调整:
可以通过-XX:AutoBoxCacheMax=200或设置系统属性java.lang.Integer.IntegerCache.high=200来扩展缓存范围。
示例:// 假设将缓存范围调整为 -128 到 200 Integer g = 150; // 自动装箱,命中缓存 Integer h = 150; // 返回缓存中的对象 System.out.println(g == h); // 输出 true
完整代码示例
public class IntegerCacheExample {public static void main(String[] args) {// 场景1:值在缓存范围内Integer a = 100;Integer b = 100;System.out.println("a == b: " + (a == b)); // true// 场景2:值超出缓存范围Integer c = 128;Integer d = 128;System.out.println("c == d: " + (c == d)); // false// 场景3:使用 new 创建对象Integer e = new Integer(100);Integer f = new Integer(100);System.out.println("e == f: " + (e == f)); // false// 使用 equals 比较值System.out.println("c.equals(d): " + c.equals(d)); // trueSystem.out.println("e.equals(f): " + e.equals(f)); // true}
}
输出结果
a == b: true
c == d: false
e == f: false
c.equals(d): true
e.equals(f): true
通过这个例子,可以清晰地看到 Integer 缓存机制的作用和不同场景下的行为差异。
相关文章:
Java缓存String(字符串常量池)、Integer (-128 到 127 )
对问题的解释 1. “字符串常量池存储的是string对象的直接引用,而不是直接存放的对象,是一张string table” 的含义 这句话可以从以下几个方面理解: (1) 字符串常量池的存储内容 直接引用:字符串常量池中存储的是指向实际 Stri…...
消息队列的特性与使用场景:Kafka、ActiveMQ、RabbitMQ与RocketMQ的深度剖析
在分布式系统和微服务架构中,消息队列是实现服务间通信和解耦的核心组件。Kafka、ActiveMQ、RabbitMQ和RocketMQ是当前最受欢迎的消息队列解决方案,它们各自具有独特的特性和适用场景。本文将从特性和使用场景两个维度进行对比分析,帮助读者更…...
开发、科研、日常办公工具汇总(自用,持续更新)
主要记录汇总一下自己平常会用到的网站工具,方便查阅。 update:2025/2/11(开发网站补一下) update:2025/2/21(补充一些AI工具,刚好在做AI视频相关工作) update:2025/3/7&…...
解决VueI18n使用浏览器插件翻译后,切换国际化失效的问题
问题复现 在使用Vue-i18n对页面进行国际化的时候,使用浏览器翻译插件(如腾讯翻译)后,切换国际化语言,随后当我们关闭浏览器翻译插件后,再次切换国际化语言,原来被翻译的文字无法正确切换 出现…...
HTML5 drag API实现列表拖拽排序
拖拽API(Drag and Drop API)是HTML5提供的一组功能,使得在网页上实现拖放操作变得更加简单和强大。这个API允许开发者为网页元素添加拖拽功能,用户可以通过鼠标将元素拖动并放置到指定的目标区域。 事件类型 dragstart࿱…...
改变一生的思维模型【11】升维
升维思维模型:突破认知局限的破局法则 一、定义与核心逻辑 升维思维是通过增加分析维度,将问题投射到更高认知层次寻找解决方案的思考方式。其本质是跳出原有竞争维度,在更广阔的空间重构游戏规则。核心逻辑在于:当低维战场陷入…...
【动手学深度学习】#2线性神经网络
主要参考学习资料: 《动手学深度学习》阿斯顿张 等 著 【动手学深度学习 PyTorch版】哔哩哔哩跟李牧学AI 目录 2.1 线性回归2.1.1 线性回归的基本元素线性模型损失函数解析解随机梯度下降 2.1.3 最大似然估计 2.2 线性回归从零开始实现2.2.1 生成数据集2.2.2 读取数…...
计算机网络——NAT
一、什么是NAT? NAT(Network Address Translation,网络地址转换) 是一种将 私有IP地址 与 公有IP地址 相互映射的技术,主要用于解决IPv4地址不足的问题。它像一名“翻译官”,在数据包经过路由器或防火墙时…...
Stable Deffusion--常见模型插件详解
1.Checkpoint大模型 Checkpoint 是生成图像的基础模型,决定了整体画风如动漫、写实、机甲等。它是必选项,所有图像生成必须基于一个主模型。文件体积较大通常 1.5GB 以上,格式为 .ckpt 或 .safetensors。 存放位置为:\models\Sta…...
防重复提交详解:从前端Vue到后端Java的全面解决方案
防重复提交详解:从前端Vue到后端Java的全面解决方案 一、重复提交问题概述 在Web应用开发中,表单重复提交是一个常见问题,可能导致: 数据库中出现重复记录重复执行业务逻辑(如多次扣款)系统资源浪费用户…...
同一子网通信
添加交换机后的通信流程 1. 同一子网内(使用交换机) 判断是否在同一子网: 主机A通过子网掩码判断主机B的IP地址是否属于同一子网。若在同一子网,主机A需要通过ARP获取主机B的MAC地址。 ARP请求(广播)&…...
快速迭代:利用 nodemon 和其他工具实现 Express.js 热更新
在开发 Express.js 应用时,热更新(Hot Reloading)可以显著提升开发效率,因为它允许你在修改代码后立即看到效果,而无需手动重启服务器。以下是几种实现热更新的方法和工具,帮助你在开发过程中更高效地工作。…...
BUG日志:Maven项目启动报错(文件名或者文件扩展名过长)
Bug日志编号:[Maven-001] 标题:Windows系统下Maven项目因路径过长导致命令行执行失败 1. 问题描述 现象:执行mvn clean install时报错: The input line is too long 或 The filename or extension is too long触发条件…...
IntelliJ IDEA 快捷键系列:重命名快捷键详解
目录 引言一、默认重命名快捷键1. Windows 系统2. Mac 系统 二、操作步骤与技巧1. 精准选择重命名范围2. 智能过滤无关内容 三、总结 引言 在代码重构中,重命名变量、类、方法 是最常用的操作之一。正确使用快捷键可以极大提升开发效率。本文针对 Ma…...
零基础掌握分布式ID生成:从理论到实战的完整指南 [特殊字符]
一、为什么需要分布式ID? 🤔 在单机系统中,使用数据库自增ID就能满足需求。但在分布式系统中,多个服务节点同时生成ID时会出现以下问题: ID冲突:不同节点生成相同ID 扩展困难:数据库自增ID无法…...
使用python反射,实现pytest读取yaml并发送请求
pytest yaml yaml - feature: 用户模块story: 登录title: 添加用户request:method: POSTurl: /system/user/listheaders: nullparams: nullvalidate: nullread_yaml_all def read_yaml_all(path):with open(path, r, encodingutf-8) as f:value yaml.safe_load(f)return v…...
点灯、点各式各样的灯
鱼离水则身枯,心离书则神索。 前言闪灯呼吸灯流水灯二进制数显示灯蜂鸣器节拍流水音乐会总结 前言 上回书咱们简单了解了一点有关特殊功能寄存器sfr、通用输入输出GPIO、位操作运算符sbit和一个不靠单片机上的晶振(拿来定时的)的依托于单片机CPU空操作的ms级延时函…...
Matlab 汽车悬架系统动力学建模与仿真
1、内容简介 略 Matlab 170-汽车悬架系统动力学建模与仿真 可以交流、咨询、答疑 2、内容说明 略 本文对题目给定的1/2汽车四自由度模型,建立状态空间模型进行系统分析,并通过MATLAB仿真对系统进行稳定性、可控可观测性分析,对得的结果进行…...
专访数势科技谭李:智能分析 Agent 打通数据平权的最后一公里
作者|斗斗 编辑|皮爷 出品|产业家 伦敦塔桥下的泰晤士河底,埋藏着工业革命的隐秘图腾——布鲁内尔设计的隧道盾构机。在19世纪城市地下轨道建设的过程中,这个直径11米的钢铁巨兽没有选择拓宽河道,而是开创了地下通行的新维度。 “我们不…...
了解浏览器
本文来自腾讯元宝 Chrome浏览器(Google Chrome)是由Google开发的一款免费网页浏览器,自2008年发布以来凭借其高效、安全、简洁的特点成为全球市场份额最高的浏览器。以下是其核心信息及最新动态的综合分析: 一、核心优势与技术特点…...
2、操作系统之软件基础
一、硬件支持系统 ,系统管理硬件 操作系统核心功能可以分为: 守护者:对硬件和软件资源的管理协调者:通过机制,将各种各样的硬件资源适配给软件使用。 所以为了更好的管理硬件,操作系统引进了软件。其中3大…...
STC89C52单片机学习——第20节: [8-2]串口向电脑发送数据电脑通过串口控制LED
写这个文章是用来学习的,记录一下我的学习过程。希望我能一直坚持下去,我只是一个小白,只是想好好学习,我知道这会很难,但我还是想去做! 本文写于:2025.03.15 51单片机学习——第20节: [8-2]串口向电脑发送数据&电脑通过串口控制LED 前言…...
K8S下nodelocaldns crash问题导致域名请求响应缓慢
前言 最近做项目,有业务出现偶发的部署导致响应很慢的情况,据了解,业务使用域名访问,相同的nginx代理,唯一的区别就是K8S重新部署了。那么问题大概率出现在容器平台,毕竟业务是重启几次正常,偶…...
CVPR2024 | TT3D | 物理世界中可迁移目标性 3D 对抗攻击
Towards Transferable Targeted 3D Adversarial Attack in the Physical World 速览总结摘要-Abstract引言-Introduction相关工作-Related Work方法-MethodologyPreliminray-预备知识问题表述-Problem FormulationNeRF参数空间中的双重优化-Dual Optimization in NeRF Paramete…...
全面对比分析:HDMI、DP、DVI、VGA、Type-C、SDI视频接口特点详解
在当今的多媒体时代,视频接口的选择对于设备连接和显示效果至关重要。不同的视频接口在传输质量、兼容性、带宽等方面各有优劣。本文将全面对比分析常用的视频接口HDMI、DP、DVI、VGA、Type-C、SDI,帮助读者更好地理解它们的特点和适用场景。 一、HDMI&…...
传输层自学
传输实体:完成传输层任务的硬件或软件 可能位于: 操作系统内核独立的用户进程绑定在网络应用中的链接库网络接口卡 1.功能: 网络层与传输层作用范围比较? 网络层负责把数据从源机送达到目的机 传输层负责把数据送达到具体的应…...
使用爬虫获取自定义API操作API接口
1. 引言 在现代Web开发中,API(应用程序接口)是前后端通信的桥梁。通过API,前端可以从后端获取数据,进行各种操作。而爬虫是一种自动化工具,用于从网站上提取数据。本文将详细介绍如何使用爬虫获取自定义AP…...
微服务架构下前端如何配置 OpenAPI 接口
在微服务架构中,后端通常由多个独立的服务组成,每个服务可能提供自己的 API 接口。为了在前端项目中高效地调用这些 API,可以使用 OpenAPI 规范生成客户端代码。以下是详细的配置步骤和最佳实践: 1. 理解 OpenAPI 规范 OpenAPI 是…...
Kotlin知识体系(二) : Kotlin的七个关键特性
前言 在Android开发中,Kotlin以其简洁的语法和强大的特性显著提升了开发效率。本文将解析Kotlin中7个关键特性,通过代码示例展示它们在实际开发中的应用。 一、构造函数:主次分明 主构造函数 class User constructor(_name: String) { //…...
FreeRTOS源码概述
FreeRTOS源码概述 1 FreeRTOS目录结构 使用 STM32CubeMX 创建的 FreeRTOS 工程中,FreeRTOS 相关的源码如下: 主要涉及2个目录: Core Inc 目录下的 FreeRTOSConfig.h 是配置文件Src 目录下的 freertos.c 是 STM32CubeMX 创建的默认任务 Mi…...
