字符串双引号、new String("")、intern()方法的总结

写本文的原因是因为看到了下面这些面试的题目,有点晕,故找了几篇博客学习了一下,以下是代码和学习记录

代码



String s1 = new String("1")+new String("2");
        s1.intern();
        String s2 = "12";
        System.out.println(s1==s2); // true


        String s1 = new String("12");
        s1.intern();
        String s2 = "12";
        System.out.println(s1==s2); // false

        String s1 = "abc";
        String s2 = "a";
        String s3 = "bc";
        String s4 = s2 + s3;
        System.out.println(s1 == s4);

        //常量池上存在常量AABB
        String AABB = "AABB";
        String AA_BB = "AA" + "BB";
        System.out.println(AABB == AA_BB); //true

        String s2 = "a";
        s2+= "b";
        String s1 = "ab";
        System.out.println(s1 == s2); // false



学习记录与解释

创建字符串分析

一、直接使用双引号创建字符串

直接使用双引号创建字符串会经历以下几个步骤:

  1. 判断这个字符串常量是否存在于常量池
  1. 如果存在
  1. 返回常量池中对象的地址
  1. 如果不存在
  1. 在常量池中创建该常量对象,并返回此常量对象的地址
String a1 = "AA";//在常量池上创建常量AA
    String a2 = "AA";//直接返回已经存在的常量AA
    System.out.println(a1 == a2); //true

    // 从上述逻辑可以看出,a1和a2都是常量池中字符串常量"AA"对象的地址。
    String a3 = new String("AA");    //在堆上创建对象AA、在常量池中创建字符串常量"AA"
    a3.intern(); //返回常量池上字符串常量"AA"的引用
    String a4 = "AA"; // 字符串常量存在于常量池,返回字符串常量"AA"的地址。
    System.out.println(a3 == a4); //false,如果这个例子不理解,请看完整篇文章再回来看这里



二、new String创建字符串

直接使用new String创建字符串会经历以下几个步骤:

  1. 首先在堆上创建对象(无论堆上是否存在相同的字面量的对象)
  2. 判断常量池中是否存在相同字符串的字面量
  1. 如果存在
  1. 啥也不做
  1. 如果不存在
  1. 在常量池中创建常量
String a1 = new String("AA");
String a2 = new String("AA");
System.out.println(a1 == a2); //false

// 很显然,a1、a2都是堆上各自的对象地址,所以返回false
//如果常量池上不存在常量AA,也不存在引用AA,则创建常量AA
String a1 = new String("AA");
System.out.println(a1 == a1.intern()); //false

// a1是堆上"AA"对象的地址,a1.intern()是字符串常量池中对象的地址,所以返回false



三、双引号相加

使用双引号相加会经历以下几个步骤:

  1. 如果两个相加的都是常量。例如“a” + “b”
  1. 判断相加后的常量在常量池上是否存在
  1. 如果不存在
  1. 在常量池上创建相应的常量
  1. 如果存在
  1. 返回常量池中对应对象的引用
  1. 如果相加的有一个是变量,例如 String b = “b”;String c = “a” + b;
  1. 判断这两个常量和相加后的常量在常量池上是否存在
  1. 如果不存在
  1. 在常量池上创建相应的常量
  1. 如果存在
  1. 返回常量池中对应对象的引用
String a2 = "AABB";//常量池上存在常量AABB
String a3 = "AA" + "BB";// 并返回"AABB"常量的地址
System.out.println(a2 == a3); //true
//常量池上存在引用AABB
String a4 = new String("AA") + new String("BB"); //在堆上创建对象AA、BB和AABB,在常量池上创建常量AA和BB
a4.intern(); // 将堆中"AABB"对象地址保存在常量池中
String a5 = "AA" + "BB";
System.out.println(a4 == a5); //true||如果注释a4.intern()将为false

String a = "AA" + "BB";
String a0 = new String("A") + new String("A");
String a1 = a0.intern(); // // a1地址是堆上“AA”的地址
System.out.println(a1 == a0); // true

String b = "BB";
String a = "AA" + b;
String a0 = new String("A") + new String("A");
String a1 = a0.intern(); // a1是常量池中“AA”地址,a0是堆中“AA”的地址
System.out.println(a1 == a0); // false,因为“AA”是在创建变量a时就已经创建了。



四、两个new String相加

  1. 创建两个对象和相加后的对象
  1. 判断常量池中是否存在这两个对象的字面量常量(注意没有判断相加后的对象是否存在)
  1. 如果存在
  1. 啥也不做
  1. 如果不存在
  1. 在常量池上创建对应的字符串常量
// 在堆中创建"AA","BB","AABB",在常量池中创建"AA","BB"
String a2 = new String("AA") + new String("BB");
// 在堆中创建"A","A","AA",在常量池中创建"A"
String a3 = new String("A")+new String("A"); 
System.out.println(a3 == a3.intern()); //false 
// 因为a3是在堆中"AA"对象的地址,a3.intern()是上一步中在常量池中创建的"AA"所以不同
//只在堆上创建AABB对象,没有在常量池中创建常量AABB
String a2 = new String("AA") + new String("BB");
System.out.println(a2 == a2.intern()); //true
// 由于没有在常量池中创建常量AABB,所以在a2.intern()的时候,会将a2的地址保存在常量池并返回,所以为true



五、双引号字符串与new String字符串相加

  1. 创建两个对象,一个是new String的对象,一个是相加后的对象
  1. 判断双引号常量与new String的字面量在常量池是否存在
  1. 如果存在
  1. 不做操作
  1. 如果不存在
  1. 在常量池上创建对象的常量
String a1 = "AABB";
    String a2 = "AA" + new String("BB");
    System.out.println(a1 == a2.intern());//true
    System.out.println(a2 == a2.intern()); //false



String.intern()分析

执行该函数会执行以下步骤

  1. 判断这个常量是否存在于常量池
  1. 如果存在
  1. 返回对象的引用
  1. 如果不存在
  1. 将当前对象的引用复制到常量池,并且返回的时当前对象的引用

总结

一、只在常量池上创建对象



String a1 = "AA";



二、只在堆上创建对象



String a2 = new String("A") + new String("A");
// 只在堆上创建了"AA"。但是还是在堆上和常量池中创建了"A",并且堆中创建了两个"A"



三、在堆上创建对象,在常量池上创建常量



String a3 = new String("AA");



四、在堆上创建对象,在常量池上创建引用



String a4 = new String("A") + new String("A");//只在堆上创建对象AA
a4.intern();//将该对象AA的引用保存到常量池上



题目分析



String s2 = "a";// 在常量池中创建"a",并将引用赋值给s2
s2+= "b"; // 注意,与变量相加的时候是在运行期堆中运行的。所以会在常量池中创建"b",在堆中创建"ab"
String s1 = "ab";// 注意这里的"ab"是在常量池中的
System.out.println(s1 == s2); // 所以会返回false