String之String的定义方法

直接定义过程:

String s1="myString";

在程序编译期,编译程序先去字符串常量池检查,是否存在“myString”,如果不存在,则在常量池中开辟一个内存空间存放“myString”;如果存在的话,则不用重新开辟空间。然后在栈中开辟一块空间,命名为“s1”,存放的值为常量池中“myString”的内存地址。

通过关键字new定义过程:

String s2=new String("myString");

在程序编译期,编译程序先去字符串常量池检查,是否存在“myString”,如果不存在,则在常量池中开辟一个内存空间存放“myString”;如果存在的话,则不用重新开辟空间,保证常量池中只有一个“myString”常量,节省内存空间。然后在内存堆中开辟一块空间存放new出来的String实例,在栈中开辟一块空间,命名为“s1”,存放的值为堆中String实例的内存地址,这个过程就是将引用s1指向new出来的String实例。

Java的字符串连接符

还有一种就是连接。

String str0 = "a";
String str1 = str0 + "b";

+的处理 

让我们看看编译器是怎么处理 + 操作符的。下面是上一个程序片段编译后的字节码指令

  public static void main(java.lang.String[]);
    Code:
       0: ldc           #2                  // String a
       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 b
      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: return
}

通过阅读这段字节码,可以发现,我们的+拼接操作实际上被编译器理解成了这个样子:

String str0 = "a";
StringBuilder sb = new StringBuilder();
sb.append(str0).append("b");
String str1 = sb.toString();

 实际上是通过StringBuilder来实现的,要注意的是最后还有toString,返回的是一个String对象

如果是这样:

String str0 = "a";
for (int i = 0; i < 10000; i++) {    
      str0 += "a";
}

他的字节码:

  public static void main(java.lang.String[]);
    Code:
       0: ldc           #2                  // String a
       2: astore_1
       3: iconst_0
       4: istore_2
       5: iload_2
       6: sipush        10000
       9: if_icmpge     38
      12: new           #3                  // class java/lang/StringBuilder
      15: dup
      16: invokespecial #4                  // Method java/lang/StringBuilder."<init>":()V
      19: aload_1
      20: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      23: ldc           #2                  // String a
      25: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      28: invokevirtual #6                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      31: astore_1
      32: iinc          2, 1
      35: goto          5
      38: return
}

5~35是我们的循环体,在循环体里面,每次拼接都会生成一个StringBuilder的临时对象,那么这个程序片段执行下去就会产生10000个StringBuilder的临时对象,这10000个临时对象都是必要的吗?显然不是,我们可以在循环体外直接创建一个StringBuilder对象,然后在循环体中通过append方法拼接字符串,这样就省下了创建并回收10000个临时对象的消耗。 

因此,当我们大量使用字符串拼接的时候,还是使用StringBuilder比较好。

字符串拼接

  • 使用字符串连接符拼接 : String s2=”se”+”cond”;
  • 使用字符串加引用拼接 : String s12=”first”+s2;
  • 使用new String(“”)创建 : String s3 = new String(“three”);
  • 使用new String(“”)拼接 : String s4 = new String(“fo”)+”ur”;
  • 使用new String(“”)拼接 : String s5 = new String(“fo”)+new String(“ur”);

  • s2 :这个在编译期间就自动进行了优化的,在常量池中存储一个"second",并且s2指向它。

  • s12 : JVM对于字符串引用,由于在字符串的”+”连接中,有字符串引用存在,而引用的值在程序编译期是无法确定的,即("first"+s2)无法被编译器优化,只有在程序运行期来动态分配使用StringBuilder连接后的新String对象赋给s12。 
    (编译器创建一个StringBuilder对象,并调用append()方法,最后调用toString()创建新String对象,以包含修改后的字符串内容),常量池中并没有产生新的字符串常量。

  • s3 : 用new String() 创建的字符串不是常量,不能在编译期就确定,所以new String() 创建的字符串不放入常量池中,它们有自己的地址空间。 
    但是”three”字符串常量在编译期也会被加入到字符串常量池(如果不存在的话)

  • s4 : 同样不能在编译期确定,但是”fo”和”ur”这两个字符串常量也会添加到字符串常量池中,并且在堆中创建String对象。(字符串常量池并不会存放”four”这个字符串)

  • s5 : 原理同s4。

参考:https://www.cnblogs.com/carsonwuu/p/9752949.html 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值