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文档吧,常用的也用得滚瓜烂熟了。接下来会介绍字符串的格式化输出和正则表达式。

时间: 2024-10-12 17:24:35

Java编程思想(十二) —— 字符串(1)的相关文章

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

上篇讲到String的基本用法及StringBuilder和String的比较.继续. 给大家感受一下RednaxelaFX的厉害,他大学看的书. 嗯,这就是去硅谷的水平,所以,还是继续看书吧. 1)格式化输出 确实,说到C的printf,是不能用重载的+操作符的. printf("%d %f", x , y); %d这些为格式修饰符,%d表示整数,x插入到%d的位置,%f表示浮点数,y查到%f的位置. Java也模仿了C: public class TestString { publ

Java编程思想(十八) —— 再谈反射

在Java编程思想(十五) -- 类型信息之反射和Java编程思想(十六) -- 联系JVM再谈Class,书上只用了3页就讲完了,还有讲了那么多Class的东西,接下来要从反射中怎么用,自己结合API和其他资料再写多一些. 示例:Test.java public class Test { public Test() {     }      public Test(int i) {         System.out.println(i);     } private void pri()

【Java编程思想】二、一切都是对象

尽管Java是基于C++的,但是相比之下,Java是一种更“纯粹”的面向对象设计语言. 2.1 用引用操纵对象 一切都被视为对象, 因此可采用单一固定的语法.尽管一切都被看作对象,但操纵的标识符实际上是对象的一个“引用”. Java里的“引用”与C++中的“引用”是有区别的,但是这个概念不需要完全明白,学会处理引用就可以了. 2.2 必须由你创建所有对象 new关键字的意思就是“给我一个新对象”! 2.2.1对象存储到什么地方 1)寄存器 2)堆栈(RAM) 3)堆(RAM) 4)常量存储(RO

Java编程思想(十六) —— 联系JVM再谈Class

编程思想这个专栏停了好久了,主要是把精力放在了其他知识上,现在继续补上. 前面两篇写到RTTI和简单的反射介绍,先回顾一下: RTTI,运行时类型信息,多态的应用,类型转换其实是发生在运行期间. Class对象: 编程思想讲到的定义,Java使用Class对象来执行其RTTI,类是程序的一部分,每个类都有一个Class对象,其实每编写和编译一个新类,就会产生一个Class对象,其实这个对象时被保存在同名的.class文件中的.生成这个类对象,其实是JVM(Java虚拟机)使用了"类加载器&quo

Java编程思想之二十 并发

20.1 并发得多面性 并发编程令人困惑的一个主要原因:使用并发时需要解决的问题有多个,而实现并发的方法也有多种,并且在这两者之间没有明显的映射关系. 20.1.1 更快的执行 速度问题初听起来很简单:如果你需要一个程序运行得更快,那么可以将起断开为多个片段,在单个处理器上运行每个片段. 并发通常是提高运行在单个处理器上的程序的性能,但在单个处理器上运行的并发程序开销确实应该比该程序所有部分都顺序执行开销大,因为其中增加了所谓的上下文切换的代价. 如果没有任务会阻塞,那么在单处理器上使用并发就没

Java编程思想(十五) —— 类型信息之反射

讲完.class,Class之后,继续. 1)泛化的Class引用 Class也可以加入泛型,加入之后会进行类型检查. 贴一下书上原话,Class<?>优于Class,虽然他们是等价的,Class<?>的好处是碰巧或疏忽使用了一个非具体的类引用.我搞不懂这个所谓非具体是什么? 后面弄懂了,其实<?>作为通配符,就是未知的,直接写结论的话不能写个具体类型吧,作者的意思其实就是说加了泛型的Class就是选择了非具体的版本. 加入泛型的原因是提供编译期间的类型检查,操作失误的

《java编程思想》:字符串

1.String对象是不可变的,String类中每个看起来会修改String值的方法,实际上都是创建了一个新的String对象,来包含修改后的内容,所以在对String修改后,想打印新的值,可以直接打印,或者使用变量接受,直接打印原String引用是不会有变化的. 2.通过 "+" 连接String字符串,实际上是编译器自动引入了StringBuilder类,通过append()方法实现的. 3.当为一个类编写toString()方法时,如果字符串操作比较简单,可以直接使用"

Java编程思想笔记(字符串)

1.不可变String:String对象是不变的.String类中每一个看起来会修改String值得方法,实际上都是创建了一个全新的String对象,以包含修改后的字符串内容.而最初的String对象则丝毫未动.(对于一个方法而言,参数是为该方法提供信息的,耳边是想让该方法改变自己的.) 2.重载“+”与StringBuilder

Java编程思想学习(二) 操作符

1. 对象“赋值”:对一个对象进行操作时,我们真正操作的是对对象的引用.所以倘若“将一个对象赋值给另一个对象”,实际是将“引用”从一个地方复制到另一个地方.(引用于对象之间存在关联,但这种关联可以被改变.) 2. == 和 != 比较的是对象的引用. equals() 方法的默认行为是比较引用,如果定义类的对象中对 equals()方法进行重写,则可以实现比较对象的实际内容是否相等的效果. 3. “与” (&&) .“或” (||) .“非” (!) 操作只可应用于布尔值.与在 C 和 C