String类如何覆盖+运算符?

Poo*_*oya 127 java string object

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

Lio*_*ion 155

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

int x=15;
String temp="x = "+x;
Run Code Online (Sandbox Code Playgroud)

编译器"x = "+x;StringBuilder内部转换为内部并用于.append(int)将整数"添加"到字符串.

5.1.11.字符串转换

可以通过字符串转换将任何类型转换为String类型.

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

  • 如果T是布尔值,则使用new Boolean(x).
  • 如果T是char,则使用new Character(x).
  • 如果T是byte,short或int,则使用new Integer(x).
  • 如果T很长,则使用新的Long(x).
  • 如果T是float,则使用new Float(x).
  • 如果T为double,则使用new Double(x).

然后通过字符串转换将此引用值转换为String类型.

现在只需要考虑参考值:

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

toString方法由原始类Object(第4.3.2节)定义.许多类都覆盖它,特别是Boolean,Character,Integer,Long,Float,Double和String.

有关字符串转换上下文的详细信息,请参见第5.4节.

15.18.1.

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

对于基本类型,实现还可以通过直接从基本类型转换为字符串来优化包装对象的创建.

优化版本实际上不会首先执行完全包装的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();
}
Run Code Online (Sandbox Code Playgroud)

生成这个 - 看看两个连接样式如何导致相同的字节码:

 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
Run Code Online (Sandbox Code Playgroud)

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

cip+ciop; 
Run Code Online (Sandbox Code Playgroud)

new StringBuilder(cip).append(ciop).toString();
Run Code Online (Sandbox Code Playgroud)

换句话说,+字符串连接中的运算符实际上是更详细的StringBuilder习语的简写.

  • 不,它没有.运算符重载(如在C++和某些语言中)有一些缺点,Java设计者认为这有点令人困惑,并且从Java中省略了它.对我来说,面向对象的语言必须具有Java所具有的继承,多态和封装的主要概念. (7认同)
  • @Pooya:实际上"int/int"与"int/float"是*已经*运算符重载,所以即使是C也有.然而*C(和Java)没有*是用户定义的运算符重载:定义运算符可以使用的不同方式(在C和Java中)的唯一方法是语言定义(并且区别已实现)在编译器中).C++的不同之处在于它允许用户定义的运算符重载(通常简称为"运算符重载"). (5认同)
  • 非常感谢,我不熟悉jvm字节代码,但为String plus = cip + ciop生成代码; 和String build = new StringBuilder(cip).append(ciop).toString(); 是一样的 我的问题是这个操作是否违反了面向对象? (3认同)
  • 是的,*运算符重载*在Java中用于串联类型的串联,但是,您无法定义自己的运算符(如在C++,C#和其他一些语言中). (3认同)
  • 是的,但我认为这个运算符已经为String类重载了 (2认同)

Ram*_*PVK 27

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

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

这就是Java规范所说的:

运算符+和-称为加法运算符.AdditiveExpression:MultiplicativeExpression AdditiveExpression + MultiplicativeExpression AdditiveExpression - MultiplicativeExpression

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

否则,运算+符的每个操作数的类型必须是可转换(第5.1.8节)到原始数字类型的类型,否则会发生编译时错误.

在每种情况下,二元-运算符的每个操作数的类型必须是可转换(第5.1.8节)到原始数字类型的类型,否则会发生编译时错误.

  • 该规范的引用完全与这个问题无关. (7认同)
  • 它没有说明它是如何实现的,这就是问题所在.我认为海报已经明白该功能存在. (7认同)

use*_*421 14

String类如何覆盖+运算符?

它没有.编译器做到了.严格地说,编译器会为String操作数重载 +运算符.


Pra*_*mar 6

首先(+)重载不被覆盖

Java语言为字符串连接运算符(+)提供了特殊支持,该运算符已针对Java Strings对象进行了重载.

  1. 如果左侧操作数是String,则它作为串联工作.

  2. 如果左侧操作数是Integer,则它作为加法运算符

  • (2)如果左操作数是一个整数,则将其自动取消装箱为"int",然后应用Java的常规规则. (3认同)
  • 引用下面给出的两个规则是错误的:我认为它们应该是:两个原语(或不可用的类)=加法; 至少一个字符串=连接 (2认同)