String在JAVA中是以final修饰的char数组实现的,所以一旦创建了某个string,不可修改。而且String不能被继承(final)。
先来看第一个例子:
String b = "b";
String anotherB="b";
String newB = new String("b");
b == anotherB //true
b == newB //false
其中原委:
b
在java加载class的时候会被放入方法区(Method Area)的常量池(Constant Pool)中,之后通过b
赋值的变量都被当作constant
,直接指向常量池中的内存地址,所以b
和anotherB
指向同一个地址。
而new String("b")
则创建了一个instance,JVM会在执行这条语句的时候(Runtime)在堆(Heap)中为其分配内存,所以和b
指向的不是同一个地址。
第二个例子:
String s = "a";
for(int i =0; i < 10; i++)
s+="a";
编译期间,上段代码会Compiler被优化成:
String s = "a";
for(int i =0; i < 10; i++){
StringBuilder sb = new StringBuilder(s);
sb.append("a");
s=sb.toString();
}
创建了10个StringBuilder
实例,所以在loop中不要用string进行操作,不然会在Heap中创建大量短生命周期的instance而加重GC的负担。
第三个例子:
String s = "a" + "b" +"c";
这条语句java创建了多少对象?
一个!因为Compiler会针对这种语句做优化,所以上述语句等价于 String s = "abc";
其中abc
被当作const处理,加载进jvm的时候放入方法区常量池,s
指向该const的地址。所以s == "abc"
是true。
另一个例子:
String a = "a";
String ab = a + "b";
System.out.println(ab == "ab"); //false
因为ab赋值中a是变量,编译器不会针对优化。
String VS. StringBuilder VS. StringBuffer
-
StringBuilder是可变的(mutable),非线程安全。
-
String是不可变的(immutable)
-
StringBuffer是可变的,而且线程安全(thread-safe),因为其内的方法都被synchronize关键字修饰,保证每次只有一个线程操作该对象。因为每次操作都要先加monitor锁,所以其效率要差于StringBuilder.
String 为什么设计成immutable的?
- literal很常用,设计成
final
在JVM加载.class类文件的时候会把字符串放到方法区的常量池中,相同的字符串共用同一个常量池地址。 - String设计成
final
的会保证hashCode()
也不会改变,从而可以在String中缓存其计算的hash code
,不必每次都重新计算。 - 如果String是mutable的话,其
hashCode()
计算的结果也会变,在作为key存取Map
的话就会出问题:改变之后的string变量作为key
检索不出之前存入的value
。
网友评论