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教程