String的不可变性
原因
String类底层采用 final 来定义,String类本身不提供修改底层byte[]数组的方法,而且String类也不可以被继承,所以String不可变。
优势
- HashMap等集合可以通过利用String的不可变性来进行hash(),String内部通过String的内容来进行哈希,而不是对象的地址。
- JVM虚拟机可以节省空间,通过将String字面量存储在字符串常量池中。
- String不可变带来了线程安全,不必要去处理其线程同步等问题。
- 在Java中,安全参数例如密码等经常使用String来定义,同时当JVM类加载器加载类时也大量使用。
code
String s1="java";
这个例子足够简单吧,创建了一个Java字符串,s1指向它,接下来让我们看下面的代码
s1=s1.contact(" code");
System.out.println(s1);
输出结果会是什么呢
java code
结果可能出乎有些人的预料,不是说String是不可变的吗?为什么s1指向的字符串变成了java code呢?
其实这里不是字符串改变了,而是s1的指向改变了,java字符串并没有改变,在字符串常量池中新创建了两个字符串常量,分别是code和java code,而s1指向了java code。
创建方法
1.使用“”
String s1="FrankLig";
上述代码首先在字符串常量池搜索是否存在FrankLig ,如果存在则s1指向它,如果不存在则会在字符串常量池创建FrankLig,再将s1指向它。
2.使用new
String s2=new String("FrankLig");
上述代码首先在堆中创建一个String对象,如果字符串常量池中不存在FrankLig,则再在字符串常量池中创建一个FrankLig,一共创建两个String对象。字符串常量池中存在FrankLig的话则只在堆中创建一个对象。s2指向的是堆中的对象。
创建了多少个字符串
这是我们经常遇到的问题,看接下来用代码就可以把这个问题弄清楚。
public class StringExample{
public static void main(String[] args){
String s1 = new String("Java");
String s2 = s1;
System.out.println(s2==s1); //true
String s3 = "code";
System.out.println(s3==s1); //true
System.out.println(s3==s2); //true
String s4 = "Frank";
String s5 = s4;
String s6 = new String("Frank");
System.out.println(s5==s4); //true
System.out.println(s6==s4); //false
s5.contact("blog");
}
}
上述代码一共创建了多少个字符串呢?
答案是6个。
来看看它们的内存分布
String.intern()
intern()方法返回一个指向字符串常量池中字符串的应用。
如果字符串常量池中有一个字符串与使用String.intern()方法的字符串相等,则直接返回对此字符串的应用。
如果没有则在字符串常量池中创建一个此字符串,返回对它的应用。
public class InternExample{
public static void main(String[] args){
String s1 = "Java";
String s2 = s1.intern();
System.out.println(s1==s2); //true
String s3 = "Rust";
String s4 = new String("Rust");
s5 = s3.intern();
s6 = s4.intern();
System.out.println(s5==s3); //true
System.out.println(s6==s3); //true
System.out.println(s6==s4); //false
}
}
一共创建了三个字符串,一个在堆中,两个在字符串常量池。