public class StringTest1 {
public static void main(String[] args) {
//通过构造放方法,将一个字符串封装为一个String对象
String str1 = new String("hello world");
//通过直接赋值创建一个String对象
String str2 = "hello world";
//声明一个char[]数组
char[] ch1 = {'h','e','l','l','o',' ','w','o','r','l,'d
//将char[]数组封装为一个String对象
Stirng str3 = new String(ch1);
//指定开始结束索引,将char[]数组封装为一个String对象
String str4 = new String(ch1,2,4);
}
}
public final class String
implements java.io.Serializable, Comparable, CharSequence {
//.....
}
通过查看String类,可以看出String类实现了三个接口
实现Serializable接口:实现这个接口意味着String对象可以被序列化(序列化可以实现对象的保存与网络传输)
实现Comparable接口:这个接口用于定义一个可比较大小的类型,实现这个接口意味着我们可以重写其中的compareTo()方法来定义两个对象之间的顺序关系。
实现CharSequence接口:StringBulider作为实现CharSequence接口的三大类之一,实现CharSequence接口StringBuilder类可以被视为一个可读、可修改并且长度可变的字符序列。这意味着我们可以通过StringBuilder对象进行字符串拼接、插入字符、替换部分文本或删除特定区间内的内容等操作。因此,在使用StringBuilder时,我们可以像操作普通字符串一样方便地对其进行增删改查操作,并且避免频繁创建新对象带来的开销。
值得注意的是:
String类中value字段用于储存字符串的实际内容,可以看见,value是一个char[]数组,确定长度后不可改变,并且用final修饰,这就表示value的引用地址是不可变的(但里面的元素是可变的),再加上final修饰的String类不能被继承,这就决定value数组的元素值已经无法从外部修改了,这决定了String对象是不可变的。
方法 | 说明 |
---|---|
length(); | 返回字符串长度 |
charAt(int index); | 返回指定索引的字符 |
concat(String str); | 字符串的拼接 |
equalsIgnoreCase(String anotherString); | 比较字符串是否相等,忽略大小写 |
indexOf(int ch)和indexOf(String str, int fromIndex); | 查找指定字符(或子串)第一次出现的位置 |
substring(int beginIndex)和substring(int beginIndex, int endIndex); | 获取指定位置的字串 |
toLowerCase();和toUpperCase(); | 转换大小写 |
trim(); | 去除字符串首尾空格 |
replace(char oldChar, char newChar); | 字符替换 |
split(String regex); | 以特定正则表达式分隔符将当前字符创拆分为数组 |
startsWith(String prefix)和endsWith(String suffix) | 判断给定前缀或者给定后缀是否匹配 |
equals(Object anObject); | 两个字符串的比较 |
compareTo(String anotherString); | 比较两个字符串是否相等,并返回结果 |
public class StringTest1 {
public static void main(String[] args) {
//创建一个空的可变字符串对象
StringBuilder strb1 = new StringBuilder();
System.out.println(strb1);
//创建一个可变字符串对象
StringBuilder strb2 = new StringBuilder("hello world");
System.out.println(strb2);
}
}
public final class StringBuilder
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence
{
//..........
}
通过查看java中的StringBuilder类,可以看出它继承了AbstractStringBuilder类,实现了Serializable接口与CharSequence接口
继承了AbstractStringBuilder类:一个抽象类,提供了构建字符串所需要的基本功能和方法
实现了Serializable接口:实现这个接口意味着StringBuilder对象可以被序列化(序列化可以实现对象的保存与网络传输)
实现CharSequence接口:StringBulider作为实现CharSequence接口的三大类之一,实现CharSequence接口StringBuilder类可以被视为一个可读、可修改并且长度可变的字符序列。这意味着我们可以通过StringBuilder对象进行字符串拼接、插入字符、替换部分文本或删除特定区间内的内容等操作。因此,在使用StringBuilder时,我们可以像操作普通字符串一样方便地对其进行增删改查操作,并且避免频繁创建新对象带来的开销。
方法 | 功能 |
---|---|
append(任意类型参数); | 将指定的类型参数添加到字符串构建器的末尾 |
insert(); | 从指定位置插入指定类型的数据 |
delete();/deleteCharAt(); | 从字符串构建器中删除字符或字符序列 |
indexOf(设置参数); | 返回指定字符或子串的索引值 |
substring(); | 返回范围内子串作为新的字符串对象 |
reverse(); | 将字符串反转 |
StringBulider中的append()添加方法:
strb2.append(任意类型参数);
在StringBuilder类中有各种类型的参数的添加方法,实现了StringBuilder的扩展功能
底层实现代码(节选):
StringBuilder中的insert()插入方法:
//传递两个参数1、offset(插入位置索引),2、xxx(任意类型数据)
str1.insert(int offset,Xxx xxx);
insert方法实现了StringBuilder对象指定位置插入数据功能
底层实现代码(节选):
StringBulider中的delete()/deleteCharAt()删除方法:
//传递两个参数1、删除开始位置索引2、删除结束位置索引
strb1.delete(int start,int end);
//传递一个参数1、删除指定位置索引
strb1.delete(int offset);
delete()/deleteCharAt()方法实现了StringBuilder对象内容的删除操作
StringBulider中的indexOf()查找方法:
//返回strb1中第一次出现指定字符串的索引位置
strb1.indexOf(String str);
//从给定索引位置fromIndex开始,返回搜索过程中第一次出现指定字符或字符串的索引位置
strb1.indexOf(String str,int fromIndex);
//返回strb1中第一次出现指定字符的索引位置
strb1.indexOf(char ch)
//从给定索引位置fromIndex开始,返回搜索过程中第一次出现指定字符或字符的索引位置
strb1.indexOf(char ch,int fromIndex)
底层实现代码:
StringBulider中的substring();返回指定字符串方法:
//返回指定索引开始后的字符串
strb1.substring(int start);
//返回指定开始结束索引的字符串
strb1.substring(int start,int end);
底层实现代码:
StringBulider中的reverse();反转字符串方法:
//将指定字符串内容反转
strb1.reverse();
方法 | 功能 |
---|---|
String s=strb1.toString(); | 通过toString()方法实现StringBuilder对象转化为String对象 |
StringBuilder strb3=new StringBuilder(s); | 通过构造方法实现String对象转换为StringBuilder对象 |
public class StringTest1 {
public static void main(String[] args) {
//创建一个空的StringBuffer对象
StringBuffer strbf1 = new StringBuffer();
System.out.println(strbf1);
//创建一个StringBuffer对象
StringBuffer strbf2 = new StringBuffer("hello world");
System.out.println(strbf2);
}
}
public final class StringBuffer
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence{
//......
}
通过查看java中的StringBuffer类,可以看出它继承了AbstractStringBuilder类,实现了Serializable接口与CharSequence接口
继承了AbstractStringBuilder类:一个抽象类,提供了构建字符串所需要的基本功能和方法
实现了Serializable接口:实现这个接口意味着StringBuilder对象可以被序列化(序列化可以实现对象的保存与网络传输)
实现CharSequence接口:StringBulider作为实现CharSequence接口的三大类之一,实现CharSequence接口StringBuilder类可以被视为一个可读、可修改并且长度可变的字符序列。这意味着我们可以通过StringBuilder对象进行字符串拼接、插入字符、替换部分文本或删除特定区间内的内容等操作。因此,在使用StringBuilder时,我们可以像操作普通字符串一样方便地对其进行增删改查操作,并且避免频繁创建新对象带来的开销。
方法 | 说明 |
---|---|
append(String str); | 将指定的字符串追加到字符串缓冲区末尾。 |
insert(int offset, String str); | 在指定偏移量位置插入指定字符串。 |
delete(int start, int end); | 删除从start索引开始到end索引结束之间的字符序列 |
replace(int start, int end, String str); | 指定字符串替换从索引开始到索引结束之间的字符序列。 |
reverse(); | 将整个字符序列反转 |
capacity(); | 返回当前容量(即总空间) |
length(); | 返回当前长度(即已使用空间) |
方法 | 说明 |
---|---|
StringBuffer strbf1=new StringBuffer(str); | 通过构造方法实现对String对象转换为StringBuffe对象 |
strbf1.toString | 通过toString方法实现StringBuffe对象转换为String对象 |
String类是不可变的,意味着一旦创建了一个String对象,就不能更改其内容。而StringBuffer和StringBuilder类是可变的,可以通过方法进行字符串的修改
String类中value字段用于储存字符串的实际内容,可以看见,value是一个char[]数组,确定长度后不可改变,并且用final修饰,这就表示value的引用地址是不可变的(但里面的元素是可变的),再加上final修饰的String类不能被继承,这就决定value数组的元素值已经无法从外部修改了,这决定了String对象是不可变的。
存储机制:
以直接赋值的方式创建一个字符串,此时字符串存储在方法区的常量池中
String str1="hello";
String str2="hello";
String str1="hello";
String str2="hello";
str2="hi";
在修改字符串的值时,无论是在原有字符串内容上做删减,还是赋值一个新的字符串,都需要分配一个新的内存地址进行赋值
以构造方法的方式创建一个字符串,此时字符串存储在方法区的常量池中,但对象储存在堆中
String str1="hello";
String str2=new String("hello");
String str1=new String("hello");
String str2=new String("hello");
String str1=new String("hello");
String str2=new String("hi");
StringBuffer与StringBuilder对象他们在创建时,都为其构造了一个16个字符的缓冲区,可对其进行后续的修改
StringBuilder的构造方法
修改的过程
以append(char[] str, int offset, int len)底层代码为例
提供添加的字符或字符序列,以索引开始于结束位置进行插入,其中当传入字符或字符序列为空,则赋null,在后续的判断中,若开始索引小于0或大于结束索引,结束索引大于传入字符长度时就抛出异常,通过for循环添加传入的非空字段(若传入字符串为空,且开始结束索引为0时,for循环执行0次)
StirngBuffer是线程安全的, 因此适合在多线程环境下使用;而StringBuilder不具备线程安全性,适合在单线程环境下使用。而String没有任何同步机制,并且也不保证线程安全。
1、输出错误(不满足预期)
我们创建10个线程,每个线程进行对StringBuilder对象进行1000次添加字符‘a’的操作
public static void main(String[] args) throws InterruptedExcept
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < 10; i++) {
new Thread(new Runnable() {
@Override
public void run() {
for (int j = 0; j < 1000; j++) {
stringBuilder.append("a");
}
}
}).start();
}
Thread.sleep(100);
System.out.println(stringBuilder.length());
}
但我们查看底层代码时发现:
代码中的count+=len可能会导致出错,举例:当两个线程同时执行到count+=len上一行,他们此时提取的count值都为a,此时第一个线程执行count+=len,此时count值变为a+1,但第二个线程继续执行count+=len时,因为进行了两次添加,理应执行完后count值为a+2。但第二个线程执行时提取的count为a,执行后count值依旧为a+1。会影响到后续线程的添加操作,这就导致总添加的字符数量少了一个,多线程下,会导致更严重的后果。
2、添加字符是索引值出错
在创建StringBuilder时,系统为其构造了一个16个字符的缓冲区,对象的扩展时,就是先向这个缓冲区添加,当传入扩展的字符序列长度大于剩余容量,就会new一个两倍原StringBuilder长度char[]数组,将原StringBuilder对象内容复制到新的char[]数组中。举例:当剩余容量为a,此时两个线程都需插入一个长度为a的字符序列,且他们都执行到提取剩余容量,取得的容量刚好可以符合他们的要求,此时他们继续往下执行,当第一个线程插入字符序列后,容量没有了,但是第二个线程提取到的信息是过时的,它依旧认为还有剩余容量,在进行添加操作时,就会导致ArrayIndexOutOfBoundsException异常
对于StringBuffer来说, 它的方法定义为 synchronized ,即采用了互斥锁机制(lock mechanism),能够确保同一时间只有一个线程可以访问该对象进行修改,从而保证了操作串行化(serialized access),任意时刻只能有一个线程执行append、delete等方法。
由于String类每次对字符串进行修改都会创建一个新的对象,在频繁操作大量数据时会产生大量无用对象,导致内存开销较大;相比之下,StingBuffer和StringBuilder直接对原始字符序列进行修改,减少了内存开销。
由于String类每次对字符串进行修改都会创建一个新的对象,在频繁操作大量数据时会产生大量无用对象,导致内存开销较大;相比之下,StingBuffer和StringBuilder直接对原始字符序列进行修改,减少了内存开销。