【Java String】类深度解析:从原理到高效使用技巧

🎁个人主页:User_芊芊君子

🎉欢迎大家点赞👍评论📝收藏⭐文章

🔍系列专栏:【Java】内容概括


【前言】

在 Java 编程中,String 类是使用频率最高的类之一,也是初学者接触最早的引用类型之一。但正是因为其基础且常用,很多开发者往往忽略了它的底层原理和高级特性。本文将从 String 类的底层实现、核心方法到性能优化、常见误区,全方位解析 Java String 类,帮你彻底搞懂这一基础却关键的类。

文章目录:

一、String类本质特征

1.String类定义

String 是引用类型,内部不储存字符串本身

  • 不可变性(Immutable): value 数组被 private final 修饰,且 String 类没有提供修改数组的方法,确保字符串创建后无法被修改
  • final 修饰:String 类被 final 修饰,无法被继承,保证了字符串操作的安全性和一致性
  • 字符数组存储:底层通过字符数组 char\[\] 存储字符串内容(JDK 9+ 后改为 byte\[\] 配合编码标识,优化存储效率)

2.字符串创建及内存机制

创建方式 语法示例 内存分配 适用场景
字面量创建 String s = "hello" 优先使用常量池中的对象,若不存在则创建 已知固定字符串,优先使用
new 关键字创建 String s = new String("hello") 一定会在堆中创建新对象,可能同时在常量池创建 需要显式创建新对象时使用

String类提供的常用的构造方法如下:

java 复制代码
public class Test {
    public static void main(String[] args) {
        String s1 = "hello";
        String s2 = new String("hello");
        char[] arr = {'h','e','l','l','0'};
        String s3 = new String(arr);
        System.out.println(s1);
        System.out.println(s2);
        System.out.println(s3);
    }
}

Java构造方法在线文档(可点击查看)

3.字符串常量池(StringTable)

字符串常量池是 JVM 为优化字符串存储引入的内存区域(属于方法区),其核心作用是复用相同内容的字符串对象。

常量池的工作流程:

  1. 当使用字面量创建字符串时,JVM 先检查常量池是否存在该字符串
  2. 若存在则直接返回引用,若不存在则创建新对象放入常量池并返回引用
  3. 通过 intern() 方法可将堆中的字符串对象移入/引用常量池对象(当调⽤intern() ⽅法时,如果字符串常量池中已经包含⼀个等于此String对象的字符串(由equals(Object)⽅法确定),则返回常量池中的字符串。否则,将此String对象添加到常量池中,并返回此String对象的引⽤。)

eg:

java 复制代码
public static void main(String[] args) {
        String str1 = "abc";
        String str2 = "abc";
        System.out.println(str1 == str2);
    }
    //返回true
  • str1 储存时,"abc"会先储存到字符串常量池中
  • str2再次储存时,会先检查字符串常量池中是否存在"abc",如果存在,就不会再次存储

eg:

java 复制代码
public static void main(String[] args) {
        String str1 = new String("abc");
        String str2 = new String("abc");
        System.out.println(str1 == str2);
    }
    //返回false
  • 第一次储存时,会将"abc"存储到常量池中;
  • 每次new,都会再堆中实例化新的对象
  • 储存str2时,会使用常量池的"abc"对象进行存储

二、String 类核心方法

1.字符串方法比较

方法 功能描述 示例 返回值
equals() 比较字符串内容是否相等 "abc".equals("abc") boolean(相等返回true,否则false)
equalsIgnoreCase() 忽略大小写比较内容 "Abc".equalsIgnoreCase("aBC") boolean(忽略大小写后相等返回true,否则false)
compareTo() 按字典顺序比较 "a".compareTo("b") int(前者小于后者返回负数,相等返回0,前者大返回正数)
==运算符 比较对象的引用是否相同 "a" == new String("a") boolean(引用相同返回true,否则false)

1.1"=="比较是否引用同一个对象

注意:对于内置类型," == "⽐较的是变量中的值;对于引⽤类型" == "⽐较的是引⽤中的地址。

java 复制代码
public static void main(String[] args) {
        int a = 10;
        int b = 20;
        int c = 10;
        //对于基本类型变量,==⽐较两个变量中存储的值是否相同
        System.out.println(a == b);// false
        System.out.println(a == c);// true
        //对于引⽤类型变量==⽐较两个引⽤变量引⽤的是否为同⼀个对象
        String s1 = new String("hello");
        String s2 = new String("hello");
        String s3 = new String("world");
        String s4 = s1;
        System.out.println(s1 == s2);   // false
        System.out.println(s2 == s3);   // false
        System.out.println(s1 == s4);   // true
    }

1.2 equals ⽅法:按照字典序⽐较

  • String类重写了⽗类Object中equals⽅法,Object equals默认按照"=="⽐较
  • String重写equals⽅法后,按照如下规则进⾏⽐较,⽐如:s1.equals(s2)
