news 2026/7/13 15:47:04

JavaString知识梳理

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
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()、循环中用+拼接、空指针判断
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/13 15:47:00

C# Task WaitAll、WaitAny、WhenAll、WhenAny

笔记承接&#xff1a;承接Task获取异步结果笔记&#xff0c;本节课核心四大方法&#xff1a;WaitAll、WaitAny、WhenAll、WhenAny核心区分&#xff1a;带Wait开头同步阻塞&#xff1b;带When开头异步非阻塞&#xff1b;搭配任务工厂回调&#xff0c;补齐限时等待用法窗体控件&a…

作者头像 李华
网站建设 2026/7/13 15:45:32

nabla.nvim插件开发指南:如何为项目贡献新的数学符号支持

nabla.nvim插件开发指南&#xff1a;如何为项目贡献新的数学符号支持 【免费下载链接】nabla.nvim take your scientific notes :pencil2: in Neovim 项目地址: https://gitcode.com/gh_mirrors/na/nabla.nvim nabla.nvim是一款专为Neovim设计的科学笔记插件&#xff0c…

作者头像 李华
网站建设 2026/7/13 15:44:50

NLP核心技术实践之(四)命名实体识别与关系抽取的端到端应用

1. 命名实体识别与关系抽取的技术全景命名实体识别&#xff08;NER&#xff09;和关系抽取&#xff08;RE&#xff09;就像自然语言处理领域的"黄金搭档"——一个负责找出文本中的关键元素&#xff0c;另一个则揭示这些元素之间的联系。想象一下&#xff0c;当你在阅…

作者头像 李华
网站建设 2026/7/13 15:42:56

SGM 代价聚合:4路径与8路径性能对比,P1/P2惩罚项调参实战

SGM代价聚合&#xff1a;4路径与8路径性能对比及P1/P2惩罚项调参实战 1. SGM代价聚合的核心原理与路径选择策略 立体匹配中的半全局匹配&#xff08;SGM&#xff09;算法之所以能在精度和效率之间取得平衡&#xff0c;关键在于其独特的代价聚合机制。与传统局部算法不同&#…

作者头像 李华
网站建设 2026/7/13 15:39:41

让qoder操作doc的步骤

ai-提效部分 由于这个要写各种文档 于是&#xff0c;我想了一下 ai既然可以帮忙操作文件夹下的代码文件 应该也可以操作wps软件的doc文档 首先理解doc文件&#xff0c;在wps里面提供的机制 对doc文件&#xff0c;可以被wps文件怎么操作 这个操作的机制是什么 机制体系是什么 了…

作者头像 李华