【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 代码。在实际开发中,应根据具体场景选择合适的字符串操作方式,平衡可读性和性能需求。

相关推荐
艾莉丝努力练剑9 分钟前
【C/C++】形参、实参相关内容整理
c语言·开发语言·c++·学习
用户849137175471631 分钟前
JDK 17 实战系列(第2期):核心语言特性深度解析
java·后端
自由的疯34 分钟前
Java 8 新特性之 Lambda 表达式
java·后端·架构
自由的疯35 分钟前
Java 17 新特性之 Text Blocks(文本块)
java·后端·架构
用户849137175471636 分钟前
JustAuth实战系列(第2期):架构设计精髓 - 分层架构与设计原则
java·后端·架构
大熊学员40 分钟前
JavaScript 基础语法
开发语言·javascript·ecmascript
乌萨奇也要立志学C++44 分钟前
【C++详解】STL-set和map的介绍和使用样例、pair类型介绍、序列式容器和关联式容器
开发语言·c++·stl
BUG?不,是彩蛋!1 小时前
从零开始用 Eclipse 写第一个 Java 程序:HelloWorld 全流程 + 避坑指南
java·eclipse·idea
Lemon程序馆1 小时前
Arthas | Java 线上问题快速定位神器
java·运维·后端
yu2024111 小时前
异世界历险之数据结构世界(非递归快排,归并排序(递归,非递归))
java·数据结构·排序算法