【Java】Java中的String类详解

前言

在 Java 中,String 类是一个非常重要的内置类,它用于处理字符串数据。字符串是不可变的(immutable),这意味着一旦创建,字符串的内容不能被修改。这篇博客将详细介绍 String 类的特性、常用方法及一些性能注意事项。


1.String 类的常用方法

1.1 创建字符串

// 方式1 直接赋值
String str = "Hello, World!";
// 方式2 使用new关键字
String str2 = new String("Hello, World!");
// 方式3 字符数组转String
char[] array = {'a', 'b', 'c'};
String str3 = new String(array);

例1:赋值str1,str2指向str1

String str1 = "Hello";
String str2 = str1;

System.out.println(str1);  // Hello
System.out.println(str2);  // Hello

内存结构如图

在这里插入图片描述


例2:修改str1为新的值

String str1 = "Hello";
String str2 = str1;
str1 = "World"
System.out.println(str1);  // World
System.out.println(str2);  // Hello

内存结构如图
在这里插入图片描述


1.2 字符串比较

例1:str1,str2相同值,str3不同值

String str1 = "Hello";
String str2 = "Hello";
String str3 = "world";
System.out.println(str1 == str2);  // true 因为 str1 和 str2 引用的是同一个对象
System.out.println(str1 == str3);  // false 因为 str1 和 str3 的内容不同,引用的对象也不同

内存结构如图
在这里插入图片描述


例2:str1,str2使用new关键字创建相同值

String str1 = new String("Hello");
String str2 = new String("Hello");
System.out.println(str1 == str2); // false
// str1 和 str2 是通过 new 关键字创建的两个不同的 String 对象,因此它们的引用不同,比较结果是 false

// 如果需要比较内容是否相同,应该使用 equals 方法
System.out.println(str1.equals(str2)); // true

1.3 获取长度

String str = "Hello, World!";
int length = str.length(); // 返回字符串的长度

1.4 字符串比较

String str = "Hello, World!";
boolean isEqual = str.equals("Hello, World!"); // 比较内容
boolean isEqualIgnoreCase = str.equalsIgnoreCase("hello, world!"); // 忽略大小写比较

1.5 子字符串

String str = "Hello, World!";
String subStr = str.substring(7, 12); // 返回 "World"

1.6 替换

String str = "Hello, World!";
String replaced = str.replace("World", "Java"); // 返回 "Hello, Java!"

1.7 字符串查找

String str = "Hello, World!";
int index = str.indexOf("World"); // 返回 7
boolean contains = str.contains("Hello"); // 返回 true

2. 字符串常量池

2.1 什么是字符串常量池

String类的设计使用了共享设计模式,在JVM底层实际上会自动维护一个对象池(字符串常量池),字符串常量池是 Java 中一个特殊的内存区域,用于存储字符串字面量和一些常量字符串,以优化内存使用和提高性能。

  • 如果采用了直接赋值的模式进行String类的对象实例化操作,那么该实例化对象(字符串内容)将自动保存到这个对象池之中
  • 如果下次继续使用直接赋值的模式声明String类对象,此时对象池之中如若有指定内容,将直接进行引用
  • 如若没有,则开辟新的字符串对象而后将其保存在对象池之中以供下次使用

在这里插入图片描述


2.2 直接使用new String()方法

  • 使用 new 关键字时,如果常量池中已经存在 “ab”,不会再常量池中重复创建,会在堆内存中创建一个新的 String 对象
  • 如果常量池中不存在 “ab”,则在常量池创建,且在堆内存中创建一个新的 String 对象
    在这里插入图片描述

2.3 intern方法

intern() 方法是 Java 中 String 类的一个方法,用于获取字符串的规范表示。在调用 intern() 方法时,如果常量池中已经存在一个与该字符串内容相同的字符串,则返回该字符串的引用;如果不存在,则将该字符串添加到常量池中,并返回这个新字符串的引用。所以我们可以使用 String 的 intern 方法来手动把 String 对象加入到字符串常量池中。

优点

  • 内存优化:使用 intern() 可以减少内存使用,因为相同内容的字符串只在常量池中存在一份。
  • 性能:在字符串比较时,如果使用 == 比较常量池中的字符串会比使用 equals 方法更快,因为它只比较对象引用。
String str1 = new String("Hello");
String str2 = str1.intern(); // 将 str1 的内容添加到常量池中

String str3 = "Hello"; // 直接从常量池中获取

System.out.println(str2 == str3); // 输出 true,str2 和 str3 引用的是常量池中的同一个对象

3.String的不可变

3.1 不可变

String对象一旦创建,其内容不能被更改。如果你对字符串进行修改,比如连接、替换等操作,实际上会生成一个新的字符串对象。例如给一个已有字符串"abcd"第二次赋值成"abcedl",不是在原内存地址上修改数据,而是重新指向一个新对象,新地址。

在这里插入图片描述


3.2 String为什么不可变

点开String源码发现

在这里插入图片描述

String 类中使用 final 关键字修饰字符数组value来保存字符串,但是如果能直接对数组元素进行操作就可以修改数组中的内容。

final int[] value={1,2,3};
value[2]=100;  //这时候数组里已经是{1,2,100}

String 真正不可变有下面几点原因

  • 保存字符串的数组被 final 修饰且为私有(private)的,并且String 类没有提供/暴露修改这个字符串的方法。
  • String 类被 final 修饰导致其不能被继承,进而避免了子类破坏 String 不可变。

3.3 String不可变的好处

  • 安全性:由于字符串对象一旦创建后不可修改,避免了意外修改内容的风险。这在多线程环境中特别重要,因为多个线程可以安全地共享同一个字符串对象,而不必担心数据被修改。

  • 内存效率:不可变字符串可以被共享。比如,当你在常量池中创建一个字符串字面量时,后续对同一内容的引用会直接指向常量池中的对象,而不是创建新的对象。这减少了内存消耗。

  • 哈希码缓存:由于字符串不可变,它们的哈希码只计算一次,并可以被缓存。这使得字符串在作为哈希表的键时更加高效,因为不需要在每次查找时重新计算哈希值。


4.性能注意事项

4.1 StringBuilder 和 StringBuffer

由于 String 的不可变性,在频繁修改字符串的情况下,使用 String 可能会导致性能问题。此时,可以考虑使用 StringBuilder(非线程安全)或 StringBuffer(线程安全)来进行字符串操作。

StringBuilder sb = new StringBuilder("Hello");
sb.append(", World!"); // 更高效的字符串拼接
String result = sb.toString(); // 转换为 String

4.2 不要在循环中使用 + 拼接

在循环中使用 + 进行字符串拼接可能导致性能问题,最好使用 StringBuilder。

StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1000; i++) {
    sb.append("Hello ");
}
String result = sb.toString();

对于三者使用的总结:

  • 操作少量的数据: 适用 String
  • 单线程操作字符串缓冲区下操作大量数据: 适用 StringBuilder
  • 多线程操作字符串缓冲区下操作大量数据: 适用 StringBuffer
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值