Java 平台提供了两种类型的字符串:String 和 StringBuffer/StringBuilder,它们都可以储存和操作字符串,区别如下:
1)String 是只读字符串,也就意味着String引用的字符串内容是不能被改变的。初学者可能会有这样的误解:
1. String str = “abc”; 2. str = “bcd”;复制代码
如上,字符串 str 明明是可以改变的呀!其实不然,str仅仅是一个引用对象,它指向一个字符串对象“abc”。 第二行代码的含义是让str重新指向了一个新的字符串“bcd”对象,而“abc”对象并没有任何改变,只不过该对象已经成为一个不可及对象罢了。
2)StringBuffer/StringBuilder 表示的字符串对象可以直接进行修改。
3)StringBuilder 是Java5中引入的,它和StringBuffer的方法完全相同,区别在于它是在单线程环境下使用的, 因为它的所有方法都没有被 synchronized 修饰,因此它的效率理论上也比 StringBuffer 要高。
什么情况下用“+”运算符进行字符串连接比调用 StringBuffer/StringBuilder 对象的 append 方法连接字符串性能更好?
字符串是 Java 程序中最常用的数据结构之一。在 Java 中 String 类已经重载了"+"。也就是说,字符串可以直接使用"+"进行连接,如下面代码所示:
1.String s = "abc" + "ddd";复制代码
使用 jad 反编译的好处之一就是可以同时生成字节码和源代码。这样可以进行对照研究。从上面的代码很容易看出,虽然在源程序中使用了"+",但在编译时仍然将"+"转换成 StringBuilder。因此,我们可以得出结论,在 Java 中 无论使用何种方式进行字符串连接,实际上都使用的是 StringBuilder。 那么是不是可以根据这个结论推出使用"+"和 StringBuilder 的效果是一样的呢?这个要从两个方面的解释。如果从运行结果来解释,那么"+"和 StringBuilder是完全等效的。但如果从运行效率和资源消耗方面看,那它们将存在很大的区别。
如果连接字符串行表达式很简单(如上面的顺序结构),那么"+"和StringBuilder 基本是一样的,但如果结构比较复杂,如使用循环来连接字符串,那么产生的 Java Byte Code 就会有很大的区别。
大家可以看到,虽然编译器将"+"转换成了 StringBuilder,但创建 StringBuilder 对象的位置却在for语句内部。这就意味着每执行一次循环,就会创建一个 StringBuilder对象(对于本例来说,是创建了10个StringBuilder 对象),虽然Java有垃圾回收器,但这个回收器的工作时间是不定的。如果不断产生这样的垃圾,那么仍然会占用大量的资源。解决这个问题的方法就是在程序中直接使用 StringBuilder 来连接字符串。
StringBuffer 和 StringBuilder 的功能基本一样,只是 StringBuffer 是线程安全的,而 StringBuilder 不是线程安全的。因此,StringBuilder 的效率会更高。
class StringEqualTest { public static void main(String[] args) { String s1 = "Programming"; String s2 = new String("Programming"); String s3 = "Program"; String s4 = "ming"; String s5 = "Program" + "ming"; String s6 = s3 + s4; System.out.println(s1 == s2); System.out.println(s1 == s5); System.out.println(s1 == s6); System.out.println(s1 == s6.intern()); System.out.println(s2 == s2.intern()); //false //true //false //true //false}}复制代码
补充:解答上面的面试题需要知道如下两个知识点:
-
String 对象的 intern()方法会得到字符串对象在常量池中对应的版本的引用(如果常量池中有一个字符串与String对象的equals结果是true),如果常量池中没有对应的字符串,则该字符串将被添加到常量池中,然后返回常量池中字符串的引用;
-
字符串的+操作其本质是创建了 StringBuilder 对象进行 append 操作,然后将拼接后的 StringBuilder 对 象用 toString 方法处理成 String 对象,这一点可以用 javap -c StringEqualTest.class 命令获得 class 文件对应 的 JVM 字节码指令就可以看出来。
String、StringBuffer、StringBuilder 的区别?
(1)、可变不可变 String:字符串常量,在修改时不会改变自身;若修改,等于重新生成新的字符串对象。 StringBuffer:在修改时会改变对象自身,每次操作都是对StringBuffer 对象本身进行修改,不是生成新的对象; 使用场景:对字符串经常改变情况下,主要方法:append(),insert()等。
(2)、线程是否安全 String:对象定义后不可变,线程安全。
StringBuffer:是线程安全的(对调用方法加入同步锁),执行效率较慢,适用于多线程下操作字符串缓冲区 大量数据。
StringBuilder:是线程不安全的,适用于单线程下操作字符串缓冲区大量数据。
(3)、共同点
StringBuilder 与 StringBuffer有公共父类AbstractStringBuilder(抽象类)。 StringBuilder、StringBuffer 的方法都会调用 AbstractStringBuilder 中的公共方法,如 super.append(...)。 只是 StringBuffer 会在方法上加 synchronized 关键字,进行同步。最后,如果程序不是多线程的,那么使用 StringBuilder 效率高于 StringBuffer。