Java 知识-- Java 中的 String 相等?

本文最后更新于:3 个月前

(本文基于 JDK 1.7 及以上)

  • test1

    String str1 = “hello word";
    
    String str2 ="hello" +" word";
    
    System.out.println(str1==str2);
    // true

    这种情况下编译器会对 str2 进行优化,直接优化成 “hello word”,str1==st2

  • test2

    String s1 = "hello word";
    // s1 此时在堆上(字符串常量池中)创建了一个 String 对象,并把对象加入到字符串常量池的引用中
    String s2 = new String("hello word");
    // s2 会在堆上生成一个 String 对象,因为字符串常量池中已经有了,那么不会创建新的 String 对象,会把字符串常量池中引用的对象直接返回给 s2 所创建的 String 对象
    // 但是 s2 和 s1 所引用的对象的地址还是不相同的
    // 因为 s2 其实还可以看成一个引用,它指向 s1
    System.out.println(s1==s2);
    // false
    System.out.println(s2.intern()=="Hello World");
    // intern() 方法会去字符串常量池中找有没有字符串对象 "Hello Word",这里找到了最开始 s1 存入的字符串常量,所以直接返回字符串常量池中的 String 对象地址
    // true
  • test3

    String s1 = new String("1");
    // 在堆上生成一个 String 对象,并且建立在字符串常量池中的 "1" String 对象引用
    String s2 = "1";
    // 把 s2 引用直接指向字符串常量池中的 "1" 对象
    System.out.println(s1 == s2);
    // s1 和 s2 引用的地址不相同,结果为 false
    System.out.println(s1.intern()=="1");
    // intern() 方法会去字符串常量池中找有没有字符串对象 "1",这里找到了最开始 s1 存入的字符串常量

    test2 和 test3 说明无论直接赋值和 new String 顺序如何改变,只要一个在常量池,一个是每次在堆上的新生成的对象,他们内存地址就一定不会相同

  • test4

    String s1 = new String("1")+new String("1");
    // 这里仅仅会在字符串常量池生成一个字符串对象 "1",和两个 String 对象,并且 String 对象指向到字符串对象
    // 因为这里两个 String 对象相加,而不是 new String("11"),所以这里虽然在堆上生成了第三个 String 对象,但是没有把对象的引用加入到字符串常量池中
    // 即此时并没有在字符串常量池中产生 "11" 这个字符串对象
    s1.intern();
    // 对于这个方法,JDK 1.7 中不会在字符串常量池生成一个 "11" 对象了,只是在字符串常量池中建立一个对于堆上字符串对象的引用
    String s2 = "11";
    // 这里去字符串常量池中查找,找到了一个指向堆上字符串对象的引用,即上面 s1 创建的对象
    System.out.println(s1 == s2);
    // true
  • test5

    为了验证上面 test4 中 s1 new String 时并没有加入到字符串常量池中:

    String s1 = new String("1")+new String("1");
    // 与上面的区别在于这里 s2 和 s1.intern() 方法顺序不同了
    String s2 = "11";
    // 这里直接看到字符串常量池中没有 "11" 这个字符串对象,那么直接创建
    s1.intern();
    // 此时返回了上面 s2 创建的字符串对象,但是没有意义了
    System.out.println(s1 == s2);
    // s1 在 堆上面,s2 在字符串常量池,结果为 false
    // false
  • test6

    String str1 = "b";
    // 编译器认为 str1 是变量,只会在运行时确定 str2 的值
    // 此时会在堆上产生一个字符串常量
    String str2 = "a" + str1;
    String str12 = "ab";
    System.out.println(str1 = str12);
    // false
    // 和上面代码不同的地方在于 str1 加了 final 修饰,编译器会认为这个 str1 是常量进而在编译时直接优化
    final String str1 = "b";
    // 在编译时会直接变成 str2 = "ab";
    String str2 = "a" + str1;
    String str12 = "ab";
    System.out.println(str1 = str12);
    // true