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_01: invokespecial #1                  // Method java/lang/Object."<init>":()V4: returnpublic static void main(java.lang.String[]);Code:0: bipush        102: istore_13: new           #2                  // class java/lang/StringBuilder6: dup7: invokespecial #3                  // Method java/lang/StringBuilder."<init>":()V10: ldc           #4                  // String stephen12: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;15: iload_116: invokevirtual #6                  // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;19: invokevirtual #7                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;22: astore_223: 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";}@Overridepublic 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
本文作者:是阿亮啊
本文来自:“掘金”,了解相关信息可以关注“掘金”

原文链接
本文为云栖社区原创内容,未经允许不得转载。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/515649.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

文件上传问题org.springframework.web.HttpMediaTypeNotSupportedException: Content type ‘multipart/form-data;

接收表单使用对象接收时不要使用注解 /*** 单文件上传&#xff08;微信小程序只支持单文件上传&#xff09;** param file* return* throws Exception*/PostMapping("/filesUpload")public R uploadFile(FormDataReq f, RequestParam("file") MultipartFil…

灵魂拷问:a = 1 + 2 究竟是怎么被 CPU 执行的

来源 | 小林coding作者 | 小林coding头图 | 下载于视觉中国代码写了那么多&#xff0c;你知道 a 1 2 这条代码是怎么被 CPU 执行的吗&#xff1f;软件用了那么多&#xff0c;你知道软件的 32 位和 64 位之间的区别吗&#xff1f;再来 32 位的操作系统可以运行在 64 位的电脑上…

如何利用全站加速,提升网站性能和用户体验?

随着网络技术的发展&#xff0c;越来越多的应用基于互联网发布&#xff0c;再好的应用&#xff0c;如果打开速度慢&#xff0c;10个用户会有9个用户选择离开&#xff0c;相关统计数据显示&#xff0c;每增加0.1秒的加载延迟&#xff0c;将会导致客户活跃度下降1%。在目前获客成…

IDEA 搭建 SpringBoot + Maven + Oracle + Hibernate 项目框架

1.Spring Initializer 创建空项目 2.安装Oracle 3.查询Oracle版本 select * from product_component_version;4.安装Oracle驱动并引入pom 5.添加yml配置文件 server:port: 8989 spring:datasource:driver-class-name: oracle.jdbc.OracleDriverurl: jdbc:oracle:thin:127.0.0…

1418 -This function has none of DETERMINISTIC, NO SQL, or READS SQL DATA in its declaration

今天在mysql中创建函数的时候&#xff0c;报错如下&#xff1a; ERROR 1418 (HY000): This function has none of DETERMINISTIC, NO SQL, or READS SQL DATA in its declaration and binary logging is enabled (you might want to use the less safe log_bin_trust_function_…

最近,老王又Get了CDN的新技能

原文链接 本文为云栖社区原创内容&#xff0c;未经允许不得转载。

线程池 总结

文章目录线程池优点线程池创建参数队列种类同步移交队列有界队列无界队列拒绝策略DiscardPolicyAbortPolicyDiscardOldestPolicyCallerRunsPolicy线程池执行流程线程池类型定长 FixedThreadPool轮询 ScheduledThreadPool缓存 CachedThreadPool单线程 SingleThreadPool线程池优点…

阿里的 RocketMQ 如何让双十一峰值之下0故障

作者 | 愈安来源 | 阿里巴巴中间件头图 | 下载于视觉中国2020 年的双十一交易峰值达到 58.3 W笔/秒&#xff0c;消息中间件 RocketMQ 继续数年 0 故障丝般顺滑地完美支持了整个集团大促的各类业务平稳。相比往年&#xff0c;消息中间件 RocketMQ 发生了以下几个方面的变化&…

OAM深入解读:使用OAM定义与管理Kubernetes内置Workload

作者 | 周正喜 阿里云技术专家 爱好云原生&#xff0c;深度参与 OAM 社区 大家都知道&#xff0c;应用开放模型 Open Application Model&#xff08;OAM&#xff09; 将应用的工作负载&#xff08;Workload&#xff09;分为三种 —— 核心型、标准型和扩展型&#xff0c;这三…

oracle 指定类型和指定位数创建序列号

文章目录一、脚本部分1. 表结构2. 函数二、代码部分2.1. xml2.2. 接口2.3. api接口2.4. api实例2.5. 控制层三、测试3.1. 效果图一、脚本部分 1. 表结构 有注释 -- Create table create table LDMAXNO (NOTYPE VARCHAR2(17) not null,NOLIMIT VARCHAR2(12) not null,MAXNO …

深入解读Flink资源管理机制

作者&#xff1a;宋辛童&#xff08;五藏&#xff09;整理&#xff1a;王文杰&#xff08;Flink 社区志愿者&#xff09; 摘要&#xff1a;本文根据 Apache Flink 系列直播整理而成&#xff0c;由阿里巴巴高级开发工程师宋辛童分享。文章主要从基本概念、当前机制与策略、未来发…

EasyExcel 实现模板导出、模板导入分析功能

文章目录0.POM依赖1.导出模板实现2.导入模板并分析实现3.git源码0.POM依赖 <dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><dependency>…

金蝶云拿下客户满意度第一,中国SaaS企业觅得“后发先至”良机

本月&#xff0c;全球SaaS行业迎来了开年第一份重磅奖项的揭晓&#xff1a;由国际知名研究机构IDC颁发的SaaS行业全球客户满意度奖&#xff08;CSAT大奖&#xff09;。 该奖项基于IDC SaaSPath针对全球约2000家组织机构中高层的调研&#xff0c;综合30多项客户满意度指标&…

一名创业者浴火涅磐的自白——对话阿里云MVP孙琦

云栖号资讯&#xff1a;【点击查看更多行业资讯】 在这里您可以找到不同行业的第一手的上云资讯&#xff0c;还在等什么&#xff0c;快来&#xff01; 简介&#xff1a; 孙琦喜欢调侃自己为“一个失败的创业者”。跟他聊过之后&#xff0c;我却发现他跟以往的创业者不同&#x…

mysql 指定类型和指定位数创建序列号

文章目录一、脚本部分1. 表结构2. 函数二、代码部分2.1. xml2.2. 接口2.3. api接口2.4. api实例2.5. 控制层三、测试3.1. 效果图一、脚本部分 1. 表结构 有注释 CREATE TABLE ldmaxno (notype varchar(60) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT 号码…

系统架构设计师 - ESB 企业服务总线

文章目录定义功能定义 传统中间件技术和WEB、XML相结合的产物&#xff0c;主要用以异构系统的集成。具备复杂数据的传输能力&#xff0c;支持基于内容的消息路由、过滤&#xff0c;并提供一系列标准的接口。 功能 服务位置透明、 消息路由、 消息增强、 消息格式转换、 传输…

阿里云助力完美日记半年内系统吞吐量提升50倍

阿里云 Redis 直播地址 近年来&#xff0c;完美日记的“小黑钻口红”“动物眼影盘”等爆款彩妆出现在了越来越多女孩子的化妆台上&#xff0c;完美日记&#xff08;Perfect Diary&#xff09;是由逸仙电商在2017年推出的彩妆品牌&#xff0c;凭借着高颜值和性价比&#xff0c;完…

唏嘘!程序员,你的年底KPI完不成的原因找到了!

加班是每个互联网人不愿面对而却又绕不过去的话题&#xff0c;就连面试时“你怎么看待加班”的问题都成了必答题。现在临近年底&#xff0c;大家都在努力冲业绩&#xff0c;期待拿更多的年终奖&#xff0c;回家过个“富足年”。年底冲业绩&#xff0c;势必会增加我们的工作量&a…

阿里云SAG2.0发布,助力企业全球互联

2016年以来&#xff0c;阿里云洛神云网络陆续发布了高速通道、VPN网关、云企业网CEN&#xff08;cloud enterprise network&#xff09;、SAG&#xff08;smart access gateway&#xff09;等混合云网络产品&#xff0c;基于阿里云全球核心网络不断扩展云网络的应用场景&#x…

RPC 中 参数传递 ImputStream 流会关闭

文章目录非RPC&#xff0c;本地方法调用时&#xff1a;RPC调用&#xff08;涉及到注册中心&#xff0c;Feign等方式&#xff09;时&#xff1a;RPC调用中&#xff0c;可将InputStream转为byte[] 进行参数传递InputStream 转 byte []实际工作中遇到很多需要对提交的附件处理的场…