JDK1.8-直接赋值和new String()的区别及字符串常量池相关问题

一、字符串常量池

        在JDK6.0及之前版本,字符串常量池存放在方法区中。而在JDK7.0版本以后,字符串常量池被移到了堆中

二、字符串

2.1 创建字符串的方式

①String s = “abc”;

  1. 首先在字符串常量池中查找是否存在内容为"abc"的字符串常量;
  2. 如果存在,则直接让s引用该字符串常量;
  3. 如果不存在,则在字符串常量池中创建"abc"字符串常量,并引用s指向池中字符串常量"abc"。

②String s = new String (“abc”);

  1. 首先在字符串常量池中查找是否存在内容为"abc"的字符串常量;
  2. 如果存在,则不在串池中创建"abc",直接在堆中创建一个"abc"字符串对象,然后将堆中的这个"abc"对象引用给s;
  3. 如果不存在,则先在串池中创建一个"abc",再在堆中创建一个"abc"字符串对象,然后将堆中的这个"abc"对象引用给s。

注意只要使用 new String() 方法,便会在堆中创建新的字符串对象!

2.2 比较字符串的 == 和 equals() 区别

        == 比较的是变量(栈)内存中存放的对象的(堆)内存地址,用来判断两个对象的地址是否相同,即是否指向同一个对象。
注意:

  1. 比较的是操作符两端的操作数是否为同一个对象;
  2. 两边的操作数必须是同一类型的(可以为父子类)才能编译通过;
  3. 引用类型比较的是地址(即是否指向同一个对象);基本数据类型比较的是值,值相等则为true,如:int i = 10 与 long l = 10L 与 double d = 10.0都是相等的(为true)。

        equals() 用来比较两个对象是否相等,由于所有的类都是继承自java.lang.Object类的,在Object中的基类中定义了一个equals方法,这个方法的初始行为是比较对象的内存地址,但String类中重写了equals方法, 比较的是字符串的内容 ,而不再是比较类在堆内存中的存放地址了,即两个字符串的内容相同则返回true,反之返回false。

2.3 String类的intern()方法

JDK1.8-直接赋值和new String()的区别及字符串常量池相关问题_第1张图片

翻译翻译:

        String类的intern方法():一个初始为空的字符串池,它由String类独自维护。

        当调用 intern方法时,如果池中已经包含一个等于此String对象的字符串(用equals(Object)方法确定),则返回池中的字符串。

        否则,将此String对象添加到池中,并返回此String对象的引用

        对于任意两个字符串s和t,当且仅当s.equals(t)为true时,s.intern() == t.intern()才为true。

        所有字面值字符串和字符串赋值常量表达式都使用 intern 方法进行操作。

三、上例题

3.1 直接赋值

String s = "abc";
String s2 = "abc";
System.out.println(s == s2);//true

        s与s2引用的都是字符串常量池中的"abc",结果为true。

3.2 new String()

String s = "abc";//在串池中创建"abc"
String s2 = new String("abc");//在堆中创建"abc"
String s3 = new String("abc");//在堆中创建另一个"abc"
System.out.println(s == s2);//false
System.out.println(s2 == s3);//false
System.out.println(s.equals(s2));//true
System.out.println(s2.equals(s3));//true

        s2指向堆中创建的一个"abc"字符串对象,而s3指向堆中创建的另一个"abc"字符串对象。s2与s3是两个指向不同对象的引用,结果为false。

3.3 常量拼接

String s = "aabb";//在串池中创建"aabb"
String s1 = "aa" + "bb";
String s2 = "a" + "abb";
System.out.println(s == s1);//true
System.out.println(s1 == s2);//true

        在常量池中创建"aa"、"bb"、"a"、"abb",连接过程编译器直接优化成"aabb",而串池中已经有了"aabb",所以s1和s2引用的都是字符串常量池中的"aabb"。
注意:字符串常量池中有"aabb"、"aa"、"bb"、"a"、"abb"。

3.4 含变量拼接

String s = "aabb";//在串池中创建"aabb"
String s1 = "aa";//在串池中创建"aa"
String s2 = s1 + "bb";//会在堆中产生一个新的"aabb"对象
System.out.println(s == s2);//false
System.out.println(s.equals(s2));//true

        拼接字符串涉及到变量(不全为常量,即使“运算数”s1是用双引号创建的常量)的相加时,会在堆中创建一个新的对象,因为s1在编译之前是未知的,其内部实现是先new一个StringBuilder,然后 append(s1).append("bb");最后s2引用toString()返回的对象。即s2为堆中"aabb"字符串对象的引用。

String s1 = "aa";//在串池中创建"aa"
String s2 = "bb";//在串池中创建"bb"
String s3 = "aabb";//在串池中创建"aabb"
final String str1 = "aa";
final String str2 = "bb";
System.out.println("aa" + "bb" == s3);//true
System.out.println(s1 + s2 == s3);//false
System.out.println(str1 + str2 == s3);//true

