文章目录
String,StringBuilder和StringBuffer
一,String
String类的特性
-
public final class String implements java.io.Serializable, Comparable<String>, CharSequence { private final char value[];
-
String是被final修饰的类,不能被继承。
-
String实现了Serializable和Comparable接口,表示String支持序列化和可以比较大小。
-
String底层是通过char类型的数据实现的,并且被final修饰,所以字符串的值被创建之后就不可以被修改,具有不可变性。
String的实例化方式
-
通过字面量方式实例化
-
String str = "abc";
-
-
new+构造器的方法实例化
-
String str=new String("abc");
-
-
区别
- 通过字面量方式为字符串赋值时,此时字符串存储在堆方法区的字符串常量池中;
- new+构造器方式实例化字符串时,字符串对象存储在堆中,但是字符串的值仍然存储在方法区的常量池中。
为什么设计String不可变?
- 因为String使用的过多,如果设计为可变的话,在多线程使用String时,字符串的值可能会因为多线程原因而改变,会引发线程安全问题,所以设计为不可变的。
String真的不可变吗?
-
其实也是可以变的,使用String.class.getDeclaredField(“value”)可以读取或修改改字段的Field对象。
-
String.class.getDeclaredField(“value”)是一个反射的语句,用于获取String类中的value字段,该字段存储了字符串的字符数组。
在java中,反射是一种强大的机制,它允许程序在运行时获取并操作类的信息,包括类的字段,方法,构造函数等。通过反射,可以在程序运行时获取类的私有字段或方法,并进行修改或调用。
需要注意的是,由于字符串对象是不可变的,因此不建议通过反射的方式修改字符串对象中的字符数组。这样可能会导致字符串对象的不一致性,从而引起程序错误。
二,StringBuffer和StringBuilder
它们的底层也是用一个数组来存储字符串的值,并且数组默认长度为16,即一个空的StringBuffer对象长度为16。但是当我们调用有参构造函数创建一个StringBuffer对象时,数组长度就不再时16了,而是根据当前对象的值来决定数组的长度,数组的长度为”当前对象的值的长+16“.所以一个StringBuffer创建完成后,又16个字符的空间可以对其值进行修改。如果修改的值范围超出16个字符,会先检查StringBuffer对象的原char数组的容量能不能装下新的字符串,如果装不下,则会对char数组进行扩容。
StringBuffer
- 当我们对字符串进行拼接操作时,每次拼接,都会构建一个新String对象,即耗时又浪费时间。而StrignBuffer就可以解决这个问题,是一个线程安全的可变字符序列。
- 当一个StringBuffer被创建后,通过StringBuffer提供的方法就可以改变这个字符串对象的字符序列,但都不会产生新的对象。
StringBuilder
- StringBuilder和StringBuffer基本相似,他们的原理和操作一样,两个类的构造器和方法也基本相同。
- 不同的时:StringBuilder没有实现线程安全功能,但是性能略高。
StringBuffer和StringBuilder常用的方法
-
StringBuffer append(xxx);//拼接字符串 StringBuffer delete(int start,int end);//删除指定范围的内容,左开右闭 StringBuffer replace(int start, int end, String str);//替换指定范围的内容 StringBuffer insert(int offset, xxx);//在指定位置插入指定的内容 StringBuffer reverse();//把当前字符序列逆转 public int indexOf(String str);//返回指定子字符串在当前字符串中第一次出现处的索引 public String substring(int start,int end);//返回指定范围的子字符串 public int length();//返回字符串的长度 public char charAt(int n );//获取指定索引处的字符 public void setCharAt(int n ,char ch);//设置指定索引处的字符
StringBufffer和StringBudiler的区别?
- 线程安全
- StringBuffer线程安全
- StringBuilder线程不安全
- 缓冲区
- StringBuffer每次获取toString都会直接使用缓存区的toStringCache值来构造一个字符串。
- StringBuilder则每次都需要复制一次字符数组,再构造一个字符串。
- 性能
- StringBuilder没有对方法加锁同步的,所以StringBuilder的性能大于StringBuffer.
- StringBuilder适合单线程场合,而StringBuffer适用于在多线程操作同一个StringBuffer的场景。
StringBuffer如何实现线程安全?
-
@Overridepublic synchronized int length() { return count;} @Overridepublic synchronized int capacity() { return value.length;}
-
我们可以看到,StringBuffer类中的方法都添加了synchounized关键字,也就是给这个方法添加了一个锁,用来保证线程安全。
StringBuffer是怎样进行扩容的?
- 扩容的逻辑就是创建一个新的char数组,将现有的容量扩大一倍在加2,如果还是不够大则直接等于需要的容量大小。扩容完成后,将数组的内容复制到新数组,最后将指针指向新的char数组。
- 如果原字符数组的长度小于64,那么新的容量是原来的2倍加2。
- 如果原字符数组的长度大于等于64,那么新的容量是原来的1.5倍。