String、StringBuffer、StringBuilder都属于字符串处理类,我们常用的字符串存储、拼接等功能通过这三个类都可以实现,但是在使用环境不同以及对代码的执行效率和安全性要求不同是,我们要有所区分。
先分析一下各个类的特点,然后再进行比较。
1、String:能在多线程环境下肆意使用的不可变的线程安全类,什么是“不可变”?没有接触过并发知识的童鞋可能会比较晕。下面给出一个简单的解释,一般来说,有Field的类,我们称之为有状态类,这些类的状态有可能会发生改变,如果一个类在单线程、多线程等环境下,它的状态都不会发生改变(一般用final修饰),那么这个类就属于不可变类。String通过把状态设置为private final让其状态不可变,将类修饰为final让本身不可继承,但是这还没有办法保证绝对的线程安全,如果发生指针逃逸(全局变量赋值,方法返回值,实例引用传递),引用类型所指向的内存堆的状态还是可能被改变,所以String采用了一些其它手段使自己变成一个不可变的线程安全类。String类中最核心的引用类型状态就是一个字符串数组char[] value(引用数据类型),String通过保护性拷贝保证了这个状态在各种环境下都是不能被调用者改变的,比如你想通过指针逃逸手段获得其状态value[]的引用,String不会直接把value[]引用给你,也不会直接使用你传入的引用给状态赋值,而是重新分配一块内存堆,拷贝当前状态值,或者拷贝传入的引用值到新的内存堆,再把这个新堆的引用给你,或赋值给本身状态。看看下面String的一个构造方法的代码:
public String(char value[], int offset, int count) { if (offset < 0) { throw new StringIndexOutOfBoundsException(offset); } if (count < 0) { throw new StringIndexOutOfBoundsException(count); } // Note: offset or count might be near -1>>>1. if (offset > value.length - count) { throw new StringIndexOutOfBoundsException(offset + count); } this.offset = 0; this.count = count; //拷贝了传入引用的内存堆,并把新的引用赋值给本身的状态value this.value = Arrays.copyOfRange(value, offset, offset+count); }
String通过使用Arrays.copyOfRange()来实现保护性拷贝,这样它的状态就不会发生改变,我们知道线程安全问题发生的本质就是类的状态改变的不能预测性,连状态都不会发生变化了,还会发生线程安全问题吗,所以我们可以再任何环境下肆意使用String对象,而不能考虑线程安全问题。当然,我们也注意到了,内存堆的重新分配和拷贝必然带来大的空间、时间资源的开销,严重时可能会造成频繁的垃圾回收,安全就要赋值代价。
2、StringBuffer:通过继承AbstractStringBuilder实现了对自身状态的同步访问,它的状态char[] value继承自父类AbstractStringBuilder,这个状态没有final修饰,是可变的。StringBuffer类中有改变自身状态的方法,并且没有针对指针逃逸做出保护性的拷贝,所以该类在多线程环境下,如果肆意使用可能会产生线程安全问题(把对象引用共享给多个线程,每个线程无法保证读写同步),StringBuffer属于有条件的线程安全,依靠调用者自己去控制避免产生线程安全问题。虽然StringBuffer不够安全,但是这给了调用者带来了其他方面的好处。由于它可以改变自身状态,在很多情况下不需要拷贝自身状态而浪费空间和时间资源,从而提高了执行效率。且看如下代码:
//调用父类的append方法,通过使用synchronized增加同步功能 public synchronized StringBuffer append(char str[], int offset, int len) { //调用父类的append方法 super.append(str, offset, len); //返回自身的引用,产生指针逃逸,灵活但不安全,依赖使用者 return this; } // AbstractStringBuilder类实现的使用System.arraycopy方法拼接字符数组 public AbstractStringBuilder append(char str[], int offset, int len) { int newCount = count + len; if (newCount > value.length) expandCapacity(newCount); //对自身字符数组的count进行扩展后,向其尾部拼接字符数组 System.arraycopy(str, offset, value, count, len); count = newCount; return this; }
可见,append方法属于同步方法,返回StringBuffer自身的引用,执行时不会做保护性拷贝,提高了执行效率,但是不够安全。
3、StringBuilder:也是继承AbstractStringBuilder类,但是与StringBuffer不同的是,它没有给任何方法加同步关键字synchronized,也没有实现像StringBuffer那么多的方法,只实现了一些常用的方法,如append、insert等,相当于是StringBuffer的一个轻量级版本,因为它是非线程安全的,所以在多线程环境下慎用。我们可以想到,没有了synchronized的束缚,它的执行效率肯定会有所提高。看一下它的一个append方法,与StringBuffer做一个对比就知道了:
//调用父类的append方法,没有使用synchronized增加同步功能 public synchronized StringBuffer append(char str[], int offset, int len) { //调用父类的append方法 super.append(str, offset, len); //返回自身的引用,产生指针逃逸,灵活但不安全,依赖使用者 return this; }
特性分析完成后,在使用这三个类时,就可以针对不同的使用环境,做出合理的选择。下面以字符串拼接为例,来分析一下三个类的使用情况:
1)单线程环境下,不用考虑线程安全问题,就执行效率而言,StringBuilder无疑使最好的,因为与StringBuilder相比,StringBuffer有synchronized同步锁,增加了开销,String对自身做出了保护性拷贝,大量的增加了开销,所以选择的优先级别为:StringBuilder > StringBuffer > String。但是有些时候我们要拼接一些常量字符串(可能是因为字符串太长而分行进行拼接),比如“123”+ “abc”+ “456”,可以直接使用String str = “123”+ “abc”+ “456”,因为JVM在编译此代码时直接将其优化成为“123abc456”放在字节码文件的常量池区域,使用时直接load到内存不需要做计算。
2)多线程环境下,要考虑线程安全问题,不过这个也得看具体情况,就线程安全的级别而言,三个类的level为:String > StringBuffer > StringBuilder,为什么上面已经分析过了。如何选择呢,这个就得分具体情况了,以下列出了几种常用场景:
a.如果对线程安全问题一无所知,请使用String,这可以确保你可以无脑的使用并且程序不会出现问题,如果实在对执行效率有要求,就去学习以下多线程编程基础知识,安全的去使用StringBuffer。
b.对执行效率有要求,比如程序中存在大量的字符串的拼接,如果需要方法同步优先使用StringBuffer,不需要方法同步优先使用StringBuilder。
相关推荐
"String StringBuffer和StringBuilder区别之源码解析" 在Java中,字符串是我们经常使用的数据类型,而String、StringBuffer和StringBuilder是Java中三种常用的字符串类。在这篇文章中,我们将从源码角度对String、...
StringBuffer:字符创变量 StringBuilder:字符创变量 从上面的名字可以看到,String是“字符创常量”,也就是不可改变的对象。对于这句话的理解你可能会产生这样一个疑问 ,比如这段代码:
在Java编程语言中,String、StringBuilder和StringBuffer都是用来处理字符串的类,它们之间存在一些重要的区别,主要涉及到性能和线程安全性。 首先,`String`类代表的是字符串常量,一旦创建,其内容就不能改变。...
在Java编程语言中,String、StringBuffer和StringBuilder都是用来处理字符串的类,但它们之间存在显著的性能和功能差异。下面我们将深入探讨这三个类的区别。 首先,`String`类是Java中最基本的字符串类型,它表示...
在Java编程语言中,`String`、`...理解`String`、`StringBuffer`和`StringBuilder`的区别和使用场合,可以帮助开发者写出更高效、更安全的代码。在实际开发中,应根据项目需求和环境选择合适的字符串处理类。
String、StringBuffer 和 StringBuilder 是 Java 语言中三种不同类型的字符串处理方式,它们之间存在着明显的性能和线程安全性差异。 String String 类型是不可变的对象,每次对 String 对象进行改变时都会生成一...
String, StringBuffer 与 StringBuilder 的区别 在 Java 中,String, StringBuffer 和 StringBuilder 三个类都是用于字符操作的,但它们之间有着很大的区别。 首先,String 是不可变类,意味着一旦创建了 String ...
stringbuilder用法 String、StringBuilder、StringBuffer 用法比较String、StringBuilder、StringBuffer 用法比较String、StringBuilder、StringBuffer 用法比较String、StringBuilder、StringBuffer 用法比较String...
`StringBuilder`是`JDK 5.0`引入的新类,它是`StringBuffer`的一个轻量级替代品,主要区别在于`StringBuilder`不是线程安全的。这意味着在单线程环境下,`StringBuilder`的操作速度通常会比`StringBuffer`更快,因为...
String、StringBuilder、StringBuffer的区别 在 Java 中,String、StringBuilder 和 StringBuffer 三者都是字符串处理类,但是它们之间存在着本质的区别。本文将从执行速度、线程安全性、字符串处理方式等方面对这...
Java 中 String 和 StringBuffer 与 StringBuilder 的区别及使用方法 Java 中 String、StringBuffer 和 StringBuilder 是三个常用的字符串操作类,每个类都有其特点和使用场景。在开发过程中,正确选择合适的字符串...
在Java编程语言中,String、StringBuffer和StringBuilder都是用来处理字符串的重要类,它们各自具有不同的特性和使用场景。下面将详细解析这三个类的区别。 首先,`String`类是最基础的字符串处理类,它被设计为不...
Java 中 String, StringBuffer 与 StringBuilder 的区别 Java 中 String, StringBuffer 与 StringBuilder 三种字符串类型的区别是很多开发者经常混淆或不了解的知识点。今天,我们将深入探讨这三种字符串类型的区别...
String、StringBuilder以及StringBuffer的区别
string,stringbuffer,stringbuilder
string,stringBuffer,stringBuilder
"Java 中 String、StringBuffer 和 StringBuilder 的区别及用法" Java 中 String、StringBuffer 和 StringBuilder 是三个常用的字符串操作类,了解它们之间的区别对 Java 开发者来说非常重要。本文将详细介绍这三者...
Java中的字符串处理是编程中常见的任务,涉及到三个关键类:String、StringBuffer和StringBuilder。它们在功能上相似,但有着重要的区别,主要体现在字符串的可变性、线程安全性和性能方面。 1. 可变性 - String类...
在Java编程语言中,String、StringBuffer和StringBuilder都是用来处理字符串的重要类,它们各自有特定的使用场景和特性。理解这三个类的区别对于任何Java开发者,无论是初学者还是经验丰富的程序员,都是非常重要的...