前言
写完上篇后,感染了红眼病做啥都没兴趣了,和牙疼一样,不致命但很要命,得亏我好得快☹️
在上篇里我只是提了提说用变量名去拼接字符串会创建新对象,但并没有解释原理,这里正好可以补充补充
StringBuilder
当我们需要频繁地对字符串进行修改时,使用 Java 的 StringBuilder 类可以提供更高效的处理方式。StringBuilder 是一个可变的字符串序列,它允许对字符串进行动态修改,而无需创建新的字符串对象。
对之前的变量名拼接的代码进行调试
调试代码:
String str1="你好";//直接赋值
String str2=new String("你好");//常用构造器赋值
String str3=str1+str2;
第一层进入StringBuilder构造器,创建了一个StringBuilder对象,
public StringBuilder() {
super(16);
}
/**
* Constructs a string builder with no characters in it and an
* initial capacity specified by the {@code capacity} argument.
*
* @param capacity the initial capacity.
* @throws NegativeArraySizeException if the {@code capacity}
* argument is less than {@code 0}.
*/
第二层:进入append方法,这里会执行两次,一次是str1,一次是str2
@Override
public StringBuilder append(String str) {
super.append(str);
return this;
}
第三层:注意这里是StringBuilder的toString方法,方法里有new String语句,说明新创建了一个String对象,然后又将这个String对象给了str3去引用
@Override
public String toString() {
// Create a copy, don't share the array
return new String(value, 0, count);
}
我们再来看看StringBuilder类的属性,这里我直接跳到了它的父类AbstractStringBuilder,StringBuilder非继承的属性不是很重要
abstract class AbstractStringBuilder implements Appendable, CharSequence {
/**
* The value is used for character storage.
*/
char[] value;
/**
* The count is the number of characters used.
*/
int count;
...
容量自动扩展:StringBuilder 内部维护了一个字符数组(char[]),用于存储字符串内容。它的容量会根据需要自动进行扩展,可以容纳任意长度的字符串。
在其他地方我们经常能看到说StringBuilder能减少创建中间对象从而而提高性能,可是我开始还有点疑问,这里不是也创建了对象吗?后来才发现它是为了迎合我的String类的str3,它原本是StringBuilder类的,转换成String当然要新建对象喽。
提高性能的案例:
String类叠加字符串hello一万次
public static void main(String[] args) {
int iterations = 10000;
String result = "";
long startTime = System.nanoTime();
for (int i = 0; i < iterations; i++) {
result += "Hello";
}
long endTime = System.nanoTime();
long duration = endTime - startTime;
System.out.println("Result length: " + result.length());
System.out.println("Duration: " + duration + " nanoseconds");
}
运行结果:单位是纳秒
StringBuilder类叠加字符串hello一万次
public static void main(String[] args) {
int iterations = 10000;
StringBuilder result = new StringBuilder();
long startTime = System.nanoTime();
for (int i = 0; i < iterations; i++) {
result.append("Hello");
}
long endTime = System.nanoTime();
long duration = endTime - startTime;
System.out.println("Result length: " + result.length());
System.out.println("Duration: " + duration + " nanoseconds");
}
运行结果:
次数越多,性能提升越明显
StringBuffer
当涉及到在多线程环境下进行字符串操作时,可以使用 Java 中的 StringBuffer 类。StringBuffer 是一个线程安全的可变字符串类,它与 StringBuilder 类似,但具有同步方法,可以保证线程安全性。
通俗一点来讲,就是StringBuilder的性能减少一点,把线程安全给加上去
看一下它的类属性,它也是AbstractStringBuilder的子类,和 StringBuilder同一个父亲生的,所以很相似
public final class StringBuffer
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence
{
/**
* A cache of the last value returned by toString. Cleared
* whenever the StringBuffer is modified.
*/
private transient char[] toStringCache;
/** use serialVersionUID from JDK 1.0.2 for interoperability */
static final long serialVersionUID = 3388685877147921107L;
...
}
主要看它的类方法
和StringBuilder的多了个synchronized修饰,也就是提高线程安全的修饰符。
性能考虑:由于 StringBuffer 是线程安全的,它在多线程环境下的性能可能会受到一定的影响。因为需要进行同步操作来确保线程安全,这可能会引入一些性能开销。如果不需要考虑线程安全,可以使用 StringBuilder 类,它是一个非线程安全的可变字符串类,性能上可能更好。
String常用构造方法
String(): 无参构造方法,创建一个空字符串对象。
String str = new String();
String(byte[] bytes): 使用指定的字节数组创建字符串对象。根据默认字符集将字节数组转换为字符串。
byte[] byteArray = {97, 98, 99, 100}; // ASCII码对应的字节数组 [a, b, c, d]
String str = new String(byteArray);
String(byte[] bytes, Charset charset):使用指定的字节数组和字符集创建字符串对象。
byte[] byteArray = {97, 98, 99, 100}; // ASCII码对应的字节数组 [a, b, c, d]
Charset charset = Charset.forName("UTF-8");
String str = new String(byteArray, charset);
String(char[] value): 使用指定的字符数组创建字符串对象。
char[] charArray = {'H', 'e', 'l', 'l', 'o'};
String str = new String(charArray);
String(char[] value, int offset, int count): 使用指定的字符数组的子数组创建字符串对象。
char[] charArray = {'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd'};
String str = new String(charArray, 6, 5); // 从索引6开始取5个字符,得到 "World"
String(String original): 使用指定字符串创建一个新的字符串对象,字符串内容和原始字符串相同。
String original = "Hello";
String str = new String(original);
String(StringBuffer buffer): 使用指定的 StringBuffer 对象创建字符串对象。
StringBuffer buffer = new StringBuffer("Hello");
String str = new String(buffer);
String(StringBuilder builder): 使用指定的 StringBuilder 对象创建字符串对象。
StringBuilder builder = new StringBuilder("Hello");
String str = new String(builder);
String常用操作字符串方法
length(): 返回字符串的长度。
String str = "Hello";
int length = str.length(); // 返回 5
charAt(int index): 返回指定索引位置的字符。
String str = "Hello";
char ch = str.charAt(1); // 返回 'e'
substring(int beginIndex): 返回从指定索引开始到字符串末尾的子字符串。
String str = "Hello, World!";
String subStr = str.substring(7); // 返回 "World!"
substring(int beginIndex, int endIndex): 返回从指定的开始索引到结束索引的子字符串。
String str = "Hello, World!";
String subStr = str.substring(7, 12); // 返回 "World"
concat(String str): 将指定的字符串连接到原始字符串的末尾。
String str1 = "Hello";
String str2 = str1.concat(", World!"); // 返回 "Hello, World!"
replace(char oldChar, char newChar): 将字符串中的所有旧字符替换为新字符。
String str = "Hello";
String newStr = str.replace('e', 'a'); // 返回 "Hallo"
toLowerCase(): 将字符串中的所有字符转换为小写。
String str = "Hello";
String lowerCaseStr = str.toLowerCase(); // 返回 "hello"
toUpperCase(): 将字符串中的所有字符转换为大写。
String str = "Hello";
String upperCaseStr = str.toUpperCase(); // 返回 "HELLO"
trim(): 移除字符串两端的空格。
String str = " Hello ";
String trimmedStr = str.trim(); // 返回 "Hello"
split(String regex): 将字符串拆分为子字符串数组,根据指定的正则表达式作为分隔符。
String str = "Hello,World,Java";
String[] arr = str.split(","); // 返回包含三个元素的字符串数组: ["Hello", "World"]
还有很多其他方法遇到查就是了