likes
comments
collection
share

String、StringBuilder、StringBuffer的区别

作者站长头像
站长
· 阅读数 5

面试官可以考察的内容

如果继续深入,面试官可以从各种不同的角度考察,比如可以:

  • 通过 String 和相关类,考察基本的线程安全设计与实现,各种基础编程实践。
  • 考察 JVM 对象缓存机制的理解以及如何良好地使用。
  • 考察 JVM 优化 Java 代码的一些技巧。
  • String 相关类的演进,比如 Java 9 中实现的巨大变化。…

String

String、StringBuilder、StringBuffer的区别

String为什么不可变

String类满足了不可变类的定义。

  • 用final关键修饰,所以String类不会被继承,避免了子类破坏String类的不可变性
  • 保存字符串的数组用final修饰并且是私有的,而且String类没有提供修改这个字符串的方法,所以外部类也无法访问和修改。

不可变类只是其实例不能被修改的类。每个实例中包含的所有信息都必须在创建该实例的时候就提供,并且在对象的整个生命周期内固定不变。为了使类不可变,要遵循下面五条规则:

1. 不要提供任何会修改对象状态的方法。

2. 保证类不会被扩展。一般的做法是让这个类成为 final的,防止子类化,破坏该类的不可变行为。

3. 使所有的域都是 final 的。

4. 使所有的域都成为私有的。防止客户端获得访问被域引用的可变对象的权限,并防止客户端直接修改这些对象。

5. 确保对于任何可变性组件的互斥访问。 如果类具有指向可变对象的域,则必须确保该类的客户端无法获得指向这些对象的引用。

Java 9 为何要将 String的底层实现由char[]改成了byte[] ?

新版的 String 其实支持两个编码方案:Latin-1 和 UTF-16。如果字符串中包含的汉字没有超过 Latin-1 可表示范围内的字符,那就会使用 Latin-1 作为编码方案。Latin-1 编码方案下,byte 占一个字节(8 位),char 占用 2 个字节(16),byte 相较 char 节省一半的内存空间。

JDK 官方就说了绝大部分字符串对象只包含 Latin-1 可表示的字符。

String、StringBuilder、StringBuffer的区别

如果字符串中包含的汉字超过 Latin-1 可表示范围内的字符,bytechar 所占用的空间是一样的。

这是官方的介绍:openjdk.java.net/jeps/254

字符串拼接,用+ 还是StringBuilder

Java 语言本身并不支持运算符重载(?),“+”和“+=”是专门为 String 类重载过的运算符,也是 Java 中仅有的两个重载过的运算符。

String str1 = "he";
String str2 = "llo";
String str3 = "world";
String str4 = str1 + str2 + str3;

上面的代码对应的字节码如下:

String、StringBuilder、StringBuffer的区别

可以看出,字符串对象通过“+”的字符串拼接方式,实际上是通过 StringBuilder 调用 append() 方法实现的,拼接完成之后调用 toString() 得到一个 String 对象 。

不过,在for循环内使用“+”进行字符串的拼接的话,存在比较明显的缺陷:StringBuilder 对象是在循环内部被创建的,这意味着每循环一次就会创建一个 StringBuilder 对象。编译器不会创建单个 StringBuilder 以复用,会导致创建过多的 StringBuilder 对象

不过,使用 “+” 进行字符串拼接会产生大量的临时对象的问题在 JDK9 中得到了解决。在 JDK9 当中,字符串相加 “+” 改为了用动态方法 makeConcatWithConstants() 来实现,而不是大量的 StringBuilder 了。这个改进是 JDK9 的 JEP 280open in new window

字符串常量池和intern方法

String 类型的变量和常量做“+”运算时发生了什么。

String str1 = "str";
String str2 = "ing";
String str3 = "str" + "ing";  //常量池中的对象
String str4 = str1 + str2;  //在堆上创建的新的对象
String str5 = "string";  //常量池中的对象
System.out.println(str3 == str4);//false
System.out.println(str3 == str5);//true
System.out.println(str4 == str5);//false

对于编译期可以确定值的字符串,也就是常量字符串 ,jvm 会将其存入字符串常量池。并且,字符串常量拼接得到的字符串常量在编译阶段就已经被存放字符串常量池,这个得益于编译器的优化。

参考文章

  1. JavaGuide: Java基础常见面试题总结(中)
  2. 面试题系列第2篇:new String()创建几个对象?