java 复制代码
public static void main(String[] args) {
        String s1 = new String("hello");

        String s2 = new String("hello");
        String s3 = new String("Hello");
        // s1 s2 s3引⽤的是三个不同对象,因此==⽐较结果全部为false
        System.out.println(s1 == s2);// false
        System.out.println(s1 == s3);// false
        // equals⽐较:String对象中的逐个字符
        //虽然s1与 s2 引⽤的不是同⼀个对象,但是两个对象中放置的内容相同,因此输出true
        // s1与s3引⽤的不是同⼀个对象,⽽且两个对象中内容也不同,因此输出false
        System.out.println(s1.equals(s2));  // true
        System.out.println(s1.equals(s3));  // false
    }

1.3 compareTo ⽅法:按照字典序进⾏⽐较

  • equals返回的是boolean类型,⽽compareTo返回的是int类型
  • compareToIgnoreCase ⽅法:与compareTo⽅式相同,但是忽略⼤⼩写⽐较
java 复制代码
 public static void main(String[] args) {
        String s1 = new String("abc");
        String s2 = new String("ac");
        String s3 = new String("abc");
        String s4 = new String("abcdef");
        System.out.println(s1.compareTo(s2));   //不同输出字符差值-1
        System.out.println(s1.compareTo(s3));   //相同输出0
        System.out.println(s1.compareTo(s4));   //前k 个字符完全相同,输出⻓度差值-3
    }

2.字符串查找

方法 功能
char charAt(int index) 返回index位置上字符,如果index为负数或者越界,抛出IndexOutOfBoundsException异常
int indexOf(int ch) 返回ch第一次出现的位置,没有返回-1
int indexOf(int ch, int fromIndex) 从fromIndex位置开始找ch第一次出现的位置,没有返回-1
int indexOf(String str) 返回str第一次出现的位置,没有返回-1
int indexOf(String str, int fromIndex) 从fromIndex位置开始找str第一次出现的位置,没有返回-1
int lastIndexOf(int ch) 从后往前找,返回ch第一次出现的位置,没有返回-1
int lastIndexOf(int ch, int fromIndex) 从fromIndex位置开始找,从后往前找ch第一次出现的位置,没有返回-1
int lastIndexOf(String str) 从后往前找,返回str第一次出现的位置,没有返回-1
int lastIndexOf(String str, int fromIndex) 从fromIndex位置开始找,从后往前找str第一次出现的位置,没有返回-1
java 复制代码
public static void main(String[] args) {
        String s = "aaabbbcccaaabbbccc";
        System.out.println(s.charAt(5));//返回下标对应的字符
        System.out.println(s.indexOf('a'));//返回a第一次出现的下标
        System.out.println(s.indexOf('a',5));//从5开始找a,如果没有,返回-1
        System.out.println(s.indexOf("aaa"));//返回aaa第一次出现的位置,没有返回-1
        System.out.println(s.indexOf("aaa",5));//从5开始找aaa,如果没有,返回-1
        System.out.println(s.lastIndexOf('b'));//从后往前找,返回b第一次出现的位置
        System.out.println(s.lastIndexOf('b',10));//从后往前找,返回b第一次出现的位置
        System.out.println(s.lastIndexOf("bbb"));//从后往前找,返回bbb第一次出现的位置
        System.out.println(s.lastIndexOf("bbb",10));//从10开始,从后往前,bbb第一次出现的位置
    }

3.字符串转换

  • 数值和字符串转换
java 复制代码
public static void main(String[] args) {
        //数字转字符串
        String s1 = String.valueOf(123);
        String s2 = String.valueOf(12.3);
        String s3 = String.valueOf(true);
        String s4 = String.valueOf(new Person("Peter",20));
        System.out.println(s1);
        System.out.println(s2);
        System.out.println(s3);
        System.out.println(s4);
        //字符串转数字
        int str1 = Integer.parseInt("123");
        Double str2 = Double.parseDouble("12.3");
        System.out.println(str1);
        System.out.println(str2);
    }
  • 大小写转换
java 复制代码
public static void main(String[] args) {
        String s1 = "hello";
        String s2 = "HELLO";
        System.out.println(s1.toUpperCase());//小转大
        System.out.println(s2.toLowerCase());//大转小
    }
  • 字符串转数组
java 复制代码
public static void main(String[] args) {
        String s = "hello";
        //字符串转数组
        char[] ch = s.toCharArray();
        for (int i = 0; i < ch.length; i++) {
            System.out.println(ch[i]);
        }
        //数组转字符串
        String s2 = new String(ch);
        System.out.println(s2);
    }
  • 格式化
java 复制代码
public static void main(String[] args) {
        String s = String.format("%d-%d-%d",2025,8,5);
        System.out.println(s);
    }
  • 字符串替换
方法 功能
String replaceAll(String regex, String replacement) 替换所有的指定内容
String replaceFirst(String regex, String replacement) 替换首个内容
java 复制代码
public static void main(String[] args) {
        String str = "hello";
        System.out.println(str.replaceAll("o","e"));
        System.out.println(str.replaceFirst("l","e"));
    }

