String为什么设计成final
String
源码剖析
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
private final char value[];
/** Cache the hash code for the string */
private int hash; // Default to 0
/** use serialVersionUID from JDK 1.0.2 for interoperability */
private static final long serialVersionUID = -6849794470754667710L;
/**
* Class String is special cased within the Serialization Stream Protocol.
*
* A String instance is written into an ObjectOutputStream according to
* <a href="{@docRoot}/../platform/serialization/spec/output.html">
* Object Serialization Specification, Section 6.2, "Stream Elements"</a>
*/
private static final ObjectStreamField[] serialPersistentFields =
new ObjectStreamField[0];
}
String
如何保证 不可变的呢?
字符数组使用了
final
修饰,这也只是表示了 字符数组的引用地址不可变,并不代表内容不可变。 其使用private
修饰,外部没有入口达到变动,从而保证了 String 的不可变性
为什么保证 String
是 final
的呢?特点:
- 因为只有保证
String
是final
的呢 只有当字符串不可变的,字符串池才可能实现;字符串池的实现可以节省很多的Heap
空间,因为不同的字符串变量都指向池中的同一个字符串.- 假如字符串可变,那么
String
interning
不能实现,那么变量改变了这个字符串的值,那么其他指向这个值的变量的值也改变了。 (安全问题来了,用户名密码,端口、IP等)- 字符串不可变,多线程安全,同一个字符串实例可以被对歌线程共享,不需要考虑同步问题
- 上面看出,在字符串不可变的情况下,创建的时候
hashcode
也被缓存了,不需要重新计算。一部分性能问题可以很好的选择字符串
String
在 JVM
层理解 拓展
JVM
层面有 虚拟机栈、本地方法栈、堆、程序计数器、元数据区(方法区)
- 字符串创建形式
String s1 = "1";
String s2 = new String("1");
- 编译期间 “1” 作为常量进入字符串常量池(这时候是静态常量池);
- 当编译
s1
时候,将s1
推进局部变量区(栈帧内部); 先判断 “1” 在字符串常量池在不在,不存在的话创建常量 “1” 加入常量池,并直接将s1
指向字符串常量池的对象 “1” 地址;- 当代码运行
s2
时候,使用的new
字段,JVM
先检查字符串常量在常量池存不存在,如果已经存在,直接在堆中复制改对象的副本,并且将s2
指向堆中的刚刚创建的对象地址。 如果不存在,则会实例化该字符串 “1” 并且将其放到常量池中(这时候是运行时常量池),然后在堆中复制刚刚加入常量池对象的副本创建出新的对象,并将s2
指向它
- ‘+‘号连接的字符串
String s1 = "1"+"2"+"3";
- 编译期间就能确定,直接作为 “123” 的常量进入常量池;
String s2 = "1"+"2"+new String("3")+"4";
- 当 ‘+’ 中间有变量时候,也只能在运行期才能确定,但是在编译期间会尽量的将字符串常量连接起来,形成新的字符串常量;
- 反编译后
String s2 = new StringBuilder("12").append(new String("3"")).append("4").toString()
;- 也就是说用 ‘+’ 连接中间有变量的时候,“13”, “3”, “4” 在常量池中,在堆中有
StringBuilder("1234")
,String("4")
,以及toString
后产生的String
对象
String s3 = new String("3") + new String("3");
- “3” 在常量池中(编译的时候)
- 运行时,在堆中产生 “3” 的副本两个
String
对象,并产生StringBuilder("11")
对象以及toString
的String
对象