解答:

         "aa" + "bb" == s3;拼接过程编译器直接优化成"aabb",而串池中已经有了"aabb",则直接指向串池中的"aabb",且s3为串池中"aabb"字符串常量的引用,所以结果为true。

        s1 + s2 == s3;有变量引用的字符串是不能优化的,除非变量是final修饰的或者直接为"aa" + "bb"的常量形式拼接,s1 + s2会在堆中创建一个新的"aabb"字符串对象,但s3为串池中"aabb"字符串常量的引用,所以结果为false。

        final修饰的为常量,所以str1 + str2为常量拼接,str1 + str2 == s3;等同于"aa" + "bb" == s3;所以结果为true。

3.5 intern()方法

①例一

String s = "aabb";
System.out.println(s == s.intern());//true

解答:

        在串池中创建"aabb",执行s.intern()发现串池中已经有"aabb",则返回池中的字符串"aabb"。执行结果为true。

②例二

String s = "aa" + "bb";//连接过程编译器直接优化成"aabb",串池中没有"aabb",则在串池中创建"aabb"
System.out.println(s == s.intern());//true

解答:

        常量拼接,拼接后s为"aabb",因为串池中没有"aabb",则就在串池中创建"aabb";执行s.intern()发现串池中已经有"aabb",则返回池中的字符串"aabb"。即s和s.intern()引用的都是字符串常量池中的"aabb",则执行结果为true。

③例三

String s = new String("aa") + new String("bb");
System.out.println(s == s.intern());//true

解答:

        new String("aa")先在字符串常量池中创建一个"aa",再在堆中创建一个"aa"字符串对象,new String("bb")先在字符串常量池中创建一个"bb",再在堆中创建一个"bb"字符串对象,拼接后在堆中产生一个新的"aabb"字符串对象,s为堆中"aabb"字符串对象的引用。串池中有:"aa"、"bb",堆中有:"aa"、"bb"、"aabb"。

        执行s.intern(),发现串池中没有"aabb",则将"aabb"添加到池中,并返回此String对象即堆中"aabb"字符串对象的引用,则s.intern()返回的是堆中"aabb"字符串对象的引用。
s和s.intern()都是堆中"aabb"字符串对象的引用,则执行结果为true。

④例四

String s = "a" + new String("a") + "bb";
System.out.println(s == s.intern());//true

解答:

        在字符串常量池中创建"a";new String("a")先在字符串常量池中创建一个"a",再在堆中创建一个"a"字符串对象;在字符串常量池中创建"bb"。相当于String s = new StringBuilder("a").append("a").append("bb").toString();拼接后在堆中产生一个新的"aabb"字符串对象,则s为堆中"aabb"字符串对象的引用。串池中有:"a"、"bb",堆中有:"a"、"aabb"。

        执行s.intern(),发现串池中没有"aabb",则将"aabb"添加到池中,并返回此String对象即堆中"aabb"字符串对象的引用,则s.intern()返回的是堆中"aabb"字符串对象的引用。
s和s.intern()都是堆中"aabb"字符串对象的引用,则执行结果为true。

⑤例五

String s = new String("aabb");
System.out.println(s == s.intern());//false

解答:

        new String("aabb")先在字符串常量池中创建一个"aabb",再在堆中创建一个"aabb"字符串对象,然后将堆中的这个"aabb"对象引用给s,即s为堆中"aabb"字符串对象的引用。串池中有:"aabb",堆中有:"aabb"。

        执行s.intern(),发现串池中已经有"aabb",则直接返回池中的字符串。即s.intern()返回的是串池中"aabb"的引用。
        s为堆中"aabb"字符串对象的引用,s.intern()是串池中"aabb"的引用,则执行结果为false。

3.6 巩固理解intern()方法

①例一

String s1 = new String("go") + new String("od");//s1为 堆中"good"字符串对象的引用
String s2 = s1.intern();//串池中没有"good",则将"good"添加到池中,且常量池中保存的是堆中"good"对象的引用,再返回此String对象,即 s2为堆中"good"字符串对象的引用
String s3 = "good";
System.out.println(s1 == s2);//true
System.out.println(s1 == s3);//true
System.out.println(s2 == s3);//true

解答:

        第一行,new String("go")先在串池中创建创建一个"go",再在堆中创建一个"go"字符串对象,new String("od")先在串池中创建创建一个"od",再在堆中创建一个"od"字符串对象,拼接后在堆中创建了一个新的"good"字符串对象,则s1为堆中"good"字符串对象的引用串池中有:"go"、"od",堆中有:"go"、"od"、"good"

        第二行,String s2 = s1.intern();发现串池中没有"good",则将"good"添加到池中(常量池中保存的是堆中"good"的引用),再返回此String对象,即s2为堆中"good"字符串对象的引用

        第三行,String s3 = "good";因为串池中已经有一个引用保存了"good"(jdk1.8字符串常量池允许将引用地址作为对象去存储),s3就直接指向这个地址,即s3还是引用的堆中的"good"

        s1、s2、s3都是堆中"good"的引用。则s1 == s2、s1 == s3、s2 == s3的结果都为true。

