为什么Java的String不可变?

为什么Java的String不可变?

场景: 你在开发多线程用户系统时,发现用户密码作为String传递后,竟被其他线程修改。这种安全隐患源于对String可变性的误解。Java将String设计为不可变类,正是为了解决这类核心问题。

1️⃣ 不可变性的本质:源码级的保护

// JDK String类关键源码
public final class Stringimplements java.io.Serializable, Comparable<String>, CharSequence {// 关键1:final修饰的字符数组private final char value[];// 关键2:哈希值缓存(首次计算后不再变更)private int hash; // Default to 0// 构造方法:深度复制而非直接引用public String(char value[]) {this.value = Arrays.copyOf(value, value.length);}// 没有修改value数组的方法!
}

三大保护机制

  1. final class:禁止继承破坏
  2. private final char[]:字符数组引用不可变
  3. 构造器Arrays.copyOf:防止外部修改原始数组

2️⃣ 不可变性的五大核心价值

▶ 价值1:线程安全的天然保障
// 多线程共享用户凭证
public class AuthService {// 不可变String天然线程安全private final String adminPassword = "S3cr3t!";public boolean login(String input) {// 无需同步锁return adminPassword.equals(input);}
}

优势

  • 多线程共享无需同步
  • 避免死锁和性能损耗
