Wie überschreibt die String-Klasse den Operator +?

129

Warum können Sie in Java Strings mit dem Operator + hinzufügen, wenn String eine Klasse ist? Im String.javaCode habe ich keine Implementierung für diesen Operator gefunden. Verstößt dieses Konzept gegen die Objektorientierung?

Pooya
quelle
21
Der +Operator Plus ( ) ist eine Sprachfunktion von Java.
Adatapost
18
Es ist alles die Magie des Compilers. Sie können in Java keine Operatorüberladung durchführen.
Lion
18
Ich denke, das Seltsame ist, dass String als Bibliothek außerhalb der Sprache (in java.lang.String) implementiert ist , aber innerhalb der Sprache spezifische Unterstützung bietet . Das stimmt nicht.
13ren
@ 13ren du liegst falsch. String gehört zur Klasse java.lang, die für Sprache steht - die Java-Sprache !!! Alle Klassen in diesem Paket sind speziell. Beachten Sie, dass Sie java.lang nicht importieren müssen, um sie zu verwenden.

Antworten:

156

Schauen wir uns die folgenden einfachen Ausdrücke in Java an

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

Der Compiler konvertiert "x = "+x;in eine StringBuilderinterne und verwendet .append(int), um die Ganzzahl zur Zeichenfolge "hinzuzufügen".

5.1.11. String-Konvertierung

Jeder Typ kann durch String-Konvertierung in den Typ String konvertiert werden.

Ein Wert x vom primitiven Typ T wird zuerst in einen Referenzwert konvertiert, als ob er als Argument für einen geeigneten Ausdruck zur Erstellung einer Klasseninstanz angegeben würde (§15.9):

  • Wenn T boolesch ist, verwenden Sie den neuen Booleschen Wert (x).
  • Wenn T char ist, verwenden Sie ein neues Zeichen (x).
  • Wenn T Byte, Short oder Int ist, verwenden Sie die neue Ganzzahl (x).
  • Wenn T lang ist, verwenden Sie neues Lang (x).
  • Wenn T float ist, verwenden Sie new Float (x).
  • Wenn T doppelt ist, verwenden Sie neues Double (x).

Dieser Referenzwert wird dann durch Zeichenfolgenkonvertierung in den Typ String konvertiert.

Jetzt müssen nur noch Referenzwerte berücksichtigt werden:

  • Wenn die Referenz null ist, wird sie in die Zeichenfolge "null" konvertiert (vier ASCII-Zeichen n, u, l, l).
  • Andernfalls wird die Konvertierung wie durch Aufrufen der toString-Methode des referenzierten Objekts ohne Argumente ausgeführt. Wenn das Ergebnis des Aufrufs der toString-Methode jedoch null ist, wird stattdessen die Zeichenfolge "null" verwendet.

Die toString-Methode wird durch die Urklasse Object (§4.3.2) definiert. Viele Klassen überschreiben es, insbesondere Boolean, Character, Integer, Long, Float, Double und String.

Siehe §5.4 für Details zum Kontext der Zeichenfolgenkonvertierung.

15.18.1.

Optimierung der String-Verkettung: Eine Implementierung kann die Konvertierung und Verkettung in einem Schritt durchführen, um zu vermeiden, dass ein Zwischen-String-Objekt erstellt und anschließend verworfen wird. Um die Leistung der wiederholten Verkettung von Zeichenfolgen zu erhöhen, kann ein Java-Compiler die StringBuffer-Klasse oder eine ähnliche Technik verwenden, um die Anzahl der zwischengeschalteten Zeichenfolgenobjekte zu verringern, die durch Auswertung eines Ausdrucks erstellt werden.

Bei primitiven Typen kann eine Implementierung auch die Erstellung eines Wrapper-Objekts optimieren, indem sie direkt von einem primitiven Typ in eine Zeichenfolge konvertiert.

Die optimierte Version führt nicht zuerst eine vollständig umschlossene String-Konvertierung durch.

