Java编程思想__字符串

  • 可以证明,字符串操作是计算机程序设计中最常见的行为。

 

不可变String

  • String 对象时不可变的。查看JDK文档你就会发现,String 类中每一个看起来会修改String值的方法,实际上都是创建了一个全新的String对象,以包含修改后的字符串内容。而最初的String对象则丝毫未动。
public class Immutable {
    static String upcase(String s){
        return s.toUpperCase();
    }
    public static void main(String[] args) {
        String q="howdy";
        System.out.println(q);
        String qq=upcase(q);
        System.out.println(qq);
        System.out.println(q);
    }
}

//运行结果为

howdy
HOWDY
howdy
  1. 当q 传给upcase() 方法时,实际传递的是引用的一个拷贝。其实,每当把String 对象作为方法的参数时,都会复制一份引用,而该引用所指的对象其实一直待在单一的物理位置上,从未动过。
  2. 回到upcase() 的定义,传入其中的引用有了名字s,只有 upcase() 运行的时候,局部引用 s才存在。一旦 upcase() 运行结束,s就消失了。当然了,upcase() 的返回值,其实只有最终结果的引用。
  3. 这足以说明,upcase() 返回的引用已经指向了一个新的对象,而原本的q则还在原地。
String s="asdf";
String x= Immutable.upcase(s);
  1. 难道你真的希望 upcase() 改变其参数吗? 对于一个方法而言,参数是为该方法提供信息的,而不是想让该方法改变自己的。
  2. 在阅读这段代码时,读者自然就会有这样的感觉。这一点很重要,正是有了这种保障,才使得代码易于编写与阅读。

 

重载 "+" 与 StringBuilder

  • String 对象时不可变的,你可以给一个String对象加任意多的别名。因为String 对象具有只读特性,所以指向它的任何引用都不可能改变它的值,因此,也不会对其他的引用有什么影响。
  • 不可变性会带来一定的效率问题。为 String 对象重载的 "+" 操作符就是一个例子。重载的意思是,一个操作符在应用于特定的类时,被赋予了特殊的意义(用于 String 的 "+" 与 "+=" 是Java 中仅有的两个重载过的操作符,而Java并不允许程序员重载任何操作符)。
  • 操作符 "+" 可以用来连接String:(如果不会反编译请参考 https://blog.csdn.net/qq_40646143/article/details/105833724)
  public static void main(java.lang.String[]);
    Code:
       0: ldc           #2                  // String mango
       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 abc
      12: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      15: aload_1
      16: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      19: ldc           #7                  // String def
      21: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      24: bipush        47
      26: invokevirtual #8                  // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
      29: invokevirtual #9                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      32: astore_2
      33: getstatic     #10                 // Field java/lang/System.out:Ljava/io/PrintStream;
      36: aload_2
      37: invokevirtual #11                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      40: return
}
  1. 如果你有汇编语言的经验,以上代码一定看着眼熟,其中的 dup 与 invokevirtural 语句相当于 Java 虚拟机上的汇编语句。即使你完全不了解汇编语言也无需担心,需要注意的重点是: 编译器自动引用了 java.lang.StringBuilder 类
  2. 虽然我们再源代码中并没有使用 StringBuilder ,但是编译器却自作主张地使用了它,因为它更高效。
  3. 在这个例子中,编译器创建了一个StringBuilder对象,用以构造最终的String,并未每个字符串调用一次StringBuilder的append()方法,总计四次。最后调用toString()生成结果,并存为 s(使用的命令为 astore_2)。
  4. 现在,也许你会觉得可以随意使用String 对象,反正编译器会为你自动地优化性能。可是在这之前,让我们更深入地看看编译器能为我们优化到什么程度。如下
public class WhitherStringBuilder {
    String implicit(String[] fields){
        String result="";
        for (int i = 0; i < fields.length; i++) {
            result +=fields[i];
        }
        return result;
    }

    String explicit(String [] fields){
        StringBuilder sb=new StringBuilder();
        for (int i = 0; i < fields.length; i++) {
            sb.append(fields[i]);
        }
        return sb.toString();
    }
}
  1. implicit() 使用javap反编译后如下
  java.lang.String implicit(java.lang.String[]);
    Code:
       0: ldc           #2                  // String
       2: astore_2
       3: iconst_0
       4: istore_3
       5: iload_3
       6: aload_1
       7: arraylength
       8: if_icmpge     38
      11: new           #3                  // class java/lang/StringBuilder
      14: dup
      15: invokespecial #4                  // Method java/lang/StringBuilder."<init>":()V
      18: aload_2
      19: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      22: aload_1
      23: iload_3
      24: aaload
      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_2
      32: iinc          3, 1
      35: goto          5
      38: aload_2
      39: areturn
  1. 注意: 从第8行到35行构成了一个循环体。第8行: 对堆栈中的操作数进行 大于或等于的整数比较运算,循环结束时跳到第38行。第35行: 返回循环体的起始点(第5行)。
  2. 需要注意的重点是: StringBuilder 是在循环之内构造的,这意味着每经过循环一次,就会创建一个新的StringBuilder对象。
  • 接下来是 explicit() 方法对应的反编译字节码。
  java.lang.String explicit(java.lang.String[]);
    Code:
       0: new           #3                  // class java/lang/StringBuilder
       3: dup
       4: invokespecial #4                  // 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 #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      23: pop
      24: iinc          3, 1
      27: goto          10
      30: aload_2
      31: invokevirtual #6                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      34: areturn
  1. 可以看到,不仅循环部分的代码更简短,更简单,而且它只生成了一个StringBuilder 对象。
  2. 显示地创建StringBuilder 还允许为其指定大小。如
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值