开发者社区> 问答> 正文

Java中的字符串相加内存怎么分配?

蛮大人123 2016-03-11 16:04:05 812
public class StringDemo {
    public static void main(String[] args) {
        String a = "hello ";
        String b = a + "world";
        String c = "hello " + "world";
        String d = "hello world";

        System.out.println(b == d); //false
        System.out.println(c == d); //true
    }
}

按照我的理解,直接对一个String对象赋值时,会将该值当作匿名对象保存到对象池(String b = a + "world";),
之后还有其他String对象也赋同样的值时(String c = "hello " + "world"; String d = "hello world";),
不会开辟新的内存空间,而是使用已有的对象进行引用的分配(b, c, d 都指向对象池中的同一块地址)。
但是实际效果是: c、d指向同一块内存,b指向另一块内存。
请问其中原理是什么?

分享到
取消 提交回答
全部回答(1)
  • 蛮大人123
    2019-07-17 18:59:29

    Java的运行时数据区中,有一个方法区(Method Area)和堆(Heap)。
    对于那些在编译时就能确定的字面量都会存放在运行时常量池中,而常量池是方法区的一部分

    Compiled from "Main.java"
    public class ffish.top.Main {
      public ffish.top.Main();
        Code:
           0: aload_0       
           1: invokespecial #1                  // Method java/lang/Object."<init>":()V
           4: return        
    
      public static void main(java.lang.String[]);
        Code:
           0: ldc           #2                  // String hello 
           2: astore_1      
           3: new           #3                  // class java/lang/StringBuilder
           6: dup           
           7: invokespecial #4                  // Method java/lang/StringBuilder."<init>":()V
          10: aload_1       
          11: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
          14: ldc           #6                  // String world
          16: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
          19: invokevirtual #7                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
          22: astore_2      
          23: ldc           #8                  // String hello world
          25: astore_3      
          26: ldc           #8                  // String hello world
          28: astore        4
          30: getstatic     #9                  // Field java/lang/System.out:Ljava/io/PrintStream;
          33: aload_2       
          34: aload         4
          36: if_acmpne     43
          39: iconst_1      
          40: goto          44
          43: iconst_0      
          44: invokevirtual #10                 // Method java/io/PrintStream.println:(Z)V
          47: getstatic     #9                  // Field java/lang/System.out:Ljava/io/PrintStream;
          50: aload_3       
          51: aload         4
          53: if_acmpne     60
          56: iconst_1      
          57: goto          61
          60: iconst_0      
          61: invokevirtual #10                 // Method java/io/PrintStream.println:(Z)V
          64: return        
    }

    反编译这段代码。其中ldc指令为加载一个常量到操作数栈。
    02: astore_1 25: astore_3 28: astore 4分别对应a c d三个变量,它们都是通过ldc指令加载进来的,所以c和d指向同一块内存
    22: astore_2 也就是变量b,是通过StringBuilder.toString()的方法生成的

       public String toString() {
            // Create a copy, don't share the array
            return new String(value, 0, count);
        }

    通过查看StringBuilder的toString()的方法,可以看到这里是通过new关键字来生成一个String对象,所以b指向的应该是堆中的字符对象。至于这个堆中的字符串对象和常量池中的字面量的关系,暂时还不太清楚。可能是通过clone的方式,在堆中新生成了一个字符串对象实现的。
    代码中的字符串拼接符号 + ,会被编译器重载为StringBuilderappend()方法以提高性能.

    0 0