编程思想:字符串1

String对象是immutable

1、String对象是不可变的,String的每个看起来都会修改对象值的方法都是创建一个新的对象。

2、uppcase传递的参数s是s1这个引用的拷贝,是值传递,而不是直接传引用s1。并且s在uppcase运行结束就消失了,跟原本的对象没有关联,方法 返回的是一个新对象的引用。

3、在代码中,可以创建多个某一个String对象的别名。但是这些别名都是的引用是相同的。 比如s1和s3都是”ljs”对象的别名,别名保存着到真实对象的引用。所以s1 = s3

   public static String upcase(String s){
        return s.toUpperCase();
    }


    public static void main(String[] args) {
        String s1 =  "ljs ai csy";
        System.out.println(s1);
        String s2 = upcase(s1);
        System.out.println(s2);
        System.out.println(s1);

        String s3 = s1;
        System.out.println(s3==s1);
    }

# ljs ai csy
# LJS AI CSY
# ljs ai csy
# true

重载+遇到的问题

1、既然String对象是immutable,那么String对象的重载+就会出现一个问题。(java不允许程序员重载操作符,c++可以),两个以上的String对象拼接必定会产生多余的中间String对象。

2、例如下面要得到ljslovecsy1314,就先生成ljslove这个临时String对象,然后在生成ljslovecsy这个临时String对象,最后才能生成我们想要的对象。这其中的两个临时对象没有主动回收,肯定会占一定的空间,假如有上百个,代价不就很大,性能也就下降。

    public static void main(String[] args) {
        String ljs = "ljs";
        String s = ljs + "love" + "csy" + 1314;
        System.out.println(s);
    }

编译器优化

1、一个Java程序如果想运行起来,需要经过两个时期,编译时和运行时。在编译时,Java 编译器(Compiler)将java文件转换成字节码。在运行时,Java虚拟机(JVM)运行编译时生成的字节码。通过这样两个时期,Java做到了所谓的一处编译,处处运行。

2、java设计者肯定不会这样设计,所以java中仅有的+,+=重载都不是真正的重载, 而是当编译器在遇到+重载的时候会创建一个StringBuilder对象,而后面的拼接会调用StringBuilder的append的方法。这个优化进行在编译器编译.java到bytecode时。

3、我们对上面的javac Demo02.java编译出class文件,然后再javap -c Demo02反编译,其中,ldc,astore等为java字节码的指令,类似汇编指令。后面的注释使用了Java相关的内容进行了说明。可以看到new StringBuilder,并且拼接使用了append方法。

E:\CodeSpace\JavaProject\think_java\out\production\think_java\com\ljs\string>javap -c Demo02
警告: 二进制文件Demo02包含com.ljs.string.Demo02
Compiled from "Demo02.java"
public class com.ljs.string.Demo02 {
  public com.ljs.string.Demo02();
    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 ljs
       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 lovecsy
      16: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      19: sipush        1314
      22: invokevirtual #7                  // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
      25: invokevirtual #8                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      28: astore_2
      29: getstatic     #9                  // Field java/lang/System.out:Ljava/io/PrintStream;
      32: aload_2
      33: invokevirtual #10                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V

只靠编译器优化就够了吗

1、既然编译器为我们做了优化,是不是仅仅依靠编译器的优化就够了呢,当然不是。 下面我们看一段未优化性能较低的代码

    public static String implicit(String[] values) {
        String result = "";
        for (int i = 0; i < values.length; i++) {
            result += values[i];
        }
        return result;
    }

2、反编译一下,其中从第8行到第35行是循环体,8: if_icmpge 38的意思是如果JVM操作数栈的整数对比大于等于(i < values.length的相反结果)成立,则跳到第38行(System.out)。35: goto 5则表示直接跳到第5行。可以看到这里11行的new在循环体里,也就是说每一次循环都会创建一个新的StringBuilder对象。这样也是不好的。

 public static java.lang.String implicit(java.lang.String[]);
    Code:
       0: ldc           #2                  // String
       2: astore_1
       3: iconst_0
       4: istore_2
       5: iload_2
       6: aload_0
       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_1
      19: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      22: aload_0
      23: iload_2
      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_1
      32: iinc          2, 1
      35: goto          5
      38: aload_1
      39: areturn

3、这就得自己优化一下了,记住String才有+=重载,StringBuilder只能调用append方法。现在new是在循环体之外所以不会创建多个StringBuilder对象。这才是真正的代码啊。

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

    }
 public static 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_1
       8: iconst_0
       9: istore_2
      10: iload_2
      11: aload_0
      12: arraylength
      13: if_icmpge     30
      16: aload_1
      17: aload_0
      18: iload_2
      19: aaload
      20: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      23: pop
      24: iinc          2, 1
      27: goto          10
      30: aload_1
      31: invokevirtual #6                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      34: areturn

4、使用StringBuilder的好处,循环代码更短,只生成一个StringBuilder对象,并且使用StringBuilder还可以指定大小,当我传入1时,调试发现当append b的时候需要扩容,然后是调用str.getChars(0, len, value, count)把字符串“b”复制到目标上(0,len是"b"的开始到结尾,value是目标数组,count是从目标数组的什么位置开始复制)。而getchars只是判断这些参数是否合理,最后还是调用System的arraycopy方法,arraycopy是一个native方法。native方法只有签名没有实现。

无意识的递归

1、当String对象后面一个"+",然后再跟着一个非String对象,这时候编译器就会把调用这个非String对象的toString方法把该非String对象自动类型转换为String,如果这发生在自定义的类的重写的toString()方法体内,就有可能发生无限递归,运行时抛出java.lang.StackOverflowError栈溢出异常。

    public static void main(String[] args) {
        ArrayList<Demo04> al = new ArrayList<Demo04>();
        for (int i = 0; i < 10; i++) {
            al.add(new Demo04());
            System.out.println(al);
        }
    }

    @Override
    public String toString() {
        return "Demo04 address" + this + "\n";
    }

2、这时候就不能调用this了,而是得调用super.toString。

toString

1、每个非基本类型对象有一个toString方法,当编译器需要String而你却只有一个对象时,该方法就会被调用。

"source" + source;

小结

1、我们应该避免在循环中隐式或显式的创建StringBuilder对象。特别是当你需要重写toString()方法时,最好自己创建一个StringBuilder。
2、如果不确定哪种方式更好,要多使用javap -c反编译
3、StringBuilder常用的方法,append,toString
4、StringBuilder是java5引进的,之前是StringBuffer,它是线程安全的,比较慢。
5、不要在toString方法里调用this,有可能会造成递归。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,377评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,390评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,967评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,344评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,441评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,492评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,497评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,274评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,732评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,008评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,184评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,837评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,520评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,156评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,407评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,056评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,074评论 2 352

推荐阅读更多精彩内容

  • 废话不多说,自己进入今天的主题 1、面向对象的特征有哪些方面? 答:面向对象的特征主要有以下几个方面: - 抽象:...
    传奇内服号阅读 2,344评论 1 31
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,608评论 18 399
  • 08‖唐老师又把我们叫出去讲了一大堆废话啊,途中还接了三个电话,最近事真多,而且从他的话中可以看出他还是最喜欢杨文...
    绘子啊阅读 277评论 0 0
  • 认识《简书》是从同事的微空间开始的,平时很少闲逛,微信横行了4年,好友说微信使用起来比较方便,几次Q她都没回应我这...
    先生别来无恙呵阅读 196评论 0 0
  • //第三方android应用的META值 public String getPackageMetaValue(Co...
    学习不断阅读 775评论 0 0