String根本个性
String : 字符串,应用一对""引起来示意。
String申明为final的。,不可被继承
String 实现了Serializeable接口:示意字符串反对序列化的。实现了Comparable 接口:示意String能够比拟大小
String在jdk8 及以前外部定义了final char[] value 用于存储字符串数据。jdk9 时改为byte[]
String存储构造变更
String应用byte[]加上编码标记,节约了内存空间
StringBuffer 和StringBuilder 都也有相应的改变。
String的根本个性
String代表不可变的字符序列,即不可变性
- 当对字符串从新赋值时,须要重写指定内存区域赋值,不能应用原有的value进行赋值。
- 当对现有的字符串进行间断操作时,也须要从新指定内存区域赋值,不能应用原有的value进行赋值
- 当调用String的replace()办法批改指定字符或字符串时,也须要从新指定内存区域赋值,不能应用原有的value进行赋值
- 通过字面量的形式(区别于new)给一个字符串赋值,此时的字符串值申明在字符串常量池中。
字符串常量池中是不会存储雷同内容的字符串。
String的String Pool 是一个固定大小的Hashtable,默认值大小长度是1009,如果放进String Pool 的String十分多,就会造成Hash抵触,从而导致链表会很长,而链表长了后会造成的影响就是当调用String.intern时性能会大幅降落。
应用-XX:StringTableSize 可设置StringTable的长度
在jdk6中StringTable是固定的,就是1009 的长度,所以如果常量池中的字符串过多就会导致效率降落地很快。StringTableSize设置没有要求
在jdk7中,StringTable的长度默认值是60012 ,jdk8开始,1009是能够设置的最小值。
String 的内存调配
Java6及之前,字符串常量池寄存在永恒代。
Java7 中Oracle的工程师对字符串池的逻辑做了很大的变动,行将字符串常量池的地位调整到Java堆内。
- 所有的字符串都保留在堆(Heap)中,就像其余一般对象一样,这样能够让你在进行调优利用时仅需调整堆大小就能够了。
- 字符串常量池概念本来应用得比拟多,然而这个改变使得咱们有足够的理由让咱们重新考虑在java7 中应用String.intern()。
Java8 元空间,字符串常量在堆。
String的基本操作
Java语言标准里要求完全相同的字符串字面量,应该蕴含同样的Unicode字符序列(蕴含同一份码点序列的常量),并且必须是指向同一个String类实例。
字符串拼接操作
- 常量与常量的拼接后果是在常量池,原理是编译期优化
- 常量池中不会存在雷同内容的常量
- 只有其中有一个是变量,后果就在堆中。变量拼接的原理是StringBuilder
- 如果拼接的后果调用intern()办法,则被动将常量池中还没有的字符串对象放入池中,并返回对象地址。
@Testpublic void test3(){ String s1 = "a"; String s2 = "b"; String s3 = "ab"; /* 如下的s1 + s2 的执行细节:(变量s是我长期定义的) ① StringBuilder s = new StringBuilder(); ② s.append("a") ③ s.append("b") ④ s.toString() --> 约等于 new String("ab") 补充:在jdk5.0之后应用的是StringBuilder,在jdk5.0之前应用的是StringBuffer */
/*1. 字符串拼接操作不肯定应用的是StringBuilder! 如果拼接符号左右两边都是字符串常量或常量援用,则依然应用编译期优化,即非StringBuilder的形式。2. 针对于final润饰类、办法、根本数据类型、援用数据类型的量的构造时,能应用上final的时候倡议应用上。 */
/** * 如何保障变量s指向的是字符串常量池中的数据呢? * 有两种形式: * 形式一: String s = "shkstart";//字面量定义的形式 * 形式二: 调用intern() * String s = new String("shkstart").intern(); * String s = new StringBuilder("shkstart").toString().intern(); * */public class StringIntern { public static void main(String[] args) { String s = new String("1"); s.intern();//调用此办法之前,字符串常量池中曾经存在了"1" String s2 = "1"; System.out.println(s == s2);//jdk6:false jdk7/8:false String s3 = new String("1") + new String("1");//s3变量记录的地址为:new String("11") //执行完上一行代码当前,字符串常量池中,是否存在"11"呢?答案:不存在!! s3.intern();//在字符串常量池中生成"11"。如何了解:jdk6:创立了一个新的对象"11",也就有新的地址。 // jdk7:此时常量中并没有创立"11",而是创立一个指向堆空间中new String("11")的地址 String s4 = "11";//s4变量记录的地址:应用的是上一行代码代码执行时,在常量池中生成的"11"的地址 System.out.println(s3 == s4);//jdk6:false jdk7/8:true }}
public class StringIntern1 { public static void main(String[] args) { //StringIntern.java中练习的拓展: String s3 = new String("1") + new String("1");//new String("11") //执行完上一行代码当前,字符串常量池中,是否存在"11"呢?答案:不存在!! String s4 = "11";//在字符串常量池中生成对象"11" String s5 = s3.intern(); System.out.println(s3 == s4);//false System.out.println(s5 == s4);//true }}
总结String 的intern()的应用
Jdk1.6中,将这个字符串对象尝试放入串池
- 如果串池中有,则并不会放入,返回已有的串池中的对象的对象地址
- 如果没有,会把此对象赋值一份,放入串池,并返回串池中的对象地址
jdk1.7起,将这个字符串对象地址尝试放入串池
- 如果串池中有,则并不会放入。返回已有的串池中的对象的地址
- 如果没有,则会把对象的援用地址复制一份,放入串池,并返回串池中 的援用地址。
参考资料:宋红康JVM教程