1、String为什么是不可变字符串?但是我们在实际应用中 String是可以被改变的,能详细说一下嘛?
不可变字符串主要是:
- 保存字符串的数组被
final
修饰并且是私有的,并且 String 类没有提供和暴露修改这个字符串的方法。 - String 类被
final
修饰不能被子类继承,进而避免了子类破坏String
不可变。
实际中可变的原因:
其实并不是改变 String ,是新创建了一个 String 对象指向改变后的值,原本的 String 成为副本字符串对象存留在内存中。
2、String str= new String(“abc”); 和String str=“abc” 这两者的区别在哪里?
String str= new String("abc")
:
不管字符串常量池中存不存在 “abc” ,直接就创建直接新建一个字符串 “abc” (注意新建的字符串 “abc” 不是在字符串常量池中,而是在堆中),然后将其赋给str。这种效率低于使用字符串常量池的方式。
String str="abc"
:
首先查看字符串常量池中是否存在字符串 “abc” ,如果存在则直接将“abc”赋给str,如果不存在则先在字符串常量池中新建一个字符串 “abc” ,然后再将其赋给str。
拓展:判断 String 类型的值相等的问题
实例一:
//指向字符串池
String str1 = "java";
//指向字符串池
String str2 = "blog";
String s = str1+str2;
+
运算符会在堆中建立起两个 String 对象,这两个对象的值分别是 “java” , “blog” ,也就是说从字符串常量池中复制这两个值,然后再堆中创建两个对象。然后再建立对象 s ,然后将 “javablog” 的堆地址赋给 s 。String s = str1+str2
这句话共创建了3个 String 对象。
//结果是false
System.out.println(s=="javablog");
实例二:
//直接将javablog对象放入字符串常量池中
String s = "java" + "blog";
//结果是true
System.out.println(s=="javablog");
实例三:
//不放在字符串常量池中,而是在堆中分分配
String s=str1+"blog";
//结果是false;
System.out.println(s=="javablog");
3、String中 “+” 和 StringBuffer 中的 append 性能上有啥区别?
"+"
每次对 String 类型进行 “+” 的时候其实都等同于生成了一个新的 String 对象,然后将指针指向新的 String 对象。
所以经常改变内容的字符串最好不要用 String 的 “+” 。因为每次生成对象都会对系统性能产生影响,特别当内存中无引用对象多了以后, JVM 的 GC 就会开始工作,那速度是一定会相当慢的。
StringBuffer
使用 StringBuffer 类的 append 方法时每次结果都会对 StringBuffer 对象本身进行操作,而不是生成新的对象,再改变对象引用。
需要线程安全使用 StringBuilder
注意:
① 如果 + 号两边的字符串都在字符串常量池中话,"+" 的性能比 StringBuffer 的 append 快。如下示例:
//字符串常量池中有 a 和 b
String str = "a" + "b";
StringBuffer sb = new StringBuilder("a").append("b");
//性能 str > ab
② 以下情况 Sting 的 “+” 实际上是使用 StringBuilder (或者 StringBuffer )对象的 append 方法:
//指向字符串池
String strA = "a";
//不放在字符串常量池中,而是在堆中分分配
String strAB = strA + "b";
上述的 strAB 会在对内存中创建 StringBuilder(或者 StringBuffer)对象,通过 append 方法拼接成 “ab” 对象,此时的 “ab” 是 StringBuilder (或者StringBuffer )类型的,通过调用 toString 方法转成 String 对象,此时 strAB 指向的是堆内存中为 “ab” 的 String对象。