In che modo la classe String ha la precedenza sull'operatore +?


129

Perché in Java puoi aggiungere stringhe con l'operatore +, quando String è una classe? Nel String.javacodice non ho trovato alcuna implementazione per questo operatore. Questo concetto viola l'orientamento agli oggetti?


21
L' +operatore Plus ( ) è la funzionalità linguistica di Java.
Adatapost,

18
È tutta la magia del compilatore. Non è possibile eseguire il sovraccarico dell'operatore in Java.
Lion,

18
Penso che la cosa strana sia che String sia implementato come libreria al di fuori della lingua (in java.lang.String), ma ha un supporto specifico all'interno della lingua. Non è giusto
13


@ 13ren ti sbagli. String è parte della classe java.lang che sta per lingua - il java lanaguage !!! Tutte le classi di questo pacchetto sono speciali. Si noti che non è necessario importare java.lang per usarli.

Risposte:


156

Diamo un'occhiata alle seguenti espressioni semplici in Java

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

I convertiti compilatore "x = "+x;in un StringBuilderinternamente ed usi .append(int)a "aggiungere" il numero intero per la stringa.

5.1.11. Conversione di stringhe

Qualsiasi tipo può essere convertito in tipo String mediante conversione di stringhe.

Un valore x di tipo T primitivo viene prima convertito in un valore di riferimento come se dandolo come argomento a un'espressione di creazione di un'istanza di classe appropriata (§ 15.9):

  • Se T è booleano, utilizzare il nuovo booleano (x).
  • Se T è char, usa il nuovo carattere (x).
  • Se T è byte, short o int, usa new Integer (x).
  • Se T è lungo, usa il nuovo lungo (x).
  • Se T è float, usa il nuovo float (x).
  • Se T è doppio, utilizzare il nuovo doppio (x).

Questo valore di riferimento viene quindi convertito nel tipo String mediante la conversione di stringhe.

Ora devono essere considerati solo i valori di riferimento:

  • Se il riferimento è null, viene convertito nella stringa "null" (quattro caratteri ASCII n, u, l, l).
  • Altrimenti, la conversione viene eseguita come se invocasse il metodo toString dell'oggetto referenziato senza argomenti; ma se il risultato dell'invocazione del metodo toString è null, viene invece utilizzata la stringa "null".

Il metodo toString è definito dalla classe primordiale Object (§4.3.2). Molte classi lo sovrascrivono, in particolare Boolean, Character, Integer, Long, Float, Double e String.

Vedere §5.4 per dettagli sul contesto di conversione delle stringhe.

15.18.1.

Ottimizzazione della concatenazione di stringhe: un'implementazione può scegliere di eseguire la conversione e la concatenazione in un solo passaggio per evitare di creare e scartare un oggetto String intermedio. Per aumentare le prestazioni della concatenazione di stringhe ripetute, un compilatore Java può utilizzare la classe StringBuffer o una tecnica simile per ridurre il numero di oggetti String intermedi creati dalla valutazione di un'espressione.

Per i tipi primitivi, un'implementazione può anche ottimizzare la creazione di un oggetto wrapper convertendo direttamente da un tipo primitivo in una stringa.

La versione ottimizzata in realtà non eseguirà prima una conversione String completamente chiusa.

Questa è una buona illustrazione di una versione ottimizzata utilizzata dal compilatore, anche se senza la conversione di una primitiva, in cui è possibile vedere il compilatore cambiare le cose in StringBuilder in background:

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


Questo codice 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();
}

Genera questo - guarda come i due stili di concatenazione portano allo stesso bytecode:

 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

Guardando l'esempio sopra e come viene generato il codice byte basato sul codice sorgente nell'esempio dato, si noterà che il compilatore ha trasformato internamente la seguente istruzione

cip+ciop; 

in

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

In altre parole, l'operatore +nella concatenazione di stringhe è effettivamente una scorciatoia per il StringBuilderlinguaggio più dettagliato .


3
Grazie mille, non ho familiarità con i codici byte jvm ma ho generato codice per String plus = cip + ciop; e String build = new StringBuilder (cip) .append (ciop) .toString (); sono uguali. e la mia domanda è che questa operazione viola l'orientamento agli oggetti?
Pooya,

7
No, non lo fa. Il sovraccarico dell'operatore (come in C ++ e in alcuni linguaggi) presenta alcuni svantaggi e i progettisti Java hanno ritenuto che fosse un concetto un po 'confuso e lo hanno omesso da Java. Per me, un linguaggio orientato agli oggetti deve avere i concetti principali di eredità, polimorfismo e incapsulamento che Java ha.
Lion,

2
sì, ma penso che questo operatore abbia sovraccaricato la classe String
Pooya il

