1、String 为什么要设计为不可变类?(指导:可以从安全性,性能等方面来考虑)
这个主要是出于线程安全和性能方面的考虑。
- 线程安全体现在,由于 String 是不可变的,多个线程共享一个 String 时不用担心它的同步问题;
- 性能体现在缓存哈希值和设计常量池上。
String 在被创建时就缓存了自己的哈希值,使用时直接拿出来就行,不用重新计算,这使得 String 适合用来作为 Map 的 Key,可以快速获得 Key 的哈希值,提高查找和比较的效率;
除此之外,基于 String 的不可变,Java 使用常量池来尽可能的共享相同的字符串,来节约 String 的存储空间。具体的,当我们使用字面值创建 String 时,会先去查它是否已经存在于常量池中,如果是则直接返回这个已存在字符串的引用,而不会创建新的对象。
2、String a = new String(“aa”) + “bb” “这句话创建了多少个对象?为什么?(指导:没啥好说,这种笔试考的多)
共创建了 4 个 String 对象,第一个是常量池中的对象 “aa”,如果常量池中有 “aa” 就直接返回,没有就创建并添加进常量池中。第二个是 new 出来的以 “aa” 为初始值创建的 String 对象,第三个 “bb” 同 “aa”,第四个是通过 “+” 拼接前两个对象,创建出的新 String 对象。
3、String 对象最多可以存放多少个字符(长度)?(指导:可以从源码角度分析勒)
String 在源码中使用 char[] 来维护字符序列的,而 char[] 的长度是 int 类型,所以理论上 String 的长度最大为 2^31−1 ,占用空间大约为 4 GB,不过根据实际 JVM 的堆内存限制,编译时,String长度最多可以是2的16次方减2,运行时长度最多可以是2的31次方减1,意思是可以在编译时定义一些短的字符串,运行时可以进行拼接,长一点也可以。
4、字符串常量池是放在堆中吗?
不是,Java 8 以前被放在永久代中,Java 8 及以后被放在方法区的元数据 metadata 中。
5、String中 “+” 和 StringBuffer 中的 append 会有性能上的差别吗?
String 的 “+” 效率低于 StringBuffer 的 append( )。原因在于 String 是不可变类,任何对 String 的操作都会创建新的 String 对象。而 “+” 的执行过程实际上是先创建了一个 StringBuffer,然后调用 append( ),最后在 toString( )。效率上肯定是不如 StringBuffer 直接 append( ) 高的。