Dies ist ein gutes Beispiel für eine optimierte Version, die vom Compiler verwendet wird, allerdings ohne die Konvertierung eines Grundelements, wobei Sie sehen können, wie der Compiler im Hintergrund Dinge in einen StringBuilder ändert:

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


Dieser Java-Code:

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

Erzeugt dies - sehen Sie, wie die beiden Verkettungsstile zu demselben Bytecode führen:

 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

Wenn Sie sich das obige Beispiel ansehen und sehen, wie der Bytecode basierend auf dem Quellcode im angegebenen Beispiel generiert wird, werden Sie feststellen, dass der Compiler die folgende Anweisung intern transformiert hat

cip+ciop; 

in

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

Mit anderen Worten, der Operator +bei der Verkettung von Zeichenfolgen ist effektiv eine Abkürzung für die ausführlichere Sprache StringBuilder.

Löwe
quelle
3
Vielen Dank, ich bin nicht mit JVM-Byte-Codes vertraut, habe aber Code für String plus = cip + ciop generiert. und String build = new StringBuilder (cip) .append (ciop) .toString (); sind gleich. und meine Frage ist, dass diese Operation die Objektorientierung verletzt?
Pooya
7
Nein, das tut es nicht. Das Überladen von Operatoren (wie in C ++ und einigen Sprachen) hat einige Nachteile, und Java-Designer waren der Meinung, dass dies ein verwirrendes Konzept ist, und haben es in Java weggelassen. Für mich muss eine objektorientierte Sprache die Hauptkonzepte der Vererbung, des Polymorphismus und der Kapselung haben, die Java hat.
Löwe
2
Ja, aber ich denke, dass dieser Operator für die String-Klasse überladen wurde
Pooya
3
Ja, das Überladen von Operatoren wird in Java für die Verkettung des String-Typs verwendet. Sie können jedoch keinen eigenen Operator definieren (wie in C ++, C # und einigen anderen Sprachen).
Löwe
5
@Pooya: Eigentlich ist "int / int" vs. "int / float" bereits eine Überladung des Operators, also hat sogar C das. Was C (und Java) jedoch nicht haben, ist die benutzerdefinierte Überladung von Operatoren: Das einzige, was die verschiedenen Verwendungsmöglichkeiten eines Operators (sowohl in C als auch in Java) definiert, ist die Sprachdefinition (und die Unterscheidung ist in der implementiert Compiler). C ++ unterscheidet sich darin, dass es eine benutzerdefinierte Operatorüberladung ermöglicht (die oft einfach als "Operatorüberladung" bezeichnet wird).
Joachim Sauer
27

Es ist eine Java-Compiler-Funktion, die die Operanden des +Operators überprüft . Und basierend auf den Operanden generiert es den Bytecode:

  • Für String wird Code für Concat-Strings generiert
  • Für Zahlen wird Code zum Hinzufügen von Zahlen generiert.

Dies ist, was die Java-Spezifikation sagt :

Die Operatoren + und -werden als additive Operatoren bezeichnet. AdditiveExpression: MultiplicativeExpression AdditiveExpression + MultiplicativeExpression AdditiveExpression - MultiplicativeExpression

Die additiven Operatoren haben dieselbe Priorität und sind syntaktisch linksassoziativ (sie gruppieren von links nach rechts). Wenn der Typ eines der Operanden eines +Operators ist String, handelt es sich bei der Operation um eine Zeichenfolgenverkettung.

Andernfalls muss der Typ jedes Operanden des +Operators ein Typ sein, der in einen primitiven numerischen Typ konvertierbar ist (§5.1.8), da sonst ein Fehler bei der Kompilierung auftritt.

In jedem Fall muss der Typ jedes Operanden des Binäroperators -ein Typ sein, der in einen primitiven numerischen Typ konvertierbar ist (§5.1.8), da sonst ein Fehler bei der Kompilierung auftritt.

Ramesh PVK
quelle
7
Das Zitat aus der Spezifikation ist für diese Frage überhaupt nicht relevant.
Ernest Friedman-Hill
Dies ist der Auszug "Wenn der Typ eines der Operanden eines + -Operators String ist, ist die Operation eine String-Verkettung. Andernfalls muss der Typ jedes Operanden des + -Operators konvertierbar sein (§5.1.8 ) auf einen primitiven numerischen Typ oder es tritt ein Fehler bei der Kompilierung auf ". Können Sie mir bitte sagen, warum es nicht relevant ist?
Ramesh PVK
7
Es sagt nichts darüber aus, wie es implementiert ist, was die Frage ist. Ich denke, das Poster versteht bereits, dass die Funktion vorhanden ist.
Ernest Friedman-Hill
14

Wie überschreibt die String-Klasse den + Operator?

Das tut es nicht. Der Compiler macht es. Streng genommen ist der Compiler Überlastungen den Operator + für String - Operanden.

Marquis von Lorne
quelle
6

Zunächst wird (+) überladen und nicht überschrieben

Die Java-Sprache bietet spezielle Unterstützung für den String-Verkettungsoperator (+), der für Java-Strings-Objekte überladen wurde.

  1. Wenn der linke Operand String ist, funktioniert er als Verkettung.

  2. Wenn der linke Operand Integer ist, funktioniert er als Additionsoperator

Pramod Kumar
quelle
3
(2) Wenn der linke Operand eine Ganzzahl ist, wird er automatisch entpackt intund es gelten die normalen Regeln von Java.
Marquis von Lorne
2
Die beiden Regeln unter dem Zitat sind falsch: Ich glaube, sie sollten sein: zwei Grundelemente (oder nicht boxbare Klassen) = Addition; mindestens eine Zeichenfolge = Verkettung
mwfearnley
4

Die Java-Sprache bietet spezielle Unterstützung für den String-Verkettungsoperator (+) und für die Konvertierung anderer Objekte in Strings. Die Verkettung von Zeichenfolgen wird über die Klasse StringBuilder(oder StringBuffer) und ihre appendMethode implementiert .

Jigar Pandya
quelle
4

Die Bedeutung des +Operators bei der Anwendung Stringwird durch die Sprache definiert, wie jeder bereits geschrieben hat. Bedenken Sie Folgendes, da Sie dies nicht ausreichend überzeugend zu finden scheinen:

Ints, Floats und Doubles haben alle unterschiedliche binäre Darstellungen. Daher ist das Hinzufügen von zwei Ints in Bezug auf die Bitmanipulation eine andere Operation als das Hinzufügen von zwei Floats: Bei Ints können Sie Bit für Bit hinzufügen, ein Bit tragen und auf Überlauf prüfen. Für Floats müssen Sie die Mantissen und Exponenten separat behandeln.

Im Prinzip hängt "Addition" also von der Art der Objekte ab, die "hinzugefügt" werden. Java definiert es sowohl für Strings als auch für Ints und Floats (Longs, Doubles, ...).

alexis
quelle
3

Der +Operator wird normalerweise durch eine StringBuilderKompilierungszeit ersetzt. Überprüfen Sie diese Antwort für weitere Details zu diesem Thema.

Skorpion
quelle
Wenn dies der Fall ist, gibt es einen Grund, warum StringBuilder überhaupt für die öffentliche Verwendung vorhanden ist? Gibt es Fälle, in denen der +Bediener nicht durch ersetzt wird StringBuilder?
Cemre
2
Die Frage, die Sie stellen möchten, lautet: "Warum gibt es den Operator + überhaupt für den öffentlichen Gebrauch?", Denn das ist hier der Greuel. Was Ihre andere Frage betrifft, weiß ich es nicht genau, aber ich würde vermuten, dass es keinen solchen Fall gibt.
Skorpion
Der Compiler verwendet stattdessen möglicherweise concat (), wenn nur zwei Elemente vorhanden sind. Außerdem kann der Compiler concat () nicht durch StringBuilder ersetzen (oder verwendet mehrere StringBuilder und hängt sie zusammen), wenn der Programmierer einen String in lang verschachteltem / gelooptem Code erstellt. Die Verwendung eines einzelnen, expliziten StringBuilder ist für die Leistung besser.
user158037