String、StringBuiler 和 StringBuffer 面试那些事
String 应该是面试中被考烂的问题,但是不知道为什么还总是有面试官喜欢问。下面就从面试常问的几个问题出发,来深入理解下 String。
String、StringBuiler 和 StringBuffer 有什么区别?
这个问题主要想问的是你对这三个类的理解,这里可以不用说原理,诱导面试官继续问下去.
String 类是不可变的,而 StringBuiler 和 StringBuffer 类是可变的;
StringBuiler 是线程不安全的,StringBuffer 是线程安全的。
到这里,你已经给自己挖了一些坑了,填了这些坑不出意外面试的节奏也已经掌握到你的手里了。
为什么说 String 类是不可变的?
String 类是 final 类,也即意味着 String 类不能被继承,并且它的成员方法都默认为 final 方法;
用于保存值的数组也是被 private final 修饰的,说明它也是不可变的。
由于这两个原因,一旦 String 被创建那就肯定是不可变的。
那为什么下面的字符串 s 还是改变了?
对上面的代码使用 javap 命令进行反编译:
对于+
这种方式的拼接的底层其实是使用了 StringBuiler,先初始化一个 StringBuilder 对象,然后使用 append()方法拼接,最后使用 toString()方法得到结果。相当于如下代码:
查看StringBuilder.toString()
方法,你会发现重新创建了一个 String 对象,所以其实是将新的引用指向了 s,而不是 s 本身发生了改变。
那么将 String 类设计成不可变的意义是什么?
安全性:String 被许多 Java 类用作参数,比如 URI、反射、文件等,这就需要字符串不可变来保证多线程安全,而不用再写复杂的代码来保证。
JVM 如何优化字符串频繁创建的问题?
字符串对象需要消耗大量资源,因此 JVM 为了提高性能和减少内存的开销,提出了使用字符串常量池进行优化。在创建 String 对象时,判断字符串是否在字符串常量池中,如果在,则直接返回常量池中的引用。
由此,可以看出下面这个结果是 true:
扩展,下面的代码共创建几个实例?
答案是 3 个,可以通过 jvisualvm 查看
通过 new String()的方式会创建 1 个或 2 个,而使用字符串创建的方式则可能是 0 个。第一行创建两个对象,堆中创建了一个 String 对象,字符串常量池创建了一个。第二行发现常量池中已经有"abc"这个字符串对象实例,直接返回。第三行,在堆中又创建一个 String 对象。
StringBuffer 如何保证线程安全?
在StringBuffer
的方法上都被加上了synchronized
关键字。关于synchronized
会再说到。
总结
String 是日常开发中最常用到的类,设计者也更加慎重。所以学习这些基础的类,也能有所收获,这也是面试官乐此不疲的原因吧。关于 String 其实还有很多内容可以说,比如空指针异常,在日常项目中很多空指针异常都来源于字符串操作上,所以在有些开发规范中也强制要求使用 StringUtils 工具类来操作。
提问
你觉得 String 还有什么可以优化的地方?
成长在于一点一滴的积累,而我在路上。欢迎大家关注和点赞。
版权声明: 本文为 InfoQ 作者【Java旅程】的原创文章。
原文链接:【http://xie.infoq.cn/article/85ff1579eb477ef4bbbb827a8】。
本文遵守【CC-BY 4.0】协议,转载请保留原文出处及本版权声明。
评论