当前位置: 首页 > news >正文

JavaString知识梳理

作者:没有四次元口袋的蓝胖
日期:2026-07-12
标签:Java, String

Java String知识梳理

一、String 的不可变性

String 是不可变的(immutable),一旦创建,其值就不能修改。

1.1 为什么设计成不可变?

原因

  1. 线程安全:不可变对象天然线程安全,无需同步
  2. 字符串常量池:可以共享相同字符串,节省内存
  3. hashCode 缓存:创建时计算一次 hashCode,后续直接使用(HashMap 的 key)
  4. 安全性:作为参数传递时不会被篡改(数据库连接、网络地址等)

1.2 源码分析

publicfinalclassString{privatefinalcharvalue[];// final 修饰,引用不可变// Java 9+ 改为:private final byte[] value;publicString(Stringoriginal){this.value=original.value;// 直接复用// ...}}

关键

  • final class String:String 类不可继承
  • final char[] value:数组引用不可变(但数组内容理论上可变,Java 通过不提供修改方法保证)

1.3 "不可变"的陷阱

Stringstr="hello";str=str+" world";// 看起来修改了,实际上创建了新对象// 实际过程:// 1. 创建 "hello"// 2. 创建 " world"// 3. 创建新字符串 "hello world"// 4. str 引用指向新字符串// 5. 原来的 "hello" 还在(可能被其他引用使用)

💡 String 的"修改"都是创建新对象,原对象不变。


二、字符串常量池

2.1 常量池的作用

字符串常量池(String Pool)是 JVM 堆内存中的一块特殊区域,用于存储字符串字面量。

优点

  • 节省内存:相同字符串共享
  • 提高效率:避免重复创建

2.2 创建方式的区别

// 方式1:字面量(常量池)Strings1="hello";// 方式2:new 对象(堆内存)Strings2=newString("hello");// 方式3:拼接(编译器优化)Strings3="hel"+"lo";// 编译期优化为 "hello"

2.3 == vs equals

Strings1="hello";Strings2="hello";Strings3=newString("hello");Strings4=newString("hello");System.out.println(s1==s2);// true(同一个常量池对象)System.out.println(s1==s3);// false(s3 在堆中)System.out.println(s3==s4);// false(两个不同的对象)System.out.println(s1.equals(s3));// true(内容相同)

规则

  • ==:比较引用(内存地址)
  • equals():比较内容

⚠️String 必须用 equals() 比较内容,不能用 ==。

2.4 intern() 方法

intern()会将字符串放入常量池(如果不存在),并返回常量池中的引用。

Strings1=newString("hello");// 堆中创建Strings2=s1.intern();// 将 "hello" 放入常量池(如果不存在)Strings3="hello";// 从常量池获取System.out.println(s1==s2);// false(s1 在堆,s2 在常量池)System.out.println(s2==s3);// true(都是常量池中的 "hello")

Java 6 vs Java 7+

  • Java 6:常量池在永久代(PermGen),intern() 会复制字符串
  • Java 7+:常量池移到堆中,intern() 不会复制,只是添加引用

三、字符串拼接

3.1 拼接方式对比

方式类型线程安全性能适用场景
+语法糖安全低(每次创建新对象)少量拼接
StringBuilder可变不安全单线程大量拼接
StringBuffer可变安全中(有同步开销)多线程拼接
String.concat()方法安全不常用

3.2 编译器优化

// 编译期常量拼接(优化)Strings1="hel"+"lo";// 编译为 "hello"// 运行期拼接(不优化)Stringa="hel";Stringb="lo";Strings2=a+b;// 运行期拼接,创建新对象// final 修饰(优化)finalStringa="hel";finalStringb="lo";Strings3=a+b;// 编译期优化为 "hello"

3.3 StringBuilder vs StringBuffer

// StringBuilder(非线程安全,性能高)StringBuildersb1=newStringBuilder();sb1.append("hello");sb1.append(" ");sb1.append("world");Stringresult1=sb1.toString();// StringBuffer(线程安全,性能低)StringBuffersb2=newStringBuffer();sb2.append("hello");sb2.append(" ");sb2.append("world");Stringresult2=sb2.toString();

底层实现

// StringBuffer 的 append 方法(有 synchronized)publicsynchronizedStringBufferappend(Stringstr){super.append(str);returnthis;}// StringBuilder 的 append 方法(无 synchronized)publicStringBuilderappend(Stringstr){super.append(str);returnthis;}

选择建议

  • 单线程:用 StringBuilder
  • 多线程:用 StringBuffer
  • 少量拼接:直接用+

四、String 常见方法

4.1 常用方法速查

Stringstr="Hello World";// 长度str.length();// 11// 字符操作str.charAt(0);// 'H'str.toCharArray();// ['H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd']// 截取str.substring(0,5);// "Hello"// 查找str.indexOf("World");// 6str.lastIndexOf("l");// 9str.contains("World");// truestr.startsWith("Hello");// truestr.endsWith("World");// true// 大小写str.toLowerCase();// "hello world"str.toUpperCase();// "HELLO WORLD"// 去除空白" hello ".trim();// "hello"// 替换str.replace("World","Java");// "Hello Java"str.replaceAll("[aeiou]","*");// "H*ll* W*rld"// 分割"a,b,c".split(",");// ["a", "b", "c"]// 比较str.equals("Hello World");// truestr.equalsIgnoreCase("hello world");// truestr.compareTo("Hello Java");// 正数(字典序)// 格式化String.format("Name: %s, Age: %d","Tom",20);// "Name: Tom, Age: 20"

4.2 性能陷阱

// 错误:循环中用 + 拼接(每次都创建新对象)Stringresult="";for(inti=0;i<1000;i++){result+=i;// 创建 1000 个 String 对象}// 正确:用 StringBuilderStringBuildersb=newStringBuilder();for(inti=0;i<1000;i++){sb.append(i);}Stringresult=sb.toString();

五、String 与其他类型转换

5.1 String ↔ 基本类型

// String → intintnum1=Integer.parseInt("123");// 123intnum2=Integer.valueOf("123");// 123// int → StringStrings1=String.valueOf(123);// "123"Strings2=Integer.toString(123);// "123"Strings3=123+"";// "123"(不推荐)// String → doubledoubled=Double.parseDouble("3.14");// String → booleanbooleanb=Boolean.parseBoolean("true");

5.2 String ↔ 字符数组

// String → char[]char[]chars="hello".toCharArray();// char[] → StringStrings1=newString(chars);Strings2=String.valueOf(chars);// String → byte[](编码)byte[]bytes="你好".getBytes("UTF-8");// byte[] → String(解码)Strings3=newString(bytes,"UTF-8");

六、常见面试题

6.1 String、StringBuffer、StringBuilder 的区别?

对比StringStringBufferStringBuilder
可变性不可变可变可变
线程安全安全(不可变)安全(synchronized)不安全
性能低(每次创建新对象)
适用少量操作多线程拼接单线程拼接

6.2 String 为什么是不可变的?

  1. 安全性:作为参数传递时不会被篡改
  2. 线程安全:不可变对象天然线程安全
  3. hashCode 缓存:创建时计算一次,HashMap 的 key 效率高
  4. 字符串常量池:可以共享相同字符串

6.3 String s = new String(“hello”) 创建了几个对象?

答案:可能创建 1 个或 2 个对象。

情况 1:常量池中已有 “hello”

  • 只创建 1 个对象(堆中的 String 对象)
  • “hello” 字面量在常量池,new 的对象在堆中

情况 2:常量池中没有 “hello”

  • 创建 2 个对象
  • 1 个在常量池(“hello” 字面量)
  • 1 个在堆中(new String)

6.4 如何判断 String 是否为空?

// 错误:会 NPEif(str.equals("")){}// 推荐if(str==null||str.isEmpty()){}// Java 8if(str==null||str.length()==0){}// 兼容老版本// Java 11+if(str==null||str.isBlank()){}// 包含空白字符

6.5 String.intern() 的作用?

  1. 检查常量池中是否存在该字符串
  2. 存在则返回常量池中的引用
  3. 不存在则将字符串放入常量池,返回引用

应用场景

  • 节省内存(大量重复字符串)
  • 字符串比较(用 == 代替 equals)

七、思维导图速览

┌───────────────────────────────────────────────────┐ │ String 高频考点 │ ├───────────────────────────────────────────────────┤ │ │ │ 不可变性 │ │ • final class String │ │ • final char[] value │ │ • 原因:安全/线程安全/hashCode缓存/常量池 │ │ │ │ 常量池 │ │ • 字符串字面量存储在常量池 │ │ • new String() 在堆中创建 │ │ • intern() 放入常量池 │ │ • == vs equals │ │ │ │ 拼接 │ │ • +:语法糖,编译器优化 │ │ • StringBuilder:单线程,性能高 │ │ • StringBuffer:多线程,性能中 │ │ │ │ 常见方法 │ │ • length/charAt/substring/indexOf/contains │ │ • replace/replaceAll/split/trim │ │ • equals/equalsIgnoreCase/compareTo │ │ │ │ 面试题 │ │ • String vs StringBuffer vs StringBuilder │ │ • 为什么不可变 │ │ • new String("hello") 创建几个对象 │ │ • intern() 的作用 │ │ │ │ 陷阱 │ │ ✗ 用 == 比较 String │ │ ✗ 循环中用 + 拼接 │ │ ✗ str.equals("") 会 NPE │ │ ✓ 用 equals() 比较内容 │ │ ✓ 用 StringBuilder 拼接 │ │ ✓ str == null || str.isEmpty() 判空 │ └───────────────────────────────────────────────────┘

八、写在最后

  1. String 不可变性是核心,要能说出 4 个原因和源码实现
  2. 字符串常量池是高频考点,要理解new String()和字面量的区别
  3. StringBuilder vs StringBuffer要知道什么时候用哪个
  4. 常见陷阱==vsequals()、循环中用+拼接、空指针判断
http://www.cnnetsun.cn/news/3346049.html

相关文章:

  • C# Task WaitAll、WaitAny、WhenAll、WhenAny
  • nabla.nvim插件开发指南:如何为项目贡献新的数学符号支持
  • NLP核心技术实践之(四)命名实体识别与关系抽取的端到端应用
  • 革命性AI视频生成工具AnyFlow-FAR-Wan2.1-1.3B-Diffusers:NVIDIA推出的任意步数视频扩散模型完全指南
  • SGM 代价聚合:4路径与8路径性能对比,P1/P2惩罚项调参实战
  • 让qoder操作doc的步骤
  • AI+港口智慧物流:集装箱调度+船舶预测+堆场优化
  • 如何在AMD NPU上部署Llama-2-7b-hf_rai_1.7.1_npu_4K:完整新手教程
  • AI+危险品运输:全程监控+应急响应+合规管理
  • AI+供应链韧性:风险预测+应急调度+弹性优化
  • gemma-4-e4b-it-OptiQ-4bit的部署优化:内存管理、批处理和性能调优最佳实践
  • 21天学pcie--Linux 下查看 BAR:lspci -vv(实战必会)
  • ChatGPT生成抖音脚本不火?问题不在模型,在于你漏掉了这4个关键元数据注入节点
  • 终极指南:如何免费解锁Wand-Enhancer的所有高级游戏修改功能
  • 一个爬虫项目改成 AI 流程后,最难的部分完全变了
  • 如何快速上手AnyFlow-FAR-Wan2.1-1.3B-Diffusers:5分钟搭建文本到视频生成环境
  • RoCEv2 网络的完整协议栈简介
  • Qwen2.5-7B-Instruct_rai_1.7.1_npu_4K:AMD Ryzen AI上的高效大语言模型部署指南
  • 如何用Gotalk实现分布式系统负载均衡:心跳机制详解
  • NVIDIA-Nemotron-3-Nano-4B-OptiQ-4bit:Apple Silicon专属的革命性混合架构大模型来了!
  • 从投诉率骤升23%到NPS提升18分,我们如何用反馈聚类+情感时序建模重构产品决策链,
  • 别再写for循环了:2024最前沿的向量化AI分析法——用PyTorch+Polars实现毫秒级特征生成(含GPU加速实测对比数据)
  • 解密Enigma虚拟打包文件:evbunpack实战指南
  • 【技术实践】上海交通大学学位论文本地化保存与自动化抓取方案解析
  • AI绘图实战:蜡笔风个人主页海报提示词拆解
  • shell,Linux用户和工作组,以及Linux文件系统·权限
  • 中小企业出海行动推进:全球市场研究网络赋能精准洞察与高效供应商对接
  • DAKeyboardControl:iOS键盘交互的革命性解决方案 - 一行代码实现iMessages式滑动关闭
  • TeleStyleV2-Qwen-Image-Edit-2511-bf16:革命性图像风格迁移模型,4步实现专业级内容保留编辑 [特殊字符]
  • Ornith-1.0-9B-6bit常见问题解答:解决10个最常遇到的运行问题