②例二

String s1 = new String("go") + new String("od");//s1为 堆中"good"字符串对象的引用
String s2 = "good";//串池中没有“good”,则将“good”添加到池中,然后s2引用“good”,即 s2为串池中“good”的引用
String s3 = s1.intern();//串池中已经有“good”,则返回池中的字符串,即 s3为串池中“good”的引用
System.out.println(s1 == s2);//false
System.out.println(s1 == s3);//false
System.out.println(s2 == s3);//true

解答:

        第一行,new String("go")先在串池中创建创建一个"go",再在堆中创建一个"go"字符串对象,new String("od")先在串池中创建创建一个"od",再在堆中创建一个"od"字符串对象,拼接后在堆中创建了一个新的"good"字符串对象,则s1为堆中"good"字符串对象的引用串池中有:"go"、"od",堆中有:"go"、"od"、"good"

        第二行,String s2 = "good";串池中没有"good",则在串池中创建"good",并让s2引用串池中的"good"字符串常量

        第三行,String s3 = s1.intern();发现串池中已经存在"good",则返回池中的字符串,则s3为串池中"good"字符串常量的引用

        s1为堆中"good"字符串对象的引用,s2和s3为串池中"good"字符串常量的引用。则s1 == s2和s1 == s3的结果为false,s2 == s3的结果为true。

③例三

String s1 = new String("g") ;
String s2 = new String("ood") ;
String s3 = s1 + s2;//s3为堆中"good"字符串对象的引用
String s4 = s3.intern();//串池中没有“good”,则将“good”添加到池中,再返回此String对象,即s4为堆中"good"字符串对象的引用
String s5 = "good";
System.out.println(s3 == s4);//true
System.out.println(s3 == s5);//true
System.out.println(s4 == s5);//true

解答:

        第一行,new String("go")先在串池中创建创建一个"go",再在堆中创建一个"go"字符串对象。s1为堆中"go"字符串对象的引用。

        第二行,new String("od")先在串池中创建创建一个"od",再在堆中创建一个"od"字符串对象。s2为堆中"od"字符串对象的引用。

        执行第一行和第二行后,串池中有:"g"、"ood",堆中有:"g"、"ood"、"good"

        第三行,String s3 = s1 + s2; s1与s2拼接后在堆中产生一个新的"aabb"字符串对象,则s3为堆中"good"字符串对象的引用

        第四行,String s4 = s3.intern();发现串池中没有"good",则将"good"添加到池中(常量池中保存的是堆中"good"的引用),再返回此String对象,即s4为堆中"good"字符串对象的引用

        第五行,String s5 = "good";因为串池中已经有一个引用保存了"good"(jdk1.8字符串常量池允许将引用地址作为对象去存储),s5就直接指向这个地址,即s5还是引用的堆中的"good"

        s3、s4、s5都是堆中"good"的引用。则s3 == s4、s3 == s5、s4 == s5的结果都为true。

④例四

String s1 = new String("g");
String s2 = new String("ood");
String s3 = s1 + s2;//s3为堆中"good"字符串对象的引用
String s4 = "good";//串池中没有“good”,则将“good”添加到池中,然后s4引用“good”,即 s4为串池中“good”的引用
String s5 = s3.intern();//串池中已经有“good”,则返回池中的字符串,即 s5为串池中“good”的引用
System.out.println(s3 == s4);//false
System.out.println(s3 == s5);//false
System.out.println(s4 == s5);//true

解答:

        第一行,new String("go")先在串池中创建创建一个"g",再在堆中创建一个"g"字符串对象。s1为堆中"g"字符串对象的引用。

        第二行,new String("od")先在串池中创建创建一个"opd",再在堆中创建一个"ood"字符串对象。s2为堆中"ood"字符串对象的引用。

        执行第一行和第二行后,串池中有:"g"、"ood",堆中有:"g"、"ood"、"good"

        第三行,String s3 = s1 + s2; s1与s2拼接后在堆中产生一个新的"aabb"字符串对象,则s3为堆中"good"字符串对象的引用

        第四行,String s4 = "good";串池中没有"good",则在串池中创建"good",并让s4引用串池中的"good"字符串常量

        第五行,String s5 = s3.intern();发现串池中已经存在"good",则返回池中的字符串,则s5为串池中"good"字符串常量的引用

        s3为堆中"good"字符串对象的引用,s4和s5为串池中"good"字符串常量的引用。则s3 == s4和s3 == s5的结果为false,s4 == s5的结果为true。


文中如有不正确的地方,请多多指教互相学习,谢谢!

你可能感兴趣的:(Java,java,字符串)