乐趣区

关于string:String-的总结

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()办法,则被动将常量池中还没有的字符串对象放入池中,并返回对象地址。
@Test
public 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 教程

退出移动版