Java中的String、StringBuffer和StringBuilder区别
2016-03-31 14:57
585 查看
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要快,二者方法基本相同
以上只是结论,那么问题来了---为什么String对象是不可变的?查看源码:
由以上的代码可以看出, 在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引用改变数组的结构:
final 的,但是这个Wheel对象内部的状态可以改变, 那么就不能很好的保证Car对象不可变。
String:字符串常量。也就是说String是不可变的对象,因此每次对String类型的对象进行更改操作时,实际上是生成了新的String对象,然后修改指针指向新的String对象。因此可以发现,如果经常要改变字符串内容,用String就会造成内存中大量无引用的对象,当内存不足时GC工作就会引起性能下降
StringBuffer:字符串变量、线程安全。首先是字符串变量,所以每次对StringBuffer对象操作时就是对StringBuffer对象本身操作,而不会生成新的对象,StringBuffer相对于String类其速度较慢。其次StringBuffer的字符缓冲区可以安全地用于多个线程,其操作方法主要有append和insert方法(append将字符添加到缓冲区的末端,insert将字符添加到指定位置)
StringBuilder:字符串变量、非线程安全。同StringBuffer是一个可变的字符序列,区别是不保证同步,所以单线程时可以优先采用StringBuilder,速度相比StringBuffer要快,二者方法基本相同
以上只是结论,那么问题来了---为什么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对象不可变。
相关文章推荐
- GCD的同步异步串行并行、NSOperation和NSOperationQueue一级用dispatch_once实现单例
- [Form Builder]:CREATE_GROUP Built-in
- 并发队列ConcurrentLinkedQueue和阻塞队列LinkedBlockingQueue用法
- String.valueOf()的一个坑—“null”
- 对Map类分别按照Key,Value排序,返回值为List对象
- iOS之NSPredicate(正则表达式和UIBarController)
- Vue.js 添加组件
- ios CAShapeLayer和UIBezierPath
- iOS在更改用户头像并保存至本地沙盒目录中对于UIImagePickerController、UIAlertController的使用
- [UITableView _endCellAnimationsWithContext:]
- iOS基本UI控件总结
- #学习笔记#(56)angular ui-router使用姿势
- An AnnotationConfiguration instance is required to use.....异常
- php中的continue用法
- iOS-一个对UIAlertController的封装类分享
- iOS: setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key name.
- iOS UITableViewController出现crash
- 从response.header中提取cookie,在request里添加cookie
- 从response.header中提取cookie,在request里添加cookie
- UItableView的两个重用机制区别