String 类如何覆盖 + 运算符?

为什么在 Java 中可以使用 + 运算符添加 String,而 String 是一个类?在 String.java代码中,我没有找到这个操作符的任何实现。这个概念是否违反了面向对象的原则?

33817 次浏览

检查 +操作符的操作数是 Java 编译器的一个特性。它根据操作数生成字节码:

  • 对于 String,它生成连接字符串的代码
  • 对于 Numbers,它生成用于添加数字的代码。

这就是 Java 规范所说的 :

运算符 + 和 -称为加法运算符。 表达式: 乘法表达式 加法表达 + 乘法表达 加性表达-乘性表达

相加运算符具有相同的优先级,并且在语法上是相同的 左结合(他们从左到右分组)。 < b > 如果 +操作符的操作数是 String,那么操作就是字符串串联。

否则,+运算符的每个操作数的类型必须是 将(5.1.8)转换为基元数值类型,或者发生编译时错误。

在每种情况下,二进制 -运算符的每个操作数的类型必须是 可转换(5.1.8)为基元数字类型的类型,或 发生编译时错误。

+操作符通常在编译时由 StringBuilder替换。查看这个 回答了解关于这个问题的更多细节。

Java 语言为字符串连接操作符(+)和将其他对象转换为字符串提供了特殊支持。字符串串联是通过 StringBuilder(或 StringBuffer)类及其 append方法实现的。

首先(+)是重载的,而不是重写的

Java 语言为字符串提供了特殊的支持 连接操作符(+) ,它已经被 Java 字符串重载 物品。

  1. 如果左边的操作数是 String,它将作为串联操作。

  2. 如果左边的操作数是整数,它作为加法运算符

让我们看看下面 Java 中的简单表达式

int x=15;
String temp="x = "+x;

编译器在内部将 "x = "+x;转换为 StringBuilder,并使用 .append(int)将整数“添加”到字符串中。

5.1.11字符串转换

任何类型都可以通过字符串转换转换为 String 类型。

首先将基元类型 T 的值 x 转换为引用值 就好像把它作为一个参数给一个合适的类实例 创造表达式(15.9) :

  • 如果 T 是布尔型的,那么使用新的布尔型(x)。
  • 如果 T 是 char,那么使用新字符(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”(4个 ASCII 字符 n、 u、 l、 l)。
  • 否则,转换就像是通过调用被引用对象的 toString 方法执行的,不带任何参数; 但是 如果调用 toString 方法的结果为 null,则 字符串“ null”被替代。

ToString 方法由原始类 Object 定义 (4.3.2)。许多类覆盖它,特别是布尔,字符, 整数,长数,浮点数,双数和字符串。

有关字符串转换上下文的详细信息,请参阅5.4。

15.18.1.

字符串串联的优化: 实现可以选择执行转换和连接 在一个步骤中避免创建然后丢弃中间体 提高重复字符串的性能 连接,Java 编译器可以使用 StringBuffer 类或 类似的技术来减少中间字符串对象的数量 通过对表达式求值创建的。

对于基元类型,实现还可以优化消除 通过直接从原语转换来创建包装器对象 键入字符串。

优化后的版本实际上不会首先执行完全包装的 String 转换。

这是编译器使用的优化版本的一个很好的例子,虽然没有转换原语,但是您可以看到编译器在后台将内容更改为 StringBuilder:

Http://caprazzi.net/posts/java-bytecode-string-concatenation-and-stringbuilder/


这个 java 代码:

public static void main(String[] args) {
String cip = "cip";
String ciop = "ciop";
String plus = cip + ciop;
String build = new StringBuilder(cip).append(ciop).toString();
}

生成这个——看看这两种连接样式是如何导致完全相同的字节码的:

 L0
LINENUMBER 23 L0
LDC "cip"
ASTORE 1
L1
LINENUMBER 24 L1
LDC "ciop"
ASTORE 2


// cip + ciop


L2
LINENUMBER 25 L2


NEW java/lang/StringBuilder
DUP
ALOAD 1
INVOKESTATIC java/lang/String.valueOf(Ljava/lang/Object;)Ljava/lang/String;
INVOKESPECIAL java/lang/StringBuilder.<init>(Ljava/lang/String;)V
ALOAD 2
INVOKEVIRTUAL java/lang/StringBuilder.append(Ljava/lang/String;)Ljava/lang/StringBuilder;
INVOKEVIRTUAL java/lang/StringBuilder.toString()Ljava/lang/String;


ASTORE 3


// new StringBuilder(cip).append(ciop).toString()


L3
LINENUMBER 26 L3


NEW java/lang/StringBuilder
DUP
ALOAD 1
INVOKESPECIAL java/lang/StringBuilder.<init>(Ljava/lang/String;)V
ALOAD 2
INVOKEVIRTUAL java/lang/StringBuilder.append(Ljava/lang/String;)Ljava/lang/StringBuilder;
INVOKEVIRTUAL java/lang/StringBuilder.toString()Ljava/lang/String;


ASTORE 4
L4
LINENUMBER 27 L4
RETURN

查看上面的示例以及如何生成基于给定示例中的源代码的字节代码,您将能够注意到编译器已经在内部转换了以下语句

cip+ciop;

进入

new StringBuilder(cip).append(ciop).toString();

换句话说,字符串串联中的操作符 +实际上是更详细的 StringBuilder习惯用法的简写。

String 类如何覆盖 + 运算符?

严格来说,编译器 超载是 String 操作数的 + 运算符。

+操作符应用于 String时的含义是由语言定义的,因为每个人都已经写过了。既然你似乎不觉得这足以令人信服,那么考虑一下:

整数、浮点数和双精度数都有不同的二进制表示,因此在位操作方面,添加两个整数与添加两个浮点数是不同的操作: 对于整数,你可以一位一位地添加,带一位并检查溢出; 对于浮点数,你必须分别处理尾数和指数。

因此,原则上,“加法”取决于被“加法”对象的性质。Java 为 String 以及 int 和 float (longs、 double、 ...)定义了它