讨论下两个String引用在各种情况下是否相等

前言

Jdk1.6及之前: JVM存在永久代, 运行时常量池在永久代,运行时常量池包含字符串常量池
Jdk1.7:有永久代,但已经逐步“去永久代”,字符串常量池从永久代里的运行时常量池分离到堆里
Jdk1.8及之后: 无永久代,变成了元空间,运行时常量池在元空间,字符串常量池依然在堆里

字符串创建各种状态如下:

  1. 只在常量池上创建常量
    String a1 = “AA”;
  2. 只在堆上创建对象:调用toString方法或者+
    String a2 = new String(“A”) + new String(“A”);
  3. 在堆上创建对象,在常量池上创建常量
    String a3 = new String(“AA”);
  4. 在堆上创建对象,在常量池上创建引用
    String a4 = new String(“A”) + new String(“A”);//只在堆上创建对象AA
    a4.intern();//将该对象AA的引用保存到常量池上

代码举例说明

package base;

public class StringInnerTest {

    private String some = "hello";

    private final String some2 = new String("hello");

    private static final String some3 = new String("hello");

    public static void main(String[] args) {
        // null 和null是可以相等的
        if (null == null) {
            System.out.println("ok"); // 这里会输出ok
        }
        /**
         * 定义在类中的成员变量some没有进行实例化,会先去从常量池里边去拿,如果拿不到才会实例化然后放入到常量池
         */
        System.out.println("StringInnerTest.new1.some == StringInnerTest.new2.some:" +
                (new StringInnerTest().some == new StringInnerTest().some)); // true

        // 类初始化的时候会创建新的对象,记住只要两个String不是同一个对象就不会相等
        System.out.println("StringInnerTest.new1.some2 == StringInnerTest.new2.some2:" +
                (new StringInnerTest().some2 == new StringInnerTest().some2)); // false

        // 静态变量在类被引用的时候会被初始化,
        // 只会被初始化一次,所以不管实例1还是实例2读取的some3都是同一个对象
        System.out.println("StringInnerTest.new1.some3 == StringInnerTest.new2.some3:" +
                (new StringInnerTest().some3 == new StringInnerTest().some3)); // true

        // 如果常量池中没有"h"则创建新实例放入到常量池
        String c = "h";

        // 常量池中已经有了"h"直接引用过来
        String e = "h";

        System.out.println("e==c:" + (e == c)); // 这里会输出位true

        // 最终JAVA虚拟机会将这种情况处理为 String a = new String(c + "w");
        String a = c + "w";

        // 同上
        String b = c + "w";

        // 这样a和b就是不同对象了,此时肯定不会相等
        System.out.println("a==b:" + (a == b)); // 结果必定为false

        // 同上String.format 也会创建一个新String对象
        String f = String.format("%s%s", c, "w");

        System.out.println("f 对象地址:" + Integer.toHexString(f.hashCode()));

        System.out.println("a 对象地址:" + Integer.toHexString(a.hashCode()));

        System.out.println("f.equals(a):" + (f.equals(a))); // 结果true

        System.out.println("f==a:" + (f == a)); // 结果为false

        // 超过一定数量的string对象想加会被编译器优化变成StringBuilder处理
        // (new StringBuilder()).append(c).append(a).append(b).append(f).toString();
        String d = c + a + b + f;

        // 这种情况相当于指针直接指向到a指向的内存块
        String g = a;
        // 同上
        String h = a;

        System.out.println("g==h:" + (g == h)); // 这里g和h指向的是相同的内存地址所以这里是true

        String a3 = "world";

        final String a1 = "hello" + a3;
        String a2 = "hello" + a;
        // String a2 = new String("hello" + a);
        System.out.println("a1 == a2:" + (a1 == a2)); // false

        String b1 = "helloworld";
        // 编译器会把b2优化为String b2 = "helloworld";这样只要常量池有实力就回去常量池去拿
        String b2 = "hello" + "world";
        String b3 = a1 + "world";

        System.out.println("b1 == b2:" + (b1 == b2)); // true
        // 不解释了,上述有说明
        System.out.println("b1 == b2:" + (a1 == b2)); // false

        final String a4 = "hello";
        String a6 = "";
        final String a5 = a4 + "world" + a6;

        System.out.println("a5 == b2:" + (a5 == b2));
        String a7 = a5.intern();

        // intern 如果常量池中没有"helloword"会将a5实例String放入到常量池中,
        // 常量池中有"helloword的话会a5会直接指向常量池中"helloword内存地址"",
        // a7引用a5在常量池中内存地址,这个时候a7和b2就相等了
        System.out.println("a7 == b2:" + (a7 == b2));

        StringBuilder sb = new StringBuilder("helloworld");
        // sb.toString 等于创建一个新的String对象所以这里也不会相等
        // new String(value, 0, count)
        System.out.println("sb == a7:" + (sb.toString() == a7));
    }

}

你可能感兴趣的:(jvm,java,算法)