Java基础12:String、StringBuffer和StringBuilder的区别

前言:
String、StringBuffer和StringBuilder是常用的字符串类,都位于java.lang.*这个包下。当字符串相加操作或改动较少的情况下,建议使用String;当字符串相加操作较多或改变较多的情况下,建议使用StringBuilder,如果采用了多线程,则使用StringBuffer。

一、String、StringBuffer和StringBuilder的区别

1、String类是不可变类

String类是final类,意味着String类不能被继承,并且它的成员方法都默认为final方法。通过查看String类的源码我们可以发现String类其实是通过char数组来保存字符串的,且在这个字符数组前面加了final修饰符,说明String对象不可变,可理解为它是线程安全的。

Strin对象不可变,这就导致每次String的改变操作都不影响到原对象,就会引发新的String对象生成,而每次生成对象都会对系统性能产生影响,特别当堆内存中无引用对象多了以后,会造成很大的内存资源浪费,还有JVM的gc线程的系统开销,最终导致程序效率低下且浪费内存空间。

在String中,subString()、concat()和replace()等等操作都不是在原有的字符串对象上进行的,而是生成了新的String对象,然后将原String的变量引用指向新生成的对象。

所以当字符串对象内容经常改变的时候,最好不要用String,推荐使用 StringBuffer

2、StringBuffer是可变类

线程安全的可变字符序列,任何对它所指代的字符串的改变都不会产生新的对象,只会对StringBuffer对象本身进行操作。

可将字符串缓冲区安全地用于多线程,在必要时对这些方法进行同步,因此任意特定实例上的所有操作就好像是以串行顺序发生的,该顺序与所涉及的每个线程进行的方法调用顺序一致。每个字符串缓冲区都有一定的容量。只要字符串缓冲区所包含的字符序列的长度没有超出此容量(通常是16个字节),就无需分配新的内部缓冲区数组。如果内部缓冲区溢出,则此容量自动增大。

从源码可以看出,StringBuffer类的成员方法前面加上了Synchronized同步锁,这个关键字是在多线程访问时起到安全保护作用的,所以是线程安全的。

Java 5.0开始,为StringBuffer类补充了一个单个线程使用的等价类,即StringBuilder。与该类相比,通常应该优先使用StringBuilder类,因为它支持所有相同的操作,但由于它不执行同步,所以速度更快。


3、StringBuilder是可变类

线程不安全的可变字符序列,是Java5.0新增的。此类提供一个与 StringBuffer兼容的API,两者的方法基本相同。该类被设计用作 StringBuffer 的一个简易替换,用在字符串缓冲区被单个线程使用的时候(这种情况很普遍)。如果可能,建议优先采用该类,因为在大多数实现中,它比StringBuffer要快。

StringBuffer和StingBuilder类都继承自抽象类AbstractStringBuilder,拥有的成员属性以及成员方法基本相同。区别在于StringBufferd支持并发操作,是线性安全的,适合多线程中使用。StringBuilder线性不安全,但由于它不执行同步,故其在单线程中的性能比StringBuffer高。

StringBuffer和StringBuilder上的主要操作是append和insert方法,可重载这些方法,以接受任意类型的数据。每个方法都能有效地将给定的数据转换成字符串,然后将该字符串的字符追加或插入到字符串缓冲区中。append方法始终将这些字符添加到缓冲区的末端,而insert()方法则在指定的位置添加字符。

直接相加/拼接字符串执行效率大于间接相加:String = "I"+"Love"+"Java">String = str1+str1+str3;(str是字符串引用)

二、equals==的区别?
Java基础12:String、StringBuffer和StringBuilder的区别
1、Java当中所有的类都是继承于Object这个基类的,在Object中的基类中定义了一个equals方法,这个方法的初始行为是用==来比较对象的内存地址即用于判断对象的等价性。所以当我们没有重写equals的方法时,若使用equals来判断两个对象的是否相等,只有这两个对象指向的是同一个内存地址时,才会返回true,否则即使内容完全相同但在内存中是两个不同的内存地址也是返回false。有些类会覆盖(override)这个方法,如String,Integer,Date,在这些类当中equals有其自身的实现,而不再是比较类在堆内存中的存放地址了。

①.基本数据类型(byte,short,char,int,long,float,double,boolean)都是用==判断相等性,比较的是他们的数值。
②.对于
对象引用的比较:

当用==进行比较的时候比较的是他们在内存中的存放地址从而判断引用所指的对象是否是同一个。因此除非是同一个new出来的对象,它们比较后的结果为true,否则比较后结果为false。

当用进行equals比较时,在没有覆写equals方法的情况下,他们之间的比较还是基于他们在内存中的存放位置的地址值的,因为Object的equals方法也是用==进行比较的,所以比较后的结果跟双等号(==)的结果相同。

2、Java的equals方法其实是交给开发者去覆写的,让开发者自己去定义满足什么条件的两个Object是equal的。所以我们不能单纯的说equals到底比较的是什么,你想知道一个类的equals方法是什么意思就是要去看其定义。

Java中默认的 equals方法实现如下:
Java基础12:String、StringBuffer和StringBuilder的区别
而String类则覆写了这个方法,直观的讲就是比较字符是否都相同。
Java基础12:String、StringBuffer和StringBuilder的区别
故对String类来说,两个引用所指向的String都是"abc",但可能出现他们实际对应的对象并不是同一个(和jvm实现方式有关),因此用==判断他们可能不相等,但用equals判断一定是相等的。

三、i++和++i的区别

自增运算符(++)给整型和浮点型变量加1,只需要一个操作数。

前缀自增自减法(++a,--a): 先进行自增或者自减运算,再进行表达式运算。

后缀自增自减法(a++,a--): 先进行表达式运算,再进行自增或者自减运算 

1、单独使用在一条语句中时,前缀和后缀形式等价,没有差别。

2、当使用在较复杂的表达式中时,产生的计算结果可能不同

int count  = 15;

int total1 = count++;  //将15赋给变量total1,再将变量count增1

int total2 = ++count;  //变量count增1得到16,再赋给total2

四、条件运算符

条件运算符(?:)

条件运算符也被称为三元运算符。该运算符有3个操作数,并且需要判断布尔表达式的值。该运算符的主要是决定哪个值应该赋值给变量。

variable x = (expression) ? value if true : value if false
Java基础12:String、StringBuffer和StringBuilder的区别

五、instanceof运算符

Java基础12:String、StringBuffer和StringBuilder的区别

参考资料:

Java中的String、StringBuilder以及StringBuffer区别