网站首页 > 精选教程 正文
和其他多数程序设计语言一样,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) 方法的代码是几乎一样的。
猜你喜欢
- 2024-11-03 4.5 Java的字符串操作和方法 java字符串使用教学
- 2024-11-03 一文教你Java字符串处理(String,StringBuffer...
- 2024-11-03 「Java工具类」Jackson工具类,json字符串转各种对象或者集合
- 2024-11-03 java int类型转char类型总结 java将int转换为char
- 2024-11-03 《JAVA编程思想》5分钟速成:第13章(字符串)
- 2024-11-03 Java字符串操作的函数式方法 java中字符串的操作
- 2024-11-03 Java基于代码实战的字符流父类、字符转换流、缓冲字符流、过滤流
- 2024-11-03 7. 反转整数(LeetCode 题解) 反转整数python
- 2024-11-03 JAVA 基本类型之间的转换(新手) java基础类型转换
- 2024-11-03 Java读取制表符文本转换为JSON java读取表格
你 发表评论:
欢迎- 04-11Java面试“字符串三兄弟”String、StringBuilder、StringBuffer
- 04-11Java中你知道几种从字符串中找指定的字符的数量
- 04-11探秘Java面试中问的最多的String、StringBuffer、StringBuilder
- 04-11Python字符串详解与示例(python字符串的常见操作)
- 04-11java正则-取出指定字符串之间的内容
- 04-11String s1 = new String("abc");这句话创建了几个字符串对象?
- 04-11java判断字符串中是否包含某个字符
- 04-11关于java开发中正确的发牌逻辑编写规范
- 最近发表
-
- Java面试“字符串三兄弟”String、StringBuilder、StringBuffer
- Java中你知道几种从字符串中找指定的字符的数量
- 探秘Java面试中问的最多的String、StringBuffer、StringBuilder
- Python字符串详解与示例(python字符串的常见操作)
- java正则-取出指定字符串之间的内容
- String s1 = new String("abc");这句话创建了几个字符串对象?
- java判断字符串中是否包含某个字符
- 关于java开发中正确的发牌逻辑编写规范
- windows、linux如何后台运行jar(并且显示进程名)
- 腾讯大佬私人收藏,GitHub上最受欢迎的100个JAVA库,值得学习
- 标签列表
-
- nginx反向代理 (57)
- nginx日志 (56)
- nginx限制ip访问 (62)
- mac安装nginx (55)
- java和mysql (59)
- java中final (62)
- win10安装java (72)
- java启动参数 (64)
- java链表反转 (64)
- 字符串反转java (72)
- java逻辑运算符 (59)
- java 请求url (65)
- java信号量 (57)
- java定义枚举 (59)
- java字符串压缩 (56)
- java中的反射 (59)
- java 三维数组 (55)
- java插入排序 (68)
- java线程的状态 (62)
- java异步调用 (55)
- java中的异常处理 (62)
- java锁机制 (54)
- java静态内部类 (55)
- java怎么添加图片 (60)
- java 权限框架 (55)
本文暂时没有评论,来添加一个吧(●'◡'●)