Java中的String、StringBuffer和StringBuilder区别

Java中关于字符串的类有String、StringBuffer和StringBuilder,然而三者到底有什么区别呢?


String:字符串常量。也就是说String是不可变的对象,因此每次对String类型的对象进行更改操作时,实际上是生成了新的String对象,然后修改指针指向新的String对象。因此可以发现,如果经常要改变字符串内容,用String就会造成内存中大量无引用的对象,当内存不足时GC工作就会引起性能下降


StringBuffer:字符串变量、线程安全。首先是字符串变量,所以每次对StringBuffer对象操作时就是对StringBuffer对象本身操作,而不会生成新的对象,StringBuffer相对于String类其速度较快。其次StringBuffer的字符缓冲区可以安全地用于多个线程,其操作方法主要有append和insert方法(append将字符添加到缓冲区的末端,insert将字符添加到指定位置)


StringBuilder:字符串变量、非线程安全。同StringBuffer是一个可变的字符序列,区别是不保证同步,所以单线程时可以优先采用StringBuilder,速度相比StringBuffer要快,二者方法基本相同


关于三者的运行速度可通过程序验证:

/** 
 * StringBuilder拼接,线程不安全,速度最快(10000条记录大概0毫秒) 
 */  
public static void getStringBuilderTime(){  
	long start = System.currentTimeMillis();  
	StringBuilder sb = new StringBuilder();  
	for(int i = 0;i < 10000;i++){  
		sb.append(i);  
	}  
	long end = System.currentTimeMillis();  
	System.out.print("StringBuilder: ");
	System.out.println(end - start);  
}  
  
/** 
 * StringBuffer拼接,线程安全,速度略快(10000条记录大概16毫秒) 
 */  
public static void getStringBufferTime(){  
	long start = System.currentTimeMillis();  
	StringBuffer sb = new StringBuffer();  
	for(int i = 0;i < 10000;i++){  
		sb.append(i);  
	}  
	long end = System.currentTimeMillis();  
	System.out.print("StringBuffer: ");
	System.out.println(end - start);  
}  
  
/** 
 * String拼接,线程安全,速度最慢(10000条记录大概265毫秒)  
 */  
public static void getStringTime(){  
	long start = System.currentTimeMillis();  
	String sb = new String();  
	for(int i = 0;i < 10000;i++){  
		sb += i;  
	}  
	long end = System.currentTimeMillis();  
	System.out.print("String: ");
	System.out.println(end - start);  
}  

多次实验,在字符串后追加字符,循环10000次,得知String的速度最慢!!!


关于字符串以上只是结论,那么问题来了---为什么String对象是不可变的?查看源码:

//JDK1.6
public final class String implements java.io.Serializable, Comparable<String>, CharSequence {  
    /** The value is used for character storage. */  
    private final char value[];  
  
    /** The offset is the first index of the storage that is used. */  
    private final int offset;  
  
    /** The count is the number of characters in the String. */  
    private final int count;  
  
    /** Cache the hash code for the string */  
    private int hash; // Default to 0  
}
//JDK1.7
public final class String implements java.io.Serializable, Comparable<String>, CharSequence {  
    /** The value is used for character storage. */  
    private final char value[];  
  
    /** Cache the hash code for the string */  
    private int hash; // Default to 0  
}

由以上的代码可以看出, 在Java中String类其实就是对字符数组的封装。JDK6中, value是String封装的数组,offset是String在这个value数组中的起始位置,count是String所占的字符的个数;在JDK7中,只有一个value变量,也就是value中的所有字符都是属于String这个对象的,这个改变不影响此处的讨论。 除此之外还有一个hash成员变量,是该String对象的哈希值的缓存,这个成员变量也和此处的讨论无关。
value,offset和count这三个变量都是private的,并且没有提供setValue, setOffset和setCount等公共方法来修改这些值,所以在String类的外部无法修改String。也就是说一旦初始化就不能修改, 并且在String类的外部不能访问这三个成员。此外,value,offset和count这三个变量都是final的, 也就是说在String类内部,一旦这三个值初始化了, 也不能被改变。所以可以认为String对象是不可变的了。


String对象真的不可变吗?

从上文可知String的成员变量是private final 的,也就是初始化之后不可改变。那么在这几个成员中, value比较特殊,因为他是一个引用变量,而不是真正的对象。value是final修饰的,也就是说final不能再指向其他数组对象,那么我能改变value指向的数组吗? 比如将数组中的某个位置上的字符变为下划线“_”。 至少在我们自己写的普通代码中不能够做到,因为我们根本不能够访问到这个value引用,更不能通过这个引用去修改数组。但是可以用反射实现,反射出String对象中的value属性, 进而改变通过获得的value引用改变数组的结构:

public static void testReflection() throws Exception {       
    //创建字符串"Hello World", 并赋给引用s  
    String s = "Hello World";   
      
    System.out.println("s = " + s); //输出Hello World  
      
    //获取String类中的value字段  
    Field valueFieldOfString = String.class.getDeclaredField("value");  
      
    //改变value属性的访问权限  
    valueFieldOfString.setAccessible(true);  
      
    //获取s对象上的value属性的值  
    char[] value = (char[]) valueFieldOfString.get(s);  
      
    //改变value所引用的数组中的第5个字符  
    value[5] = '_';  
      
    System.out.println("s = " + s);  //输出Hello_World  
}  
在这个过程中,s始终引用的同一个String对象,但是再反射前后,这个String对象发生了变化, 也就是说,通过反射是可以修改所谓的“不可变”对象的,但是一般我们不这么做。这个反射的实例还可以说明一个问题:如果一个对象,他组合的其他对象的状态是可以改变的,那么这个对象很可能不是不可变对象。例如一个Car对象,它组合了一个Wheel对象,虽然这个Wheel对象声明成了private final 的,但是这个Wheel对象内部的状态可以改变, 那么就不能很好的保证Car对象不可变。







  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值