String,StringBuffer,StringBuilder相关理解及其区别
String
String是不可变对象, 由String创建的字符的内容是无法修改的,主要原因是其底层使用的是char[]数组,且使用final所修饰如下图
对于String而言我们应该尽量把它当成一个基本类型来使用 ,而不是当成类,因为当我们使用一String的时候一般仅仅只是为了定义一个字符串,使用new字符去创建一个对象初始化字符串时候会消耗堆栈的空间,十分浪费资源。由于String字符串值是不可改变的,即增添值相当于new一个对象(后面会讲解)
String具体分析
上述只是一个简单的了解,那下面我们来具体分析一下,帮助大家理解
String a = "123";
String b = new String("123");
对于String a = "123"
而言,程序在编译时,就会从常量池中找"123"这个字符串,若存在,将地址赋给a,若不存在,创建并赋值地址给a。
可以简单假想为:a -> b
而对于String b = new String("123")
而言,程序会在运行时会在栈上创建一个引用对象b,在堆上创建一个空间 存放 new String的对象,由于String对象比较特殊,会在常用池中查找"123"这个字符串,若存在,将地址赋给堆上的String对象,若不存在,创建并赋值地址给b。
可以简单假想为:b -> String ->“123”
如下图所示:
根据上面所示我们进行测试
@Test
public void demo() {
String a = "123";
String b = "123";
String c = "12"+"3";
String d = "12";
String e = d+"3";
String f = new String("123");
String g = new String("123");
System.out.println("1. a和b比较:"+(a == b));
System.out.println("2. a和c比较:"+(a == c));
System.out.println("3. a和e比较:"+(a == e));
System.out.println("4. a和f比较:"+(a == f));
System.out.println("5. f和g比较:"+(f == g));
}
下面是结果:
1. a和b比较:true
2. a和c比较:true
3. a和e比较:false
4. a和f比较:false
5. f和g比较:false
字符之间==是比较地址而不是比较内容,比较内容使用equals()。如上所示
- 说明a和b引用地址相同,都是指向常量池同一个值,故true
- 说明常量String得到的是字符串连接后的完成后的地址,
实际上String c = "12"+"3"
等价于String a = "123"
,即true - 值得说明的一个地方就是,由于String类型是不可以修改本身的,
String e = d+"3"
实际上是拆分成了两个部分:
StringBuilder str = new StringBuilder("12")
String e = str.append("3").toString
后面会说明StringBuilder
可以知道因为e指向的是堆中的Str对象,故比较是false - 由于a和f一个指向常量池,一个指向堆栈,引用不一样,故false
- f和g虽然传的参数一样,都指向堆内存中,但是对于new关键字而言每个new会开辟一片新的空间,即f和g指向堆内存中的对象不同,故false
StringBuffer和StringBuilder
StringBuffer是一个可变对象,与String不同的是可以对其字符串进行修改,添加值可以通过它的append()方法实现,可以通过toString()方法转化为String类型
,由于StringBuffer中的大部分方法采用了synchronized关键字修饰,因此线程安全,StringBuffer每次都需要判断锁,故效率会低于StringBuilder
//StringBuffer使用
StringBuffer str = new StringBuffer();//构造方法不传参数时为null
Str.append("123");
String a = Str.toString();
StringBuilder和StringBuffer用法几乎一样,唯一不同是StringBuilder没有synchronized修饰,不是线程安全的,但也因此效率会更优
//StringBuilder使用
StringBuilderstr Str = new StringBuilder();//构造方法不传参数时为null
Str.append("123");
String a = Str.toString();
测试String,StringBuffer,StringBuilder的性能
话不多说直接上代码:
package com.ll.String.demo;
/**
* 测试String,StringBuffer,StringBuilder的性能
* @author 李小轮
*
*/
public class Test {
@org.junit.Test
public void demo1() {
int times = 100000;
String addstr = "abc";
String str1 = "";
//测试String性能
long start1 = System.currentTimeMillis();
for(int i = 0;i<times;i++) {
str1 += addstr;
}
long end1 = System.currentTimeMillis();
//测试StringBuffer性能
StringBuffer str2 = new StringBuffer("");
long start2 = System.currentTimeMillis();
for(int i = 0;i<times;i++) {
str2.append(addstr);
}
long end2 = System.currentTimeMillis();
//测试StringBuilder性能
StringBuilder str3 = new StringBuilder("");
long start3 = System.currentTimeMillis();
for(int i = 0;i<times;i++) {
str3.append(addstr);
}
long end3 = System.currentTimeMillis();
System.out.println("这是String:" + (end1-start1));
System.out.println("这是StringBuffer:" + (end2-start2));
System.out.println("这是StringBuilder:" + (end3-start3));
}
}
运行结果:
这是String:14330
这是StringBuffer:4
这是StringBuilder:2
从上面结果也可以看出来,String每次字符增添相当于new一个对象,既耗费时间也浪费资源,而StringBuffer和StringBuilder每次都是操作同一个对象,速度相比String快太多了,StringBuffer和StringBuilder的效率相差不多,不过StringBuffer还是相对StringBuffer要慢一点,也恰好验证了前面的所说。
- 总结:不对字符进行操作,使用String就够了,而且使用时候不要new,没有必要。而需要经常操作字符的时候,建议使用StringBuffer,因为线程安全。但是实在要追求效率的时候,可以在单线程使用StringBuilder。
第一次写博客,感觉思维还是有些混乱的,不过我想表达的东西应该算是表达完了,建议大家去看看java虚拟机的深入了解,对java而言,基本上很多地方都可以解释得通。小白不容易,我也想有一天当大牛。