String str = “i” 与 String str = new String(“i”) 一样吗?

不一样,因为内存的分配方式不一样。String str = “i” 的方式,Java 虚拟机会将其分配到常量池中;而 String str = new String(“i”) 则会被分到堆内存中。

public class StringTest {    
    public static void main(String[] args) {
    String str1 = "abc";
    String str2 = "abc";
    String str3 = new String("abc");
    String str4 = new String("abc");
    System.out.println(str1 == str2);      // true
    System.out.println(str1 == str3);      // false
    System.out.println(str3 == str4);      // false
    System.out.println(str3.equals(str4)); // true
   }
}

在执行 String str1 = “abc” 的时候,JVM 会首先检查字符串常量池中是否已经存在该字符串对象,如果已经存在,那么就不会再创建了,直接返回该字符串在字符串常量池中的内存地址;如果该字符串还不存在字符串常量池中,那么就会在字符串常量池中创建该字符串对象,然后再返回。所以在执行 String str2 = “abc” 的时候,因为字符串常量池中已经存在“abc”字符串对象了,就不会在字符串常量池中再次创建了,所以栈内存中 str1 和 str2 的内存地址都是指向 “abc” 在字符串常量池中的位置,所以 str1 = str2 的运行结果为 true。

而在执行 String str3 = new String(“abc”) 的时候,JVM 会首先检查字符串常量池中是否已经存在“abc”字符串,如果已经存在,则不会在字符串常量池中再创建了;如果不存在,则就会在字符串常量池中创建 “abc” 字符串对象,然后再到堆内存中再创建一份字符串对象,把字符串常量池中的 “abc” 字符串内容拷贝到内存中的字符串对象中,然后返回堆内存中该字符串的内存地址,即栈内存中存储的地址是堆内存中对象的内存地址。String str4 = new String(“abc”) 是在堆内存中又创建了一个对象,所以 str 3 str4 运行的结果是 false。str1、str2、str3、str4 在内存中的存储状况如下图所示:

image-20210602132321684

发表评论

后才能评论

评论(2)

  • jedi 普通 2021-09-01 9:07 上午

    JDK8字符串常量池不是在堆中吗?

    • 帅地 永久会员 @ jedi 2021-09-01 11:23 下午

      p关于这个问题,其实挺容易造成误解的。

      在JDK1.8中,使用元空间代替永久代来实现方法区,但是方法区并没有改变,所谓”Your father will always be your father”,变动的只是方法区中内容的物理存放位置。正如上面所说,类型信息(元数据信息)等其他信息被移动到了元空间中;但是运行时常量池和字符串常量池被移动到了堆中。但是不论它们物理上如何存放,逻辑上还是属于方法区的。

      JDK1.8中字符串常量池和运行时常量池逻辑上属于方法区,但是实际存放在堆内存中,因此既可以说两者存放在堆中,也可以说两则存在于方法区中,这就是造成误解的地方。