我们都知道java中的加号操作符除了加法、表示正数之外,还可以用作字符串的连接。初学java时,你很可能会碰到类似下面的题目:

以下这段代码产生了几个String对象:

String str1 = "abc" + "def";
String str2 = "123" + new String("456");

我还记得以前看过的文章是这样分析的:第一行先产生abc和def,再产生一个新的abcdef,共3个;第二行先产生123,再产生一个456,最后再产生一个123456,共3个;一共产生了6个String对象。

先说结论,上面的分析过程是错误的,这两段代码一共产生了5个String对象。下面是验证过程:

先编译,然后再反编译可以得到下面的代码:

String var1 = "abcdef";
String var2 = "123" + new String("456");

可以看到第一行在编译的阶段就直接把两个字符串常量的连接结果计算出来了,直接在常量池生成一个abcdef字符串,所以只产生了一个String对象;而第二行并没有进行计算,即使我们可以很直观地看出计算结果。在有new的地方,只有在运行阶段才会去动态分配内存,然后进行初始化的,所以第二行会生成4个String对象,即在常量池生成一个123和一个456对象,再在堆中生成456和123456两个String对象。

然后回到字符串相加的问题,上面两行代码都是字符串相加,分别做了什么呢?可以用javap -c反汇编查看指令如下:

0: ldc           #2                  // String abcdef
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 123
12: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
15: new #7 // class java/lang/String
18: dup
19: ldc #8 // String 456
21: invokespecial #9 // Method java/lang/String."<init>":(Ljava/lang/String;)V
24: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
27: invokevirtual #10 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
30: astore_2
31: return

可以看到第一行是在编译阶段就把两个常量字符串相加好了直接生成了字符串常量;第二行代码做的工作比较多,先是生成了一个StringBuilder对象,然后append了123,接着生成一个String对象,值为456,接着再被append到StringBuilder中,最后再调用toString方法得到了最终的字符串。

再看一个例子,下面的代码做了怎样的操作?

String str1 = 1 + 2 + "345";
String str2 = 1 + 2 + new String("345");

用同样的方法,用javap命令对class文件反汇编查看指令:

0: ldc           #2                  // String 3345
2: astore_1
3: new #3 // class java/lang/StringBuilder
6: dup
7: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V
10: iconst_3
11: invokevirtual #5 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
14: new #6 // class java/lang/String
17: dup
18: ldc #7 // String 345
20: invokespecial #8 // Method java/lang/String."<init>":(Ljava/lang/String;)V
23: invokevirtual #9 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
26: invokevirtual #10 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
29: astore_2
30: return

第一行依然是在编译阶段就完成了计算;第二行还是使用了StringBuilder对象,先在编译阶段计算了1+2的值,然后再用append方法拼接后toString获得结果。

String类对象相加时做了什么的更多相关文章

  1. 反射消除String类对象的不可变特性

    大家都知道,在JAVA中字符串一旦声明就不可改变,如果尝试修改字符串的内容,将会重新实例化一个新的字符串对象,这也是为了安全性和效率. 由于字符串在程序之中被大量使用,所以JAVA引入了一个字符串常量 ...

  2. java笔记--String类对象解析与运用

    --如果朋友您想转载本文章请注明转载地址"http://www.cnblogs.com/XHJT/p/3877236.html "谢谢-- 1.String中的equals和==的 ...

  3. String类对象两种实例化方式比较

    第一种:直接赋值 String str =  "hello!" ; 在java中,有一个字符串常量池,对于这种直接赋值的,会直接写进常量池(常量池里面不存在其value,) 自JD ...

  4. JAVA笔记3__字符串String类/对象一对一关联

    import java.lang.String; import java.util.Scanner; public class Main { public static void main(Strin ...

  5. c++中string类对象和字符数组之间的相互转换

    string类在c++中是一个模板类,位于名字空间std中,注意这里不是string.h,string.h是C字符串头文件. 将string类型转换为字符数组char arr[10];string s ...

  6. String类的写时拷贝

    #include<iostream>using namespace std; class String;ostream& operator<<(ostream & ...

  7. String类对象的比较

    1.字符串比较,是按照字符串(String)中每一个字符(char)的字段表顺序进行比较 /** * Compares two strings lexicographically(字典序,按照字典顺序 ...

  8. Scanner类、匿名对象、Random类、ArrayList集合、String类、static静态类、math类和Arrays工具类

    一.Scanner类 1.除了八种基本数据类型,其他都是引用类型: 引用类型使用三步骤: 2.Scanner类 引用jdk提供的类,Scanner在java.util包下,不在java.lang包(S ...

  9. 跟着刚哥梳理java知识点——深入理解String类(九)

    一.String类 想要了解一个类,最好的办法就是看这个类的实现源代码,来看一下String类的源码: public final class String implements java.io.Ser ...

随机推荐

  1. TLS实现代码段加密

    刚开始见到这个思路是看到周大师用这个东西做的免杀,当时感觉这个想法很好,但是由于当时对PE结构了解的少,看到二进制的东西就打怵,所以当时也没能成功的去实现这个思路,只是简单的记录了一下TLS的特性,直 ...

  2. 通过 Netty、ZooKeeper 手撸一个 RPC 服务

    说明 项目链接 微服务框架都包括什么? 如何实现 RPC 远程调用? 开源 RPC 框架 限定语言 跨语言 RPC 框架 本地 Docker 搭建 ZooKeeper 下载镜像 启动容器 查看容器日志 ...

  3. 7个IntelliJ IDEA必备插件,提高编码效率

    1. FindBugs-IDEA 作用: 检测代码中可能的bug及不规范的位置,写完代码后检测下 避免低级bug. 静态分析工具承诺无需开发人员费劲就能找出代码中已有的缺陷.当然,如果有多年的编写经验 ...

  4. thinkphp之独立日志(tp5.1)

    为了便于分析,File类型的日志还支持设置某些级别的日志信息单独文件记录,以error类型的日志为例,例如: 1.在log.php 中配置 'apart_level' => [ 'error' ...

  5. 从执行上下文(ES3,ES5)的角度来理解"闭包"

    目录 介绍执行上下文和执行上下文栈概念 执行上下文 执行上下文栈 伪代码模拟分析以下代码中执行上下文栈的行为 代码模拟实现栈的执行过程 通过ES3提出的老概念-理解执行上下文 1.变量对象和活动对象 ...

  6. 将mysql数据同步到ES6.4(全量+增量)

    下载安装包时注意下载到指定文件夹 这里我放在OPT文件夹下一:安装logstash进入到opt文件夹打开终端 执行以下命令wget -c https://artifacts.elastic.co/do ...

  7. OO第一单元总结-多项式求导

    OO第一单元总结-多项式求导 一.第一.第二次作业总结 因为前两次作业设计复杂度差别不大,因而放在这里统一总结. 基于度量分析程序结构: 前两次作业确实存在缺乏可拓展设计的构想,基本还是面向过程的思维 ...

  8. docker-compose如何动态配置springboot项目的application.yml的配置

    假如我们再springboot的工程中有配置文件 方式1: application.properties里面存在环境变量: #配置数据库链接 spring.datasource.url = jdbc: ...

  9. JAVA基础——包机制

    包机制 包的语法格式package pkg1[.pkg2[.pkg3...]] 一般利用 公司域名倒置 作为包名; 例如www.baidu.com,则建立报的名字com.baidu.www 一般不要让 ...

  10. [笔记] 《c++ primer》显示器程序 Chapter7

    补充Sales_data没有体现出的其他类特性 Screen.h 1 #include <string> 2 #include <iostream> 3 4 class Scr ...