【从0开始学习Java | 第11篇】String、StringBuilder与StringBuffer
文章目录
- 前言
- 🥝基础定义与核心差异
- 🐦🔥底层实现与内存表现
- String字符串详解💡
- 创建String字符串
- String 类是不可改变的解析
- String类的常用方法🧾
- StringBuilder和StringBuffer详解
- StringBuffer常用方法🧾
- 示例
- 🤔三种字符类型总结
前言
在 Java 中,字符串处理是日常开发中不可或缺的部分。而String、StringBuilder和StringBuffer这三个类,更是频繁出现在各种代码场景中。它们看似相似,却在底层实现、性能表现和适用场景上有着天壤之别,让我们通过这篇文章深入了解。
🥝基础定义与核心差异
要理解这三个类的区别,首先得从它们的定义和最核心的特性说起:
- String类是 Java 中最基本的字符串表示形式,它被声明为final,这意味着它是不可变的。所谓不可变,就是指一旦String对象被创建,它所包含的字符序列就不能再被修改。每次对String进行拼接、截取等操作时,都会生成一个新的String对象,而原来的对象则会被丢弃,等待垃圾回收。
- StringBuilder和StringBuffer则是为了解决String不可变带来的性能问题而出现的。它们都继承自AbstractStringBuilder类,内部都维护着一个可变的字符数组,可以对字符序列进行动态修改。两者最大的区别在于线程安全性:StringBuffer中的大部分方法都被 synchronized关键字修饰,因此它是线程安全的;而StringBuilder没有进行线程同步处理,所以它是非线程安全的。
🐦🔥底层实现与内存表现
深入了解它们的底层实现,能让我们更清晰地认识到为什么会存在这些差异:
- String类内部使用一个char数组(JDK9 及以上改为byte数组,根据编码方式不同节省空间)来存储字符,并且这个数组被声明为final。这就从根本上保证了String对象的不可变性。当我们执行str = str + "world"这样的操作时,实际上是创建了一个新的String对象,将原来的str和"world"的字符序列复制到新的数组中,原来的str对象则失去了引用。
- StringBuilder和StringBuffer的底层同样是一个字符数组,但这个数组没有被声明为final。它们通append、insert等方法直接对数组进行修改,当数组的容量不足时,会进行扩容操作 —— 创建一个更大的数组,将原来的字符序列复制过去,然后继续使用新的数组。这种方式避免了频繁创建新对象,大大提高了字符串操作的效率。不过,StringBuffer由于需要保证线程安全,在每个方法上都添加了同步锁,这会导致一定的性能开销。在单线程环境下,StringBuilder的性能要优于StringBuffer。
String字符串详解💡
创建String字符串
创建字符串最简单的方式如下:
String str = “Runoob”;
在代码中遇到字符串常量时,这里的值是 “Runoob”,编译器会使用该值创建一个 String 对象。
和其它对象一样,可以使用关键字和构造方法来创建 String 对象,用构造函数创建字符串:
String str2=new String(“Runoob”);
String 创建的字符串存储在公共池中,而 new 创建的字符串对象在堆上:
String s1 = "Runoob"; // String 直接创建
String s2 = "Runoob"; // String 直接创建
String s3 = s1; // 相同引用
String s4 = new String("Runoob"); // String 对象创建
String s5 = new String("Runoob"); // String 对象创建
该代码中的各个String在公共池和堆中的存在方式:
String 类是不可改变的解析
例如:
String s = "Google";
System.out.println("s = " + s);s = "Runoob";
System.out.println("s = " + s);
输出结果为:
Google
Runoob
从结果上看是改变了,但为什么说 String 对象是不可变的呢?
原因在于实例中的 s 只是一个 String 对象的引用,并不是对象本身,当执行 s = “Runoob”; 创建了一个新的对象 “Runoob”,而原来的 “Google” 还存在于内存中。
String类的常用方法🧾
方法 | 说明 |
---|---|
public int length() | 获取字符串的长度 |
public boolean equals(Object anObject) | 比较两个字符串是否相同 |
public boolean equalsIgnoreCase(String anotherString) | 忽略大小比较两个字符串是否相同 |
public String toLowerCase() | 转换为小写 |
public String toUpperCase() | 转换为大写 |
public int indexOf(int ch) | 获取指定字符在字符串中第一次出现的下标 |
public int lastIndexOf(int ch) | 获取指定字符在字符串中最后一次出现的下标 |
public int indexOf(String str) | 获取指定字符串在字符串中第一次出现的下标 |
public int lastIndexOf(String str) | 获取指定字符串在字符串中最后一次出现的下标 |
public char charAt(int index) | 获取字符串中的指定下标的字符 |
public String substring(int beginIndex) | 从指定开始位置截取字符串,直到字符串的末尾 |
public String substring(int beginIndex, int endIndex) | 从指定开始位置到指定结束位置截取字符串 |
public String concat(String str) | 将字符串追加到末尾 |
StringBuilder和StringBuffer详解
当对字符串进行修改的时候,需要使用 StringBuffer 和 StringBuilder 类。
和 String 类不同的是,StringBuffer 和 StringBuilder 类的对象能够被多次的修改,并且不产生新的未使用对象。
在使用 StringBuffer 类时,每次都会对 StringBuffer 对象本身进行操作,而不是生成新的对象,所以如果需要对字符串进行修改推荐使用 StringBuffer。
StringBuffer常用方法🧾
public StringBuffer append(String s) | 将一个字符串添加到StringBuilder存储区 |
public StringBuilder append(StringBuffer sb) | 将StringBuffer存储的内容添加StringBuilder存储区 |
public StringBuffer reverse() | 将此字符序列用其反转形式取代 |
public delete(int start, int end) | 移除此序列的子字符串中的字符 |
public insert(int offset, int i) | 将 int 参数的字符串表示形式插入此序列中 |
insert(int offset, String str) | 将 str 参数的字符串插入此序列中。 |
replace(int start, int end, String str) | 使用给定 String 中的字符替换此序列的子字符串中的字符 |
示例
public class Test{public static void main(String[] args){StringBuilder sb = new StringBuilder(10);sb.append("Runoob..");System.out.println(sb); sb.append("!");System.out.println(sb); sb.insert(8, "Java");System.out.println(sb); sb.delete(5,8);System.out.println(sb); }
}
🤔三种字符类型总结
String 、 StringBuilder 和 StringBuffer 都是用来处理字符串的。在处理少量字符串的时候,它们之间的处理效率几乎没有任何区别。
但在处理大量字符串的时候,由于 String 类的对象不可再更改,因此在处理字符串时会产生新的对象,就算调用 String 的 concat 方法,那也是把字符串拼接起来并重新创建一个对象,把拼接后的 String 的值赋给新创建的对象,因此相比较于 StringBuffer,String 一旦发生长度变化,是非常耗费内存的,导致效率低下。
而 StringBuilder和 StringBuffer 使用的是对字符串的字符数组内容进行拷贝,不会产生新的对象,因此效率较高,而StringBuffer 为了保证在多线程情况下字符数组中内容的正确使用,在每一个成员方法上面加了锁,有锁就会增加消耗,因此 StringBuffer 在处理效率上要略低于 StringBuilder 。
如果我的内容对你有帮助,请 点赞 , 评论 , 收藏 。创作不易,大家的支持就是我坚持下去的动力!