String 类如何重写 + 运算符?

2022-08-31 08:16:38

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


答案 1

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

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

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

5.1.11. 字符串转换

任何类型都可以逐个字符串转换转换为字符串类型。

基元类型 T 的值 x 首先转换为引用值,就好像将其作为参数提供给适当的类实例创建表达式 (§15.9) 一样:

  • 如果 T 是布尔值,则使用新的布尔值(x)。
  • 如果 T 是字符,则使用新的字符(x)。
  • 如果 T 是 byte、short 或 int,则使用新的 Integer(x)。
  • 如果 T 很长,则使用新的 Long(x)。
  • 如果 T 是 float,则使用新的 Float(x)。
  • 如果 T 是双精度的,则使用新的 Double(x)。

然后,此引用值将转换为逐个字符串转换类型。

现在只需要考虑参考值:

  • 如果引用为 null,则将其转换为字符串“null”(四个 ASCII 字符 n、u、l、l)。
  • 否则,转换就像调用引用对象的toString方法一样执行,没有参数;但是,如果调用 toString 方法的结果是 null,则使用字符串“null”。

toString 方法由原始类 Object (§4.3.2) 定义。许多类会覆盖它,特别是布尔值、字符、整数、长整型、浮点数、双精度型和字符串类。

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

15.18.1.

字符串串联优化:实现可以选择在一个步骤中执行转换和串联,以避免创建然后丢弃中间 String 对象。为了提高重复字符串串联的性能,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


答案 2

它是Java编译器功能,用于检查运算符的操作数。根据操作数,它生成字节码:+

  • 对于 String,它生成代码来连接字符串
  • 对于数字,它生成代码以对数字求和。

这是Java规范所说的

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

加法运算符具有相同的优先级,并且在语法上是左结合的(它们从左到右分组)。如果 + 运算符的任一操作数的类型是 String,则该操作是字符串串联。

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

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