首页 > 代码库 > Java编程思想(十二) —— 字符串(1)

Java编程思想(十二) —— 字符串(1)

字符串在编程中也是经常使用的。


1)不可变

其实查看API就会发现:

public final class String
extends Object
implements Serializable, Comparable<String>, CharSequence

final,不可变的。


public class TestString {
    static String upcase(String s){
        return s.toUpperCase();
    }
    public static void main(String[] args) {
        String s = "bbc";
        String ss = upcase(s);
        System.out.println(s);
    }
}

这里虽然s传参给了upcase方法,但是s并没有改变,传参的时候,s会复制一份引用,但是引用所指的对象却没有移动过。


2)重载“+”与StringBuilder

其实,StringBuilder和StringBuffer的东西,在面试笔试题里面已经看过很多遍了,但是,但是,每次都会忘记,这属性的东西,虽然一测试可以知道效率问题,但是直接回答还是凭借记忆。前段时间在知乎看到一个在外国工作的女程序员妹纸用了英文的记忆,当时记住了,答案现在找不到。


public class Test{
        public static void main(String[] args) {
        String s = "bbc";
       
        String ss = "ddt"+s+"sdf"+3;
        System.out.println(ss);
    }
}


F:\>javac Test.java
F:\>javap -c Test
Compiled from "Test.java"
public class Test {
  public Test();
    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 bbc
       2: astore_1
       3: new           #3                  // class java/lang/StringBuilder
       6: dup
       7: invokespecial #4                  // Method java/lang/StringBuilder."<
init>":()V
      10: ldc           #5                  // String ddt
      12: invokevirtual #6                  // Method java/lang/StringBuilder.ap
pend:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      15: aload_1
      16: invokevirtual #6                  // Method java/lang/StringBuilder.ap
pend:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      19: ldc           #7                  // String sdf
      21: invokevirtual #6                  // Method java/lang/StringBuilder.ap
pend:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      24: iconst_3
      25: invokevirtual #8                  // Method java/lang/StringBuilder.ap
pend:(I)Ljava/lang/StringBuilder;
      28: invokevirtual #9                  // Method java/lang/StringBuilder.to
String:()Ljava/lang/String;
      31: astore_2
      32: getstatic     #10                 // Field java/lang/System.out:Ljava/
io/PrintStream;
      35: aload_2
      36: invokevirtual #11                 // Method java/io/PrintStream.printl
n:(Ljava/lang/String;)V
      39: return
}

作者还真是厉害,反编译class,出来一大堆汇编,之前学过8086的看起来还是挺面熟的,代码里面发现竟然自己调用了StringBuilder。因为跟作者所说的思路,如果+是String的append反复,那么每每加上一个之后就会有新对象,最后产生很多没用的对象。


竟然默认调用了StringBuilder,那么是不是可以全部都用重载+呢?

public class Test{
   public String plus(String[] array){
        String s = null;
        for (int i = 0; i < array.length; i++) {
            s += array[i]; 
        }
        return s;
    }
    
    public String builder(String[] array){
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < array.length; i++) {
            sb.append(array[i]);
        }
        return sb.toString();
    }
}

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

  public java.lang.String plus(java.lang.String[]);
    Code:
       0: aconst_null
       1: astore_2
       2: iconst_0
       3: istore_3
       4: iload_3
       5: aload_1
       6: arraylength
       7: if_icmpge     37
      10: new           #2                  // class java/lang/StringBuilder
      13: dup
      14: invokespecial #3                  // Method java/lang/StringBuilder."<
init>":()V
      17: aload_2
      18: invokevirtual #4                  // Method java/lang/StringBuilder.ap
pend:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      21: aload_1
      22: iload_3
      23: aaload
      24: invokevirtual #4                  // Method java/lang/StringBuilder.ap
pend:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      27: invokevirtual #5                  // Method java/lang/StringBuilder.to
String:()Ljava/lang/String;
      30: astore_2
      31: iinc          3, 1
      34: goto          4
      37: aload_2
      38: areturn

  public java.lang.String builder(java.lang.String[]);
    Code:
       0: new           #2                  // class java/lang/StringBuilder
       3: dup
       4: invokespecial #3                  // Method java/lang/StringBuilder."<
init>":()V
       7: astore_2
       8: iconst_0
       9: istore_3
      10: iload_3
      11: aload_1
      12: arraylength
      13: if_icmpge     30
      16: aload_2
      17: aload_1
      18: iload_3
      19: aaload
      20: invokevirtual #4                  // Method java/lang/StringBuilder.ap
pend:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      23: pop
      24: iinc          3, 1
      27: goto          10
      30: aload_2
      31: invokevirtual #5                  // Method java/lang/StringBuilder.to
String:()Ljava/lang/String;
      34: areturn
}

有没有发现,有默认的空参数的构造器Test();

plus方法,34 goto 4,4是i的初始化,然后再接下去运行,比较,不停地跳转,StringBuilder在循环中不停的构建。这样就有很多的StringBuilder对象浪费了。注意10那里,new的创建。


在看看,反汇编之后的build方法,循环中是没有new东西的,就只有一个StringBuilder对象。


这样分析之后,明显StringBuider在不停地对字符串进行操作是效率更高资源占用少的,如果只是简单的几个字符串合并,那可以用+。


这个例子用了反汇编之后看起来简单好多,其实有些比较是通过时间计算,当然也是可以的。


StringBuilder是在SE5版本引入,之前用的是StringBuffer,这笔试题经常有的东西,出现了,StringBuffer是线程安全的,这样开销就比StringBuilder大,所以不存在线程问题的时候,使用StringBuilder。

可以看看下面那个网址,这是知乎上的一个叫程序猎人写的(由化学读软件,这是一个很牛逼的选择)。里面就是StringBuffer VS StringBuilder。

https://gist.github.com/programus/1978494


这一看发现GitHub原来还有Gist这样好用的东西。类似于保存零散代码的仓库。



3)常用方法

String实现三个接口。

CharSequence:

charAt(int i), 返回字符串中该索引指向的字符。

length,字符串长度返回。

subSequence(int start, int end) ,返回索引从start开始end结束的字符串。

toString().


Comparable:

compareTo(T o) ,x.compareTo(y),这样比较。



其他常用的方法自己查JDK文档吧,常用的也用得滚瓜烂熟了。接下来会介绍字符串的格式化输出和正则表达式。

Java编程思想(十二) —— 字符串(1)