浅谈Java中的String,StringBuffer和StringBuilder
String
- String声明为final的,不可被继承,final char[] value用于存储字符串数据,final体现了String的不可变性。
- String的实例化方式:
/*
1. 方式一:通过字面量定义的方式
String s1 = "java"
2. 方式二:通过new+构造器的方式
String s1 = new String("java")
3. 以String s = new String( "abc");的方式创建对象,在内存中创建了几个对象?
答:两个,一个是堆空间中通过new创建了一个String对象,另一个是char[]对应的常量池中的数据”abc“
*/
@Test
public void test2(){
//通过字面量定义的方式:此时的s1和s2的数据声明在方法区中的字符串常量池中
String s1 = "java";
String s2 = "java";
//通过new+构造器的方式:此时的s3和s4保存的地址值,是数据在堆空间中开辟空间以后对应的地址值
String s3 = new String("java");
String s4 = new String("java");
System.out.println(s1==s2);//true
System.out.println(s1==s3);//false
System.out.println(s1==s4);//false
System.out.println(s3==s4);//false
}
- 通过字面量的方式(区别于new)给一个字符串赋值,此时的字符串值声明在字符串常量池中,字符串常量池中是不会存储相同内容的字符串的,即当两个字符串的值相同时他们共用的是字符串常量池中的同一个对象,即地址是相同的。
@Test
public void test1(){
String s1 = "abc";
String s2 = "abc";
System.out.println(s1 == s2);//true
}
- 当对字符串重新赋值时,需要重写指定内存区域赋值,不能使用原有的value进行赋值。当对现有的字符串进行连接操作时,也需要重新指定内存区域赋值,不能使用原有的value进行赋值。
@Test
public void test1(){
String s1 = "abc";
String s2 = "abc";
s1="hello";
System.out.println(s1 == s2);//false
System.out.println(s1);//hello
System.out.println(s2);//abc
}
@Test
public void test3(){
String s1 = "javaSE";
String s2 = "hadoop";
String s3 = "javaSE"+"hadoop";
String s4 = "javaSEhadoop";
String s5 = s1 + "hadoop";
String s6 = "javaSE" + s2;
String s7 = s1 + s2;
String s8 = s5.intern();
System.out.println(s3==s7);//false
System.out.println(s4==s7);//false
System.out.println(s3==s4);//true
System.out.println(s3==s5);//false
System.out.println(s3==s6);//false
System.out.println(s5==s6);//false
System.out.println(s3==s8);//true
}
/*
1.s3==s4的结果为true,说明s3和s4的地址相同,因为常量与常量的拼接结果在常量池中,而常量池中不会存在相同内容的常量,所以这里
的s3和s4是指向常量池中的同一对象。
2.s3==s5的结果为false,因为只要拼接双方其中有一个是变量,结果就在堆中。(想当于new的效果了)
3.如果拼接的结果调用intern()方法,返回值就在常量池中
*/
- String 的转换
/*
String 与基本数据类型,包装类之间的转换。
String --> 基本数据类型、包装类:调用包装类的静态方法:parseXxx(str)
基本数据类型,包装类 --> String:调用String重载的valueOf(xxx),或+""
*/
@Test
public void test4(){
String s1 = "123";
int num = Integer.parseInt(s1);
System.out.println(num);
String s2 = String.valueOf(num);
String s3 = num + "";
System.out.println(s2);
System.out.println(s3);
}
/*
String 与 char[]之间的转换
String --> char[]:调用String的toCharArray()
char[] --> String:调用String的构造器
*/
@Test
public void test5(){
String s1 = "java";
char[] c1 = s1.toCharArray();
for(int i = 0 ;i < c1.length;i++){
System.out.println(c1[i]);
}
String s2 = new String(c1);
System.out.println(s2);
}
StringBuffer和StringBuilder的区别和联系
因为String的值是不可变的,这就导致每次对String的操作都会生成新的String对象,当需要对字符串进行修改的时候,就可以考虑使用 StringBuffer 或 StringBuilder 类。他们与String 类的不同之处在于StringBuffer 和 StringBuilder 类的对象能够被多次的修改,并且不产生新的未使用对象(调用append(),insert()等方法)
String、StringBuffer、StringBuilder三者的异同?
1. String:不可变的字符序列;底层使用char[]存储
2. StringBuffer:可变的字符序列;线程安全的,效率低;底层使用char[]存储
3. StringBuilder:可变的字符序列;jdk5.0新增的,线程不安全的,效率高;底层使用char[]存储
StringBuffer:
StringBuilder:
通过源码我们可以发现StringBuffer和StringBuilder都是继承于AbstractStringBuilder类,二者的构造器和方法也基本一样,但是StringBuffer类的方法都有Synchronized关键字,也就是说StringBuffer是保证了线程安全的。
扩容问题:
StringBuffer和StringBuilder都是底层创建了一个长度是16的数组来存储对象,如果当我们要添加的数据底层数组盛不下了,那就需要扩容底层的数组。默认情况下,扩容为原来容量的2倍 + 2,同时将原有数组中的元素复制到新的数组中。