字符串(一)——JAVA

字符串操作是计算机程序设计中最常见的行为


1.不可变的String

String对象是不可变的。String类中每一个看起来会修改String值的方法,实际上都只是简单粗暴的创建一个全新的String对象,而最初的String对象是丝毫不动的。

示例:

package thinkingstring;

public class NoChangeStringTest {
	
	public static void main(String[] args) {
		
		
		String oldString = "oldString";
		
		System.out.println(oldString);
		
		String newString = oldString.toUpperCase();
		
		System.out.println("oldString = " + oldString + " newString = " + newString);
	}

}

运行结果

oldString
oldString = oldString newString = OLDSTRING

首先,现在虚拟机常量池中创建“oldString”这个字符串常量,然后把引用复制给oldString这个变量,然后打印输出,同理,把oldString引用的常量大写创建一个全新的变量,然后赋值给newString。

1.1不可变性的带来的效率问题

看如下代码:

	public static void main(String[] args) {
		String mango = "mango";
		String s = "abc" + mango + "def" + 47;
		System.out.println(s);
	}

可以想象一下,String可能有一个append()方法,他会生成一个新的String对象,以包含“abc”与mango连接后的字符串。然后再与“def”相连,生成一个新的String对象,以此类推。这个方式按理来讲可行,但是这样会产生一大堆垃圾.

我们来看看实际情况是怎样的,通过Javap来反编译代码进行查看得到如下结果

Compiled from "NoChangeStringTest.java"
public class thinkingstring.NoChangeStringTest {
  public thinkingstring.NoChangeStringTest();
    Code:
       0: aload_0
       1: invokespecial #8                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: ldc           #16                 // String mango
       2: astore_1
       3: new           #18                 // class java/lang/StringBuilder
       6: dup
       7: ldc           #20                 // String abc
       9: invokespecial #22                 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
      12: aload_1
      13: invokevirtual #25                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      16: ldc           #29                 // String def
      18: invokevirtual #25                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      21: bipush        47
      23: invokevirtual #31                 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
      26: invokevirtual #34                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      29: astore_2
      30: getstatic     #38                 // Field java/lang/System.out:Ljava/io/PrintStream;
      33: aload_2
      34: invokevirtual #44                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      37: return
}

由上可知编译器自动引入了StringBuilder类,我们在源代码并没有使用这个类,在这个例子中,编译器创建了一个StringBuilder对象,用以构造最终的String,并为每个字符串调用一次StringBuilder的append()方法,最后调用toString生成结果,并存在s中。

现在你是不是觉得可以随意使用String对象,反正编译器会帮你进行优化,没错我也是这样想的。but...我们更深入的看看编译器能为我们优化到什么程度。下面采取两种方式生成同一个String进行检验,一使用多个String对象,二在代码中使用StringBuilder。


public String implicit(String[] fields) {
		String result = "";
		for(int i = 0;i < fields.length;i++) {
			result += fields[i];
		}
		return result;
	}
	
	public String explicit(String[] fields) {
		StringBuilder result = new StringBuilder();
		for(int i = 0;i < fields.length;i++) {
			result.append(fields[i]);
		}
		return result.toString();
	}

反编译结果

Compiled from "NoChangeStringTest.java"
public class thinkingstring.NoChangeStringTest {
  public thinkingstring.NoChangeStringTest();
    Code:
       0: aload_0
       1: invokespecial #8                  // Method java/lang/Object."<init>":()V
       4: return

  public java.lang.String implicit(java.lang.String[]);
    Code:
       0: ldc           #16                 // String
       2: astore_2
       3: iconst_0
       4: istore_3
       5: goto          32
       8: new           #18                 // class java/lang/StringBuilder
      11: dup
      12: aload_2
      13: invokestatic  #20                 // Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;
      16: invokespecial #26                 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
      19: aload_1
      20: iload_3
      21: aaload
      22: invokevirtual #29                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      25: invokevirtual #33                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      28: astore_2
      29: iinc          3, 1
      32: iload_3
      33: aload_1
      34: arraylength
      35: if_icmplt     8
      38: aload_2
      39: areturn

  public java.lang.String explicit(java.lang.String[]);
    Code:
       0: new           #18                 // class java/lang/StringBuilder
       3: dup
       4: invokespecial #45                 // Method java/lang/StringBuilder."<init>":()V
       7: astore_2
       8: iconst_0
       9: istore_3
      10: goto          24
      13: aload_2
      14: aload_1
      15: iload_3
      16: aaload
      17: invokevirtual #29                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      20: pop
      21: iinc          3, 1
      24: iload_3
      25: aload_1
      26: arraylength
      27: if_icmplt     13
      30: aload_2
      31: invokevirtual #33                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      34: areturn
}

可以看到如果使用String对象的话,就会出现每次循环创建一次StringBuilder的情况,而使用StringBuilder的话,只会创建一次。所以拼接复杂的字符串的时候还是用StringBuilder比较好,相对简单的可以交给String。

顺带一提,StringBuilder是Java SE5引入的,在这之前Java用的是StringBuffer。后则是线程安全,因此开销会大点。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值