Java 字符串连接运算符干了什么?
云栖号资讯:【点击查看更多行业资讯】
在这里您可以找到不同行业的第一手的上云资讯,还在等什么,快来!
和其他多数程序设计语言一样,Java 语言允许使用 + 连接两个字符串。
String name = "stephen"; String foo = "Hey, " + name;
当我们将一个字符串和一个非字符串的值进行拼接时,并不会报错:
String name = "Stephen"; int age = 25; String foo = name + age; // 结果为 Stephen25
其原因是当 + 运算符左右两边有一个值是字符串时,会将另一个值尝试转化为字符串。
字符串转换机制
我们在了解字符串连接运算符前,先了解一下字符串转换机制(String Conversion)。
Any type may be converted to type String by string conversion.
如果值 x 是基本数据类型 T,那么在字符串转换前,首先会将其转换成一个引用值,举几个例子:
- 如果 T 是 boolean 类型的,那么就会用 new Boolean(x) 封装一下;
- 如果 T 是 char 类型的,那么就会用 new Character(x) 封装一下;
- 如果 T 是 byte、short、int 类型的,那么就会用 new Integer(x) 封装一下;
我们知道,对于基本数据类型,Java 都对应有一个包装类(比如 int 类型对应有 Integer 对象),这样操作以后,每个基础数据类型的值 x 都变成了一个对象的引用。
为什么这么做?为了统一对待,当我们把基础数据类型转换成对应的包装类的一个实例后,所有的值都是统一的对象引用。
此时才开始真正进行字符串转换。我们需要考虑两种情况:空值和非空值。
如果此时的值 x 是 null,那么最终的字符串转换结果就是一个字符串 null;
否则就会调用这个对象的 toString() 的无参方法。
前者很好理解,后者我们一起来看看:
在 Java 所有的父类 Object 中,有一个重要的方法就是 toString 方法,它返回表示对象值的一个字符串。在 Object 类中对 toString 的定义如下:
public String toString() { return getClass().getName() + "@" + Integer.toHexString(hashCode()); }
该方法返回对象的类名和散列码。如果类没有重写 toString 方法,默认就会调用它的父类的 toString 方法,而此时我们的值 x 统一都是对象值,所以一定有 toString 方法可以调用并打印出值(也有个特殊,如果调用 toString 返回的值是一个 null 值,那么就会用字符串 null 代替)。
字符串连接符
当 + 运算符左右两边参与运算的表达式的值有一个为字符串时,那么在程序运行时会对另一个值进行字符串转换。
这里需要注意的是 + 运算符同时作为算术运算符,在含有多个值参与运算的时候,要留意优先级,比如下面这个例子:
String a = 1 + 2 + " equals 3"; String b = "12 eqauls " + 1 + 2;
变量 a 的结果是 3 equals 3,变量 b 的结果是 12 equals 12。
有些人这里可能会有疑问,解释一下,第一种情况根据运算优先级是先计算 1+2 那么此时的 + 运算符是算术运算符,所以结果是 3,然后再和 " equals 3" 运算,又因为 3 + " equals 3" 有一个值为字符串,所以 + 运算符是字符串连接运算符。
在运行时,Java 编译器一般会使用类似 StringBuffer/StringBuilder 这样带缓冲区的方式来减少通过执行表达式时创建的中间 String 对象的数量,从而提高程序性能。
我们可以用 Java 自带的反汇编工具 javap 简单的看一下:
假设有如下这段代码:
public class Demo { public static void main(String[] args) { int i = 10; String words = "stephen" + i; } }
然后编译,再反汇编一下:
javac Demo.java javap -c Demo
可以得到如下内容:
Compiled from "Demo.java" public class Demo { public Demo(); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return public static void main(java.lang.String[]); Code: 0: bipush 10 2: istore_1 3: new #2 // class java/lang/StringBuilder 6: dup 7: invokespecial #3 // Method java/lang/StringBuilder."<init>":()V 10: ldc #4 // String stephen 12: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 15: iload_1 16: invokevirtual #6 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder; 19: invokevirtual #7 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 22: astore_2 23: return }
我们可以发现,Java 编译器在执行字符串连接运算符所在表达式的时候,会先创建一个 StringBuilder 对象,然后将运算符左边的字符串 stephen 拼接(append)上去,接着在拼接右边的整型 10,然后调用 StringBuilder 的 toString 方法返回结果。
如果我们拼接的是一个对象呢?
public class Demo { public static void main(String[] args) { Demo obj = new Demo(); String words = obj + "stephen"; } @Override public String toString() { return "App{}"; } }
一样的做法,我们会发现此时 Method java/lang/StringBuilder.append:(Ljava/lang/Object;) 也就是 StringBuilder 调用的是 append(Object obj) 这个方法,我们查看 StringBuilder 类的 append 方法:
public StringBuilder append(Object obj) { return append(String.valueOf(obj)); }
而 String.valueOf(obj) 的实现代码如下:
public static String valueOf(Object obj) { return (obj == null) ? "null" : obj.toString(); }
也就是会调用对象的 toString() 方法。
可能到这里大家会有一个疑问:上面不是说字符串转换对于基本类型是先转换成对应的包装类,然后调用它的 toString 方法吗,这边怎么都是调用 StringBuilder 的 append 方法了呢?
实现方式不同,其实是本质上是一样的,只不过为了提高性能(减少创建中间字符串等的损耗),Java 编译器采用 StringBuilder 来做。感兴趣的可以自己去追踪下 Integer 包装类的 toString 方法,其实和 StringBuilder 的 append(int i) 方法的代码是几乎一样的。
【云栖号在线课堂】每天都有产品技术专家分享!
课程地址:https://yqh.aliyun.com/live立即加入社群,与专家面对面,及时了解课程最新动态!
【云栖号在线课堂 社群】https://c.tb.cn/F3.Z8gvnK
原文发布时间:2020-06-23
本文作者:是阿亮啊
本文来自:“掘金”,了解相关信息可以关注“掘金”
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
在 Python 中,三个关于可哈希不可不知的问题
云栖号资讯:【点击查看更多行业资讯】在这里您可以找到不同行业的第一手的上云资讯,还在等什么,快来! 作为一种通用的编程语言,Python 为不同的用户场景提供了一系列内置的数据结构。 当你学习 Python 基础知识的时候,你可能在某些地方看到有提及可哈希。例如,你可能会看到 dict 中的键需要是可哈希的(请参见下面代码片段中的一个小示例)。 在另一个例子中,它提到了 set 中的元素需要是可哈希的。 >>> # 一个正确的字典声明 >>> good_dict = {"a": 1, "b": 2} >>> >>> # 一个错误的字典声明 >>> failed_dict = {["a"]: 1, ["b"]: 2} Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: unhashable type: 'list' 你可能会想知道 —— 可哈希到底是什么意思?哪些对...
- 下一篇
2020年,我们该如何学习 WEB 前端开发
作者|小问编辑|橙子君出品|阿里巴巴新零售淘系技术 我们可以把学习路线比作游戏中的段位上分,在不同的分段都有自己的定位和要锻炼的事情: 青铜—从零开始小学生:怀着满腔的热血,看到了这一个行业的希望和未来,准备开始学习 Web 开发知识。 先通过 w3cschool 等免费学习资源把 HTML、CSS 和 JavaScript 的基本操作学会了 写一个简单的表白页面送给你的女/男朋友,展示一下自己努力的成果,如果没有就当我没说 白银 —懵懵懂懂初学者:懂得如何使用 HTML、CSS 和 JavaScript 三大件来实现基本页面开发功能 选择一个可以覆盖多种场景、可以随自己意愿调整难度的项目尝试实现,如博客系统、记账本、Markdown 编辑器等 从 React 和 Vue 这两个框架中选择一个进行学习 黄金—轻车熟路新玩家:懂得使用框架来实现上面
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- SpringBoot2全家桶,快速入门学习开发网站教程
- Eclipse初始化配置,告别卡顿、闪退、编译时间过长
- Hadoop3单机部署,实现最简伪集群
- SpringBoot2更换Tomcat为Jetty,小型站点的福音
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- CentOS7,CentOS8安装Elasticsearch6.8.6
- CentOS6,7,8上安装Nginx,支持https2.0的开启