3
Sì, l'overloading dell'operatore viene utilizzato in Java per la concatenazione del tipo String, tuttavia non è possibile definire il proprio operatore (come in C ++, C # e alcuni altri linguaggi).
Lion,

5
@Pooya: in realtà "int / int" vs. "int / float" sta già sovraccaricando l'operatore, quindi anche C ha questo. Ciò che C (e Java) tuttavia non hanno è il sovraccarico dell'operatore definito dall'utente: l'unica cosa che definisce i diversi modi in cui un operatore può essere usato (sia in C che in Java) è la definizione del linguaggio (e la distinzione è implementata nel compilatore). Il C ++ differisce in quanto consente il sovraccarico dell'operatore definito dall'utente (che viene spesso definito semplicemente "sovraccarico dell'operatore").
Joachim Sauer,

27

È la funzione del compilatore Java che controlla gli operandi +dell'operatore. E in base agli operandi genera il codice byte:

  • Per String, genera codice per concatenare le stringhe
  • Per Numbers, genera codice per aggiungere numeri.

Questo è ciò che dice la specifica Java :

Gli operatori + e -sono chiamati operatori additivi. AdditiveExpression: MultiplicativeExpression AdditiveExpression + MultiplicativeExpression AdditiveExpression - MultiplicativeExpression

Gli operatori additivi hanno la stessa precedenza e sono sintatticamente lasciati associativi (raggruppano da sinistra a destra). Se il tipo di uno degli operandi di un +operatore è String, allora l'operazione è la concatenazione di stringhe.

Altrimenti, il tipo di ciascuno degli operandi +dell'operatore deve essere un tipo convertibile (§5.1.8) in un tipo numerico primitivo, altrimenti si verifica un errore di compilazione.

In ogni caso, il tipo di ciascuno degli operandi -dell'operatore binario deve essere un tipo convertibile (§5.1.8) in un tipo numerico primitivo, altrimenti si verifica un errore di compilazione.


7
La citazione dalle specifiche non è affatto rilevante per questa domanda.
Ernest Friedman-Hill,

Questo è l'estratto "Se il tipo di uno degli operandi di un operatore + è String, allora l'operazione è la concatenazione di stringhe. Altrimenti, il tipo di ciascuno degli operandi dell'operatore + deve essere un tipo convertibile (§5.1.8 ) a un tipo numerico primitivo o si verifica un errore in fase di compilazione ". Potete per favore dirmi perché non è rilevante.
Ramesh PVK,

7
Non dice nulla su come è implementata, qual è la domanda. Penso che il poster capisca già che la funzionalità esiste.
Ernest Friedman-Hill,

14

In che modo la classe String ignora + operatore?

Non Il compilatore lo fa. A rigor di termini, il compilatore sovraccarica l'operatore + per gli operandi di stringa.


6

Innanzitutto (+) è sovraccarico, non sovrascritto

Il linguaggio Java fornisce un supporto speciale per l'operatore di concatenazione di stringhe (+), che è stato sovraccaricato per gli oggetti Java Strings.

  1. Se l'operando sul lato sinistro è String, funziona come concatenazione.

  2. Se l'operando sul lato sinistro è intero, funziona come operatore addizione


3
(2) Se l'operando di sinistra è un numero intero in cui è auto-decompresso inte quindi si applicano le normali regole di Java.
Marchese di Lorne,

2
Le due regole fornite sotto la citazione sono errate: credo che dovrebbero essere: due primitive (o classi non boxabili) = addizione; almeno una stringa = concatenazione
mwfearnley

4

Il linguaggio Java fornisce un supporto speciale per l'operatore di concatenazione di stringhe (+) e per la conversione di altri oggetti in stringhe. La concatenazione di stringhe viene implementata tramite la classe StringBuilder(o StringBuffer) e il suo appendmetodo.


4

Il significato +dell'operatore quando applicato Stringè definito dalla lingua, come tutti hanno già scritto. Dal momento che non ti sembra sufficientemente convincente, considera questo:

Ints, float e double hanno tutti differenti rappresentazioni binarie, e quindi aggiungere due ints è un'operazione diversa, in termini di manipolazione dei bit, rispetto all'aggiunta di due float: per gli ints puoi aggiungere bit per bit, portando un bit e controllando l'overflow; per i carri armati devi trattare separatamente le mante e gli esponenti.

Quindi, in linea di principio, "addizione" dipende dalla natura degli oggetti che vengono "aggiunti". Java lo definisce per stringhe, nonché ints e float (long, double, ...)


3

L' +operatore viene di solito sostituito da a StringBuilderin fase di compilazione. Controlla questa risposta per maggiori dettagli in merito.


In questo caso, c'è qualche motivo per cui StringBuilder esiste per uso pubblico? Ci sono casi in cui l' +operatore non è sostituito da StringBuilder?
Cemre,

2
La domanda che vuoi porre è "perché l'operatore + esiste per uso pubblico?", Perché questo è l'abominio qui. Per quanto riguarda l'altra tua domanda, non lo so esattamente, ma immagino che non esiste un caso del genere.
Scorpione,

Il compilatore potrebbe usare concat () invece, se ci sono solo due elementi. Inoltre il compilatore non riesce a sostituire concat () con StringBuilder (o usa più StringBuilder e li aggiunge insieme) quando il programmatore sta costruendo una stringa in codice nidificato / in loop lungo - l'uso di StringBuilder singolo ed esplicito sarà migliore per le prestazioni.
user158037,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.