String类如何覆盖+运算符?


129

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


21
加号(+)运算符是Java的语言功能。
adatapost

18
这都是编译器的魔力。您无法在Java中执行运算符重载。
Lion

18
我认为很奇怪的是String 在语言(在java.lang.String中)之外被实现为一个库,但是在语言内部有特定的支持。那是不对的。
13ren


@ 13ren你错了。字符串是代表语言的java.lang类的一部分-Java lanaguage !!! 此软件包中的所有类都是特殊的。注意,您不需要导入java.lang即可使用它们。

Answers:


156

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

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

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

5.1.11。字符串转换

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

首先将原始类型T的值x转换为参考值,就像通过将其作为适当的类实例创建表达式的参数(第15.9节):

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

然后,通过字符串转换将该参考值转换为String类型。

现在只需要考虑参考值:

  • 如果引用为空,则将其转换为字符串“ null”(四个ASCII字符n,u,l,l)。
  • 否则,转换的执行就好像是通过调用不带参数的引用对象的toString方法进行的;但是,如果调用toString方法的结果为null,则使用字符串“ null”。

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

有关字符串转换上下文的详细信息,请参见§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();
}

生成此代码-查看两种串联样式如何导致相同的字节码:

 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习惯用法的简写形式。


3
非常感谢,我不熟悉jvm字节码,但是为String plus = cip + ciop生成了代码;和String build = new StringBuilder(cip).append(ciop).toString(); 都一样 我的问题是此操作是否违反了面向对象的方向?
Pooya

7
不,不是。运算符重载(如C ++和某些语言)有一些缺点,Java设计人员认为这有点令人困惑,因此从Java中省略了。对我而言,面向对象的语言必须具有Java所具有的继承,多态和封装的主要概念。
Lion

2
是的,但是我认为该运算符已针对String类进行了重载
Pooya

3
是的,运算符重载在Java中用于String类型的串联,但是,您不能定义自己的运算符(如C ++,C#和某些其他语言)。
Lion

5
@Pooya:实际上,“ int / int”与“ int / float” 已经是运算符重载,因此,即使C也是如此。但是C(和Java)没有的是用户定义的运算符重载:定义操作符可以使用的不同方式(在C和Java中)的唯一一件事就是语言定义(并且区别是在编译器)。C ++的不同之处在于,它允许用户定义运算符重载(通常称为“运算符重载”)。
约阿希姆·绍尔

27

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

  • 对于String,它将生成代码以连接字符串
  • 对于数字,它会生成代码以添加数字。

这就是Java规范所说的

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

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

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

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


7
规范中的报价与该问题完全无关。
欧内斯特·弗里德曼·希尔

这是摘录“如果+运算符的任何一个操作数的类型为String,则该操作为字符串连接。否则,+运算符的每个操作数的类型必须为可转换的类型(第5.1.8节)转换为原始数字类型,否则会发生编译时错误”。您能告诉我为什么它不相关吗?
拉梅什PVK 2012年

7
它没有说明如何实现,这是一个问题。我认为发布者已经知道该功能存在。
欧内斯特·弗里德曼·希尔

14

字符串类如何覆盖+运算符?

没有。编译器执行此操作。严格来说,编译器会将 String操作数的+运算符重载


6

首先(+)超载而不是被覆盖

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

  1. 如果左侧操作数为String,则它可以作为串联。

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


3
(2)如果左操作数是Integer,则将其自动拆箱int,然后应用Java的常规规则。
罗恩侯爵

2
引用下面给出的两个规则是错误的:我认为应该是:两个原语(或不可装箱的类)=加法;至少一个字符串=串联
mwfearnley

4

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


4

正如每个人都已经写过的那样,+运算符的含义在应用时String由该语言定义。由于您似乎没有足够的说服力,请考虑以下事项:

整数,浮点数和双精度数都有不同的二进制表示形式,因此就位操作而言,添加两个整数与添加两个浮点数是不同的操作。对于浮点数,必须分别处理尾数和指数。

因此,原则上,“添加”取决于要“添加”的对象的性质。Java为字符串以及整数和浮点数(long,double,...)定义了它


3

+通常StringBuilder在编译时将运算符替换为。查看此答案以获取有关此问题的更多详细信息。


如果是这样,是否有任何理由完全存在StringBuilder供公众使用?是否有某些情况下+操作符不被替换StringBuilder
Cemre

2
您想问的问题是“为什么+运算符根本不存在以供公共使用?”,因为这是可憎的。至于您的其他问题,我不十分清楚,但我猜没有这种情况。
天蝎座

如果只有两个元素,则编译器可能会改用concat()。当程序员在长嵌套/循环代码中构建字符串时,编译器也无法用StringBuilder(或使用多个StringBuilder并将它们附加在一起)替换concat()-使用单个显式StringBuilder可以提高性能。
2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.