String、StringBuffer、StringBuilder对比
String类型
- 源码:
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
private final char value[];
...
...
}
String具有不可变性,String底层使用的是final定义的char数组,不能被改变,String串被改变,会直接生成一个新的串。
- 测试代码:
@Test
public void test(){
//字面量的定义方式 ,直接指向常量池
String s1 = "abc";
String s2 = "abc";
System.out.println(s1 == s2);//比较s1和s2的地址值 true
String str1 = new String("javaEE");
String str2 = new String("javaEE");
//new String()方式会在堆空间中开辟新的空间,并都去指向常量池中的"javaEE"
System.out.println(str1 == str2); //比较的是地址值,false
s1 = "hello"; //s1指向常量池中的hello
System.out.println(s1 == s2);// false
System.out.println(s1);//hello
System.out.println(s2);//abc
System.out.println("********拼接字符串*********");
String s3 = "abc";
s3 += "def";
System.out.println(s3);//abcdef
System.out.println("*******替换数据**********");
String s4 = "abc";
String s5 = s4.replace('a', 'm');
String s6 = new String("abc");
String b = s6.replace("b", "123");
System.out.println(s4);//abc
System.out.println(s5);//mbc
System.out.println(s6);//abc
System.out.println(b);//a123c
/**
* 地址值发生变化,不会改变原有的串
* private final char value[]; String底层使用的是final定义的char数组,不能被改变,String串被改变,会直接生成一个新的串
*/
System.out.println(s4 == s5);//比较s1和s2的地址值 false
System.out.println(s6 == b);//false
System.out.println("============= 字符串拼接 ================");
String str3 = "java ";
String str4 = "spring boot";
String str5 = "java spring boot";
//使用常量
final String string1 = "java ";
String str6 = str3 + "spring boot";
String str7 = "java " + "spring boot";
String str8 = new String("java ") + str4;
String str9 = str3 + str4;
String str10 = new String("java ") + "spring boot";
//字符串拼接使用到变量时,会在堆空间中开辟新的空间,并指向常量池中的"java spring boot"
System.out.println(str5 == str6); //false
System.out.println(str5 == str8); //false
System.out.println(str5 == str9); //false
System.out.println(str5 == str10); //false
System.out.println(str5 == str7); //true
//string1是常量
String string2 = string1 + "spring boot";
System.out.println(str5 == string2); //true
//i为变量
int i = 1;
String st1 = "1";
String st2 = i + "";
System.out.println(st1 == st2); //false
}
/**
* String常用方法 前闭后开
*/
@Test
public void StringTest() {
String str = "xm_1234_56";
//返回第一次出现"_"的索引
int index = str.indexOf("_"); //2
int index2 = str.indexOf("_", 2);//包含起始索引在内
int index3 = str.indexOf("_", 4);
System.out.println("index2:" + index2 + "/index3:" + index3); //2 7
int index4 = str.lastIndexOf("_", 2); //从索引为2往前找,包含起始索引在内
System.out.println("lastIndexOf:" + index4); //2
String substring = str.substring(0, index); //xm 前闭后开
System.out.println(index + "/" + substring + "\n");
boolean b = str.startsWith("3", 5); //true 包含起始索引在内
boolean b1 = str.startsWith("3", 4); //false
System.out.println(b + "/" + b1);
boolean contains = str.contains("123");
System.out.println("contains: " + contains); //true
//字符与字符串
String string= "中国加油 88888";
char[] chars = string.toCharArray();
String s = new String(chars);
//字节与字符串 (编码解码不一致时,会出现中文乱码)
byte[] bytes = string.getBytes(Charset.defaultCharset());
String s1 = new String(bytes, Charset.defaultCharset());
String gbk = new String(bytes, Charset.forName("GBK"));
System.out.println("编码解码格式一致:" + s1 + "、不一致:" + gbk);
}
/**
* 7种基本数据类型
* 1、自动类型转换
* byte 、char 、short --> int --> long --> float --> double
* 当byte、char、short三种类型的变量做运算时,结果为int型,其他变量做运算,结果看大头
* 小可以赋值给大(容量)
* 2、强转
* 使用()进行强转,可能出现精度丢失
*/
@Test
public void charTest(){
char a = 'a';
System.out.println(a); //a
Long num = 12L;
long b = a + num;
System.out.println(b); //109
System.out.println((char)b); //m
short num2 = 1;
int i = a + num2;
System.out.println(i);
double c = a;
System.out.println(c); //97.0
char d = '0';
System.out.println((int)d);//48
}
StringBuffer、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;
/**
* Constructs a string buffer with no characters in it and an
* initial capacity of 16 characters.
*/
public StringBuffer() {
super(16);
}
public StringBuffer(int capacity) {
super(capacity);
}
...
...
@Override
public synchronized StringBuffer append(CharSequence s) {
toStringCache = null;
super.append(s);
return this;
}
//重写的方法AbstractStringBuilder抽象类的方法,通过super又调用了呗方法,所有StringBuffer/StringBuilder实际上是执行了相同父类中的方法。
}
//抽象父类中的append方法
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;
public AbstractStringBuilder append(String str) {
if (str == null)
return appendNull();
int len = str.length();
ensureCapacityInternal(count + len);
//将新数据str存放到底层char数组中
str.getChars(0, len, value, count);
count += len;
return this;
}
private void ensureCapacityInternal(int minimumCapacity) {
// overflow-conscious code
//数组容量不够用,minimumCapacity 为最少需要占用的容量大小
if (minimumCapacity - value.length > 0) {
//创建一个新数组,把原数组的数据复制过来
value = Arrays.copyOf(value,
newCapacity(minimumCapacity));
}
}
//获取扩容之后容量大小
private int newCapacity(int minCapacity) {
// overflow-conscious code
//扩容为原来的2倍+2
int newCapacity = (value.length << 1) + 2;
// 扩容为原来的2倍+2之后还不够,则使用最少需要占用的容量大小
if (newCapacity - minCapacity < 0) {
newCapacity = minCapacity;
}
//MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
return (newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0)
? hugeCapacity(minCapacity)
: newCapacity;
}
private int hugeCapacity(int minCapacity) {
//出现内存溢出问题
if (Integer.MAX_VALUE - minCapacity < 0) { // overflow
throw new OutOfMemoryError();
}
return (minCapacity > MAX_ARRAY_SIZE)
? minCapacity : MAX_ARRAY_SIZE;
}
}
推荐使用StringBuffer/StringBuilder时,根据实际需求指定capacity容量大小(使用有参构造),避免频繁的扩容。
- 测试代码:
public class StringBufferBuilderTest {
/**
* StringBuffer(线程安全,内部都是同步方法) 、StringBuilder(线程不安全) 都继承同一个抽象父类AbstractStringBuilder
* 执行效率: StringBuilder > StringBuffer > String
*
* 默认底层创建了一个长度是16的char数组(无参构造方法)
*
*/
@Test
public void test(){
//
//char[] value = new char[16];底层创建了一个长度是16的char数组。
StringBuffer sb = new StringBuffer();
System.out.println("被使用的长度:" + sb.length()); // 0
System.out.println("容量:" + sb.capacity()); // 16
sb.append('a');//value[0] = 'a';
sb.append('b');//value[1] = 'b';
System.out.println("被使用的长度:" + sb.length()); // 2
System.out.println("容量:" + sb.capacity()); // 16
StringBuffer sb1 = new StringBuffer("aaa"); //底层初始创建了容量是16 + "aaa".length() = 19 的char数组。
System.out.println("被使用的长度:" + sb1.length()); // 3
System.out.println("容量:" + sb1.capacity()); // 19
sb1.append("111111111111111111111"); //长度为21
/**
* 扩容方法:ensureCapacityInternal();
* 容量不足时,扩容为原来容量的2倍 + 2,并比较需要占用的容量大小和新扩容的容量大小,取大值作为新数组的容量,
* 如果使用新容量还需要判断容量是否超出Integer.MAX_VALUE,会出现OOM,
* 将原数组中的元素复制到新的数组中,再将新数据添加到新数组中。
*/
System.out.println("新容量:" + sb1.capacity());// 19*2 + 2 = 40
System.out.println("被使用的长度:" + sb1.length()); // 24
/**
* 常用方法(左闭右开)
*/
StringBuilder builder = new StringBuilder(64);
builder.append("abcdef");
builder.append(true);
int tr = builder.indexOf("tr", 2);
System.out.println("indexOf() : " + tr);
//替换一个字符串
builder.replace(1,3,"BC");
System.out.println("replace():" + builder);
//替换一个字符
builder.setCharAt(3,'中');
System.out.println("setCharAt() : " + builder);
//删除一个字符deleteCharAt
builder.deleteCharAt(3);
System.out.println("删除一个字符deleteCharAt:" + builder);
builder.insert(3,"中国人");
System.out.println("插入一个字符串insert():" + builder);
//遍历
for (int i = 0; i < builder.length(); i++) {
char c = builder.charAt(i);
System.out.println(c);
}
System.out.println(builder.toString());
}
}