我知道我可以在 Java 中做这样的事情:
String foo = 'a' + "bee";
如果我打印foo到控制台,我会得到abee. 这里的幕后究竟发生了什么?我假设'a'正在被提升为String,但我不确定什么规则管理这种类型的转换。据我所知,这似乎不是自动装箱的情况,因为自动装箱从原始类型映射到包装类(例如int-> Integer)。被String认为是 a的包装器char吗?
使这更有趣的一件事是,如果我执行类似的操作,则会
String test = 'a' + 'b';
出现编译错误。我明白,这是因为字符作为整数处理在添加时,虽然看似合理的期望,我会得到这样的事情"ab"在test加入时给出的行为char和String。
您准确命名了使用+运算符进行字符串连接的原因,可以将其视为历史设计错误。提供内置连接运算符并没有错,但它不应该是加号运算符。
除了对不同行为的混淆之外,例如 for'a'+'b'和""+'a'+'b',加号运算符通常被认为是可交换的,即与a + b具有相同的结果b + a,这不适用于字符串连接。此外,运算符优先级可能会导致意外。
15.18.1. 字符串连接运算符 +
如果只有一个操作数表达式是 type
String,则在运行时对另一个操作数执行字符串转换(第5.1.11 节)以生成字符串。字符串连接的结果是对一个
String对象的引用,该对象是两个操作数字符串的连接。在新创建的字符串中,左侧操作数的字符位于右侧操作数的字符之前。
此定义链接到§5.1.11:
5.1.11. 字符串转换
任何类型都可以
String通过字符串转换转换为类型。首先将
x原始类型的值T转换为引用值,就像将其作为参数提供给适当的类实例创建表达式(第15.9 节)一样:
如果
T是boolean,则使用new Boolean(x).如果
T是char,则使用new Character(x).如果
T是byte、short、 或int,则使用new Integer(x)。如果
T是long,则使用new Long(x).如果
T是float,则使用new Float(x).如果
T是double,则使用new Double(x).然后
String通过字符串转换将此引用值转换为类型。现在只需要考虑参考值:
如果引用为
null,则将其转换为字符串“null”(四个 ASCII 字符n、u、l、l)。否则,转换就像通过调用
toString不带参数的引用对象的方法一样执行;但如果调用该toString方法的结果是null,则使用字符串“null”代替。
(规范的格式确实是“ null”而不是"null")
所以 的行为String foo = 'a' + "bee";被指定为就像你写的一样String foo = new Character('a').toString() + "bee";
但引用的第 15.18.1 节继续:
该
String对象是新创建的(第12.5 节),除非表达式是一个常量表达式(第15.28 节)。实现可以选择在一个步骤中执行转换和连接,以避免创建然后丢弃中间
String对象。为了提高重复字符串连接的性能,Java 编译器可以使用StringBuffer类或类似技术来减少String通过表达式求值创建的中间对象的数量。对于原始类型,实现还可以通过直接从原始类型转换为字符串来优化包装对象的创建。
因此,对于你具体的例子,'a' + "bee",的实际行为
String foo = 'a' + "bee";
Run Code Online (Sandbox Code Playgroud)
将会
String foo = "abee";
Run Code Online (Sandbox Code Playgroud)
在运行时没有任何额外的操作,因为它是一个编译时常量。
如果操作数之一不是编译时常量,例如
char c = 'a';
String foo = c + "bee";
Run Code Online (Sandbox Code Playgroud)
从 Java 5 到 Java 8(包括在内)的大多数编译器(如果不是全部)使用的优化变体是
char c = 'a';
String foo = new StringBuilder().append(c).append("bee").toString();
Run Code Online (Sandbox Code Playgroud)
结果行为将始终与指定的一样。
| 归档时间: |
|
| 查看次数: |
165 次 |
| 最近记录: |