【注意】:由于字符串是不可变对象,替换不修改当前字符串,⽽是产⽣⼀个新的字符串.

  • 字符串拆分
方法 功能
String\[\] split(String regex) 将字符串全部拆分
String\[\] split(String regex, int limit) 将字符串以指定的格式,拆分为limit组

(1)按照空格拆分

java 复制代码
public static void main(String[] args) {
        String str = "hello world hello";
        String[] ret = str.split(" ");//按照空格拆分
        for (String s: ret){
            System.out.println(s);
        }
    }

(2)按照空格拆分,分成两组

java 复制代码
public static void main(String[] args) {
        String str = "hello world hello";
        String[] ret = str.split(" ",2);//按照空格拆分,分成两组
        for (String s: ret){
            System.out.println(s);
        }
    }

(3)拆分IP地址

java 复制代码
public static void main(String[] args) {
        String str = "145.169.1.1";
        String[] ret = str.split("\\.");
        for (String s: ret){
            System.out.println(s);
        }
    }

(4)多次拆分

java 复制代码
public static void main(String[] args) {
        String str = "name=zhangsan&age=18" ;
        String[] result = str.split("&") ;
        for (int i = 0; i < result.length; i++) {
            String[] temp = result[i].split("=");
            System.out.println(temp[0] + " = " + temp[1]);
        }
    }
  • 字符串截取
方法 功能
String substring(int beginIndex) 从指定索引截取到结尾
String substring(int beginIndex, int endIndex) 截取部分内容

【注意】:区间左闭右开

java 复制代码
public static void main(String[] args) {
        String str = "hello world";
        System.out.println(str.substring(9));
        System.out.println(str.substring(5,9));
    }
  • 去除左右两边空格
方法 功能
String trim() 去掉字符串中的左右空格,保留中间空格
java 复制代码
 String str = "   hello  world   " ; 
 System.out.println("["+str+"]");
 System.out.println("["+str.trim()+"]");

4.字符串修改

由于String的不可变性,为了⽅便字符串的修改,Java中⼜提供StringBuilder和StringBuffer类。

  • StringBuffer 采用同步处理,属于线程安全操作;
  • StringBuilder 未采用同步处理,属于线程不安全操作
java 复制代码
public static void main(String[] args) {
        StringBuffer stringBuffer = new StringBuffer("hello");
        stringBuffer.append("world");
        System.out.println(stringBuffer);

        StringBuilder stringBuilder = new StringBuilder("hello");
        stringBuilder.append("world");
        System.out.println(stringBuilder);
       
    }
}

三、总结

String 类作为 Java 中最基础也最常用的类,其底层原理和使用技巧值得每个开发者深入掌握。总结本文核心要点:

  1. 理解不可变性:字符串创建后无法修改,每次修改都会产生新对象
  2. 掌握创建方式:优先使用字面量创建,避免不必要的 new String()
  3. 优化拼接性能:动态拼接使用 StringBuilder ,并指定初始容量
  4. 合理使用常量池:通过 intern() 方法复用字符串,减少内存占用
  5. 注意空指针安全:始终先判断 null 再调用字符串方法

掌握这些知识,不仅能避免常见的字符串操作误区,还能写出更高效、更优雅的 Java 代码。在实际开发中,应根据具体场景选择合适的字符串操作方式,平衡可读性和性能需求。

相关推荐
Scott9999HH7 小时前
【IIoT流量实战】蒸汽管道阀门全关却仍有流量?用 Python 实现涡街信号 FFT 频谱分析与温压全补偿积算网关,深度拆解靠谱的涡街流量计厂家硬核技术标准
开发语言·python
腻害兔7 小时前
【若依项目-产品经理视角】深度拆解 RuoYi-Vue-Pro 商城模块:从商品管理到交易引擎,50 张表撑起一整套电商系统
java·大数据·vue.js·产品经理·ai编程
码智社8 小时前
AES加密原理详解及Java实现加解密实战
java·开发语言
AI云海8 小时前
python 列表、元组、集合和字典
开发语言·python
萧瑟余晖9 小时前
JDK 26 新特性详解
java·开发语言
马优晨10 小时前
Freemarker 完整讲解(后端 Java 模板引擎)
java·开发语言·freemarker·freemarker 完整讲解·freemarker模板引擎
人邮异步社区11 小时前
怎么把C语言学到精通?
c语言·开发语言
心平气和量大福大12 小时前
C#-WPF-控件-TextBox 数据绑定
开发语言·c#·wpf
ttwuai12 小时前
Cursor 生成 CRUD 后,Go 后台接口别只测 200:JWT、RBAC 和 tenant_id 怎么验
开发语言·后端·golang
维天说12 小时前
CLI-Switch 2026年3月版历史设计:Hook、TTY 隔离与 JSON 状态
java·服务器·json