▶ 价值2:哈希优化的关键基础
// String的hashCode实现(JDK源码)
public int hashCode() {int h = hash;if (h == 0 && value.length > 0) {char val[] = value;for (int i = 0; i < value.length; i++) {h = 31 * h + val[i];}hash = h; // 计算后缓存}return h;
}

Map性能优势

  1. 作为HashMap键时,哈希值只需计算一次
  2. 相同字符串可复用哈希值(如常量池字符串)
▶ 价值3:字符串常量池的基石
String s1 = "Java";  // 在常量池创建
String s2 = "Java";  // 复用常量池对象
String s3 = new String("Java"); // 强制堆中创建新对象System.out.println(s1 == s2); // true (同一对象)
System.out.println(s1 == s3); // false (不同对象)

内存优化效果

场景创建对象数内存占用
100次new String("text")100100x
100次"text"字面量11x
▶ 价值4:安全防御的坚固盾牌
// 敏感信息传递
public void processPassword(String password) {// 即使恶意方法尝试修改modifyString(password); // 无效!// password保持原值
}void modifyString(String s) {// 无法修改原始字符串s = "hacked!"; // 只改变局部引用
}

安全场景

  • 网络传输参数
  • 数据库连接凭证
  • 文件路径验证
▶ 价值5:类加载机制的安全保障
// 类加载依赖字符串
public class MyClass {static {System.loadLibrary("nativeLib"); // 依赖不可变路径}
}

系统级影响

  • 类名、方法名等元数据使用String
  • 防止核心类加载被篡改

3️⃣ 性能对比:不可变 vs 可变

测试场景:千万次字符串拼接
// 不可变方案(产生大量中间对象)
String result = "";
for (int i = 0; i < 10_000_000; i++) {result += i; // 每次循环创建新String
}// 可变方案(推荐)
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 10_000_000; i++) {sb.append(i);
}
String result = sb.toString();

性能测试结果

方案执行时间内存消耗对象创建数
直接拼接String超时(>60s)超高1000万+
StringBuilder0.8s稳定1

最佳实践

  • 少量拼接:直接用+
  • 循环/大批量:必须用StringBuilder

4️⃣ 不可变性的实现代价与解决方案

代价:频繁修改的性能损耗
// 反例:在循环中拼接字符串
String path = "";
for (String dir : directories) {path += "/" + dir; // 每次创建新对象!
}// 正解:使用StringBuilder
StringBuilder pathBuilder = new StringBuilder();
for (String dir : directories) {pathBuilder.append("/").append(dir);
}
String path = pathBuilder.toString();
解决方案:可变搭档类
场景特点
StringBuilder单线程字符串操作非线程安全,高性能
StringBuffer多线程字符串操作线程安全,稍慢
char[]超高性能底层操作直接操作字符数组

5️⃣ 现代Java的增强设计

Java 8+ 的紧凑字符串优化
// -XX:+UseCompactStrings 默认开启
public final class String {private final byte[] value; // 不再总是char[]private final byte coder;   // 标识编码(LATIN1/UTF16)
}

优化效果

  • 纯英文字符串内存占用减半(1字节/字符)
  • 保持完全兼容的不可变性

总结:为什么不可变是终极选择?

需求维度不可变String的解决方案可变字符串的风险
线程安全天然支持,无需同步需额外锁机制
哈希性能一次计算,永久缓存每次需重新计算
内存效率常量池复用,减少重复相同字符串多次存储
系统安全核心参数防篡改敏感数据可能被修改
类加载安全保证元数据完整性可能破坏类加载机制

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

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

相关文章

在Ubuntu上使用QEMU学习RISC-V程序(1)起步第一个程序

文章目录一、 引言二、 环境准备三、编写简单的RISC-V程序四、 编译步骤详解五、使用QEMU运行程序六、程序详解七、退出QEMU八、总结附录&#xff1a;QEMU中通过UTRA显示字符工作原理1、内存映射I/O原理2、add.s程序工作流程3、关键指令解析4、QEMU模拟的UART控制器5、为什么不…

R拟合 | 一个分布能看到三个峰,怎么拟合出这三个正态分布的参数? | 高斯混合模型 与 EM算法

1. 效果已知数据符合上图分布&#xff0c;怎么求下图的三个分布的参数mu, sigma&#xff0c;及每个分布的权重 lambda&#xff1f; 2. 代码: 高斯混合模型&#xff08;Gaussian Mixture Model&#xff0c;简称GMM&#xff09; library(mixtools) set.seed(123) # 确保结果可重复…

Excel自动分列开票工具推荐

软件介绍 本文介绍一款基于Excel VBA开发的自动分列开票工具&#xff0c;可高效处理客户对账单并生成符合要求的发票清单。 软件功能概述 该工具能够将客户对账单按照订单号自动拆分为独立文件&#xff0c;并生成可直接导入发票清单系统的标准化格式。 软件特点 这是一款体…

【自用】JavaSE--Stream流

概述获取Stream流集合的stream流集合名.stream( );collection集合List集合与Set集合都属于Collection集合&#xff0c;因此可以直接调用stream方法获取stream流&#xff0c;示例如下结果>map集合map集合存在键值对&#xff0c;因此无法使用该方法直接获取stream流&#xff0…

【Elasticsearch】快照与恢复功能详解

《Elasticsearch 集群》系列&#xff0c;共包含以下文章&#xff1a; 1️⃣ 冷热集群架构2️⃣ 合适的锅炒合适的菜&#xff1a;性能与成本平衡原理公式解析3️⃣ ILM&#xff08;Index Lifecycle Management&#xff09;策略详解4️⃣ Elasticsearch 跨机房部署5️⃣ 快照与恢…

技嘉z370主板开启vtx

技嘉z370vtx应该默认就是开启状态&#xff0c;虽然主板开启的vtx但是系统默认设置会导致vtx不能使用 1. 关闭hyper-V,Windows虚拟机监控程序平台,虚拟机平台 控制面板->程序->启用或关闭windows功能 2.以管理员身份运行CMD bcdedit /set hypervisorlaunchtype off 3.…

Springmvc的自动解管理

中央转发器&#xff08;DispatcherServlet&#xff09;控制器视图解析器静态资源访问消息转换器格式化静态资源管理一、中央转发器Xml无需配置<servlet><servlet-name>chapter2</servlet-name><servlet-class>org.springframework.web.servlet.Dispatc…

C#_定时器_解析

问题一:这里加lock是啥意思?它的原理是, 为什么可以锁住? private readonly Timer _timer;/// <summary>/// 构造函数中初始化定时器/// </summary>public FtpTransferService(){// 初始化定时器_timer new Timer(_intervalMinutes * 60 * 1000);_timer.Elapsed…

Trae开发uni-app+Vue3+TS项目飘红踩坑

前情 Trae IDE上线后我是第一时间去使用体验的&#xff0c;但是因为一直排队问题不得转战Cursor&#xff0c;等到Trae出付费模式的时候&#xff0c;我已经办了Cursor的会员&#xff0c;本来是想等会员过期了再转战Trae的&#xff0c;但是最近Cursor开始做妖了 网上有一堆怎么…

低代码中的统计模型是什么?有什么作用?

低代码开发平台中的统计模型是指通过可视化配置、拖拽操作或少量代码即可应用的数据分析工具&#xff0c;旨在帮助技术人员及非技术人员快速实现数据描述、趋势预测和业务决策。其核心价值在于降低数据分析门槛&#xff0c;使业务人员无需深入掌握统计原理或编程技能&#xff0…

Linux 下在线安装启动VNC

描述 Linux中的VNC就类似于Windows中的远程桌面系统 本文只记录在Cent OS 7的系统下在线安装VNC。 安装VNC 1、安装VNC yum install tigervnc-server2、配置VNC的密码 为用户设置 VNC 密码&#xff08;首次运行会提示输入&#xff0c;也可以提前输入&#xff09; vncpasswd密码…

支持OCR和AI解释的Web PDF阅读器:解决大文档阅读难题

支持OCR和AI解释的Web PDF阅读器&#xff1a;解决大文档阅读难题一、背景&#xff1a;为什么需要这个工具&#xff1f;问题场景解决方案二、技术原理&#xff1a;如何实现这些功能&#xff1f;1、核心技术组件2、工作流程3、关键点三、操作指南1、环境准备2、生成Html代码3、We…

研发过程都有哪些

产品规划与定义 (Product Planning & Definition) 在详细的需求调研之前&#xff0c;通常会进行市场分析、竞品分析、确立产品目标和核心价值。这个阶段决定了“我们要做什么”以及“为什么要做”。 系统设计与架构 (System & Architectural Design) 这是开发的“蓝图”…

旧物回收小程序系统开发——开启绿色生活新篇章

在当今社会&#xff0c;环保已经成为全球关注的焦点话题。随着人们生活水平的提高&#xff0c;消费能力不断增强&#xff0c;各类物品的更新换代速度日益加快&#xff0c;大量旧物被随意丢弃&#xff0c;不仅造成了资源的巨大浪费&#xff0c;还对环境产生了严重的污染。在这样…

UE5 UI 水平框

文章目录slot区分尺寸和对齐方式尺寸&#xff1a;自动模式尺寸&#xff1a;填充模式对齐常用设置所有按钮大小一致&#xff0c;不受文本影响靠右排列和unity的HorizontalLayout不太一样slot 以在水平框中放入带文字的按钮为例 UI如下布置 按钮的大小受slot的尺寸、对齐和内部…

【Golang】Go语言变量

Go语言变量 文章目录Go语言变量一、Go语言变量二、变量声明2.1、第一种声明方式2.2、第二种声明方式2.3、第三种声明方式2.4、多变量声明2.5、打印变量占用字节一、Go语言变量 变量来源于数学&#xff0c;是计算机语言中能存储计算结果或能表示值抽象的概念变量可以通过变量名…

Qt WebEngine Widgets的使用

一、Qt WebEngine基本概念Qt WebEngine中主要分为三个模块&#xff1a;Qt WebEngine Widgets模块&#xff0c;主要用于创建基于C Widgets部件的Web程序&#xff1b;Qt WebEngine模块用来创建基于Qt Quick的Web程序&#xff1b;Qt WebEngine Core模块用来与Chromeium交互。网页玄…

【C++】标准模板库(STL)—— 学习算法的利器

【C】标准模板库&#xff08;STL&#xff09;—— 学习算法的利器学习 STL 需要注意的几点及 STL 简介一、什么是 STL&#xff1f;二、学习 STL 前的先修知识三、STL 常见容器特点对比四、学习 STL 的关键注意点五、STL 学习路线建议六、总结七、下一章 vector容器快速上手学习…

YOLO算法演进综述:从YOLOv1到YOLOv13的技术突破与应用实践,一文掌握YOLO家族全部算法!

引言&#xff1a;介绍目标检测技术背景和YOLO算法的演进意义。YOLO算法发展历程&#xff1a;使用阶段划分方式系统梳理各代YOLO的技术演进&#xff0c;包含早期奠基、效率优化、注意力机制和高阶建模四个阶段。YOLOv13的核心技术创新&#xff1a;详细解析HyperACE机制、FullPAD…

快速将前端得依赖打为tar包(yarn.lock版本)并且推送至nexus私有依赖仓库(笔记)

第一步创建js文件 文件名为downloadNpmPackage.jsprocess.env.NODE_TLS_REJECT_UNAUTHORIZED "0";const fs require("fs"); const path require("path"); const request require("request");// 设置依赖目录 const downUrl "…