Qual è la differenza tra "testo" e nuova stringa ("testo")?


195

Qual è la differenza tra queste due seguenti affermazioni?

String s = "text";

String s = new String("text");


Per favore, chiunque risponda a questo. String a = "Java"; Stringa b = "Java"; System.out.println (a == b); true // but System.out.println ("a == b?" + a == b); // false ...
Energia

non capisco quando ho aggiunto un commento ("a == b?) => il mio risultato diventa FALSO. perché?
Energia

@Energy Il risultato è falseperché l'ordine delle operazioni impone che l'operatore + vada per primo, concatenando "a == b?" con a per creare una stringa "a == b? Java". Quindi l'espressione viene "a==b?Java" == bvalutata come falsa.
Allison B

@AllisonB capito, grazie mille!
Energia

Risposte:


187

new String("text"); crea esplicitamente un'istanza nuova e referenzialmente distinta di un Stringoggetto; String s = "text";può riutilizzare un'istanza dal pool di costanti di stringa, se disponibile.

È molto raramente vorrebbe mai di utilizzare la new String(anotherString)funzione di costruzione. Dall'API:

String(String original): Inizializza un oggetto appena creato in String modo che rappresenti la stessa sequenza di caratteri dell'argomento; in altre parole, la stringa appena creata è una copia della stringa dell'argomento. A meno che non sia necessaria una copia esplicita dell'originale, l'uso di questo costruttore non è necessario poiché le stringhe sono immutabili.

Domande correlate


Cosa significa distinzione referenziale

Esamina il seguente frammento:

    String s1 = "foobar";
    String s2 = "foobar";

    System.out.println(s1 == s2);      // true

    s2 = new String("foobar");
    System.out.println(s1 == s2);      // false
    System.out.println(s1.equals(s2)); // true

==su due tipi di riferimento è un confronto dell'identità di riferimento. Due oggetti che equalsnon sono necessariamente ==. Di solito è sbagliato usare ==su tipi di riferimento; La maggior parte delle volteequals deve essere utilizzata invece.

Tuttavia, se per qualsiasi motivo è necessario creare due equalsma non una ==stringa, è possibile utilizzare il new String(anotherString)costruttore. Va detto di nuovo, tuttavia, che questo è molto peculiare e raramente è l'intenzione.

Riferimenti

Problemi correlati


3
Se scrivo: String s = new String ("abc"); E ora scrivo: String s = "abc"; Will String s = "abc"; creare un nuovo valore letterale String nel pool di stringhe?
Kaveesh Kanwal,

Perché nessuno risponde alla domanda precedente?
zeds

2
@KaveeshKanwal No, il valore letterale non verrà duplicato. Come puoi vedere ci sono 2 "abc"secondi. Solo uno di essi andrà al pool di stringhe e l'altro farà riferimento ad esso. Poi c'è quello sche sarà il nuovo oggetto appropriato.
Kayaman,

1
@Kaveesh Kanwal - String s = new String ("abc") creerà solo un nuovo oggetto String con valore "abc". E la seconda istruzione verificherà se esiste un letterale di stringa "abc" già presente nel pool di stringhe o meno. Se già presente, viene restituito il riferimento a quello esistente e in caso contrario viene creato un nuovo valore letterale ("abc") nel pool di stringhe. Spero che risolva la tua domanda !!
user968813,

Non c'è "maggio" al riguardo. Il compilatore deve raggruppare valori letterali stringa. JLS 3.10.5 .
Marchese di Lorne,

119

I letterali stringa entreranno nel pool Costante stringhe .

Lo snapshot di seguito potrebbe aiutarti a capirlo visivamente per ricordarlo più a lungo.

inserisci qui la descrizione dell'immagine


Creazione dell'oggetto riga per riga:

String str1 = new String("java5");

Usando il valore letterale di stringa "java5" nel costruttore, un nuovo valore di stringa viene memorizzato nel pool di costanti di stringhe. Utilizzando un nuovo operatore, viene creato un nuovo oggetto stringa nell'heap con "java5" come valore.

String str2 = "java5"

Il riferimento "str2" indica il valore già memorizzato nel pool di costanti di stringhe

String str3 = new String(str2);

Nell'heap viene creato un nuovo oggetto stringa con lo stesso valore del riferimento di "str2"

String str4 = "java5";

Il riferimento "str4" indica il valore già memorizzato nel pool di costanti di stringhe

Totale oggetti: Heap - 2, Pool - 1

Ulteriori letture sulla comunità Oracle


1
Buona risposta .. ma vuoi sapere che ora sto cambiando il valore di str1 = "java6", allora cambierà il valore di str4?
CoronaPintu,

2
sì, ho controllato che non cambierà il valore di str4
CoronaPintu,

@Braj Puoi fornire la documentazione dell'affermazione della tua risposta?
Basil Bourque,

@Braj: Le intestazioni per 'Heap' e 'pool' nella tabella dovrebbero essere inverse?
Rahul Kurup,

Non corretto. Il pool costante viene creato in fase di compilazione, non in fase di esecuzione. Non utilizzare la formattazione del preventivo per il testo che non è citato.
Marchese di Lorne,

15

Uno crea una stringa nel pool di costanti di stringhe

String s = "text";

l'altro crea una stringa nel pool costante ( "text") e un'altra stringa nello spazio heap normale ( s). Entrambe le stringhe avranno lo stesso valore, quello di "testo".

String s = new String("text");

s viene quindi perso (idoneo per GC) se successivamente non utilizzato.

I letterali stringa invece sono riutilizzati. Se lo usi "text"in più punti della tua classe, sarà in effetti una e una sola stringa (ovvero più riferimenti alla stessa stringa nel pool).


Le stringhe nel pool costante non vengono mai perse. Intendevi dire che 's' è perso se successivamente non viene utilizzato?
Marchese di Lorne,

@EJP: sì, volevo dire "s". Grazie per averlo notato. Correggerò la domanda.

9

JLS

Il concetto è chiamato "interning" da JLS.

Passaggio pertinente di JLS 7 3.10.5 :

Inoltre, un letterale stringa si riferisce sempre alla stessa istanza della classe String. Questo perché i letterali di stringa - o, più in generale, stringhe che sono i valori di espressioni costanti (§15.28) - vengono "internati" in modo da condividere istanze univoche, usando il metodo String.intern.

Esempio 3.10.5-1. Letterali a corda

Il programma costituito dall'unità di compilazione (§7.3):

package testPackage;
class Test {
    public static void main(String[] args) {
        String hello = "Hello", lo = "lo";
        System.out.print((hello == "Hello") + " ");
        System.out.print((Other.hello == hello) + " ");
        System.out.print((other.Other.hello == hello) + " ");
        System.out.print((hello == ("Hel"+"lo")) + " ");
        System.out.print((hello == ("Hel"+lo)) + " ");
        System.out.println(hello == ("Hel"+lo).intern());
    }
}
class Other { static String hello = "Hello"; }

e l'unità di compilazione:

package other;
public class Other { public static String hello = "Hello"; }

produce l'output:

true true true true false true

JVM

JVMS 7 5.1 dice :

Un letterale stringa è un riferimento a un'istanza della classe String ed è derivato da una struttura CONSTANT_String_info (§4.4.3) nella rappresentazione binaria di una classe o interfaccia. La struttura CONSTANT_String_info fornisce la sequenza di punti di codice Unicode che costituisce la stringa letterale.

Il linguaggio di programmazione Java richiede che i letterali stringa identici (ovvero i letterali che contengono la stessa sequenza di punti di codice) debbano fare riferimento alla stessa istanza della classe String (JLS §3.10.5). Inoltre, se il metodo String.intern viene chiamato su qualsiasi stringa, il risultato è un riferimento alla stessa istanza della classe che verrebbe restituita se quella stringa apparisse come un valore letterale. Pertanto, la seguente espressione deve avere il valore vero:

("a" + "b" + "c").intern() == "abc"

Per derivare letteralmente una stringa, la Java Virtual Machine esamina la sequenza di punti di codice forniti dalla struttura CONSTANT_String_info.

  • Se il metodo String.intern è stato precedentemente chiamato su un'istanza della classe String contenente una sequenza di punti di codice Unicode identici a quelli forniti dalla struttura CONSTANT_String_info, il risultato della derivazione letterale di stringa è un riferimento a quella stessa istanza della classe String.

  • Altrimenti, viene creata una nuova istanza della classe String contenente la sequenza di punti di codice Unicode forniti dalla struttura CONSTANT_String_info; un riferimento a quell'istanza di classe è il risultato della derivazione letterale di stringa. Infine, viene invocato il metodo intern della nuova istanza String.

bytecode

È anche istruttivo esaminare l'implementazione del bytecode su OpenJDK 7.

Se decompiliamo:

public class StringPool {
    public static void main(String[] args) {
        String a = "abc";
        String b = "abc";
        String c = new String("abc");
        System.out.println(a);
        System.out.println(b);
        System.out.println(a == c);
    }
}

abbiamo nel pool costante:

#2 = String             #32   // abc
[...]
#32 = Utf8               abc

e main:

 0: ldc           #2          // String abc
 2: astore_1
 3: ldc           #2          // String abc
 5: astore_2
 6: new           #3          // class java/lang/String
 9: dup
10: ldc           #2          // String abc
12: invokespecial #4          // Method java/lang/String."<init>":(Ljava/lang/String;)V
15: astore_3
16: getstatic     #5          // Field java/lang/System.out:Ljava/io/PrintStream;
19: aload_1
20: invokevirtual #6          // Method java/io/PrintStream.println:(Ljava/lang/String;)V
23: getstatic     #5          // Field java/lang/System.out:Ljava/io/PrintStream;
26: aload_2
27: invokevirtual #6          // Method java/io/PrintStream.println:(Ljava/lang/String;)V
30: getstatic     #5          // Field java/lang/System.out:Ljava/io/PrintStream;
33: aload_1
34: aload_3
35: if_acmpne     42
38: iconst_1
39: goto          43
42: iconst_0
43: invokevirtual #7          // Method java/io/PrintStream.println:(Z)V

Nota come:

  • 0e 3: ldc #2viene caricata la stessa costante (i letterali)
  • 12: viene creata una nuova istanza di stringa (con #2come argomento)
  • 35: ae cvengono confrontati come oggetti regolari conif_acmpne

La rappresentazione di stringhe costanti è piuttosto magica sul bytecode:

  • ha una struttura CONSTANT_String_info dedicata , diversamente dagli oggetti normali (esnew String )
  • la struttura punta a una CONSTANT_Utf8_info struttura che contiene i dati. Questi sono gli unici dati necessari per rappresentare la stringa.

e la citazione JVMS sopra sembra dire che ogni volta che Utf8 indicato è lo stesso, vengono caricate istanze identiche ldc .

Ho fatto test simili per i campi e:

  • static final String s = "abc"punta alla tabella costante tramite l' attributo ConstantValue
  • i campi non finali non hanno quell'attributo, ma possono comunque essere inizializzati con ldc

Conclusione : esiste un supporto bytecode diretto per il pool di stringhe e la rappresentazione della memoria è efficiente.

Bonus: confrontalo con il pool Integer , che non ha il supporto del bytecode diretto (cioè nessun CONSTANT_String_infoanalogo).


2

@Braj: penso che tu abbia menzionato il contrario. Perfavore, correggimi se sbaglio

Creazione dell'oggetto riga per riga:

String str1 = new String ("java5")

   Pool- "java5" (1 Object)

   Heap - str1 => "java5" (1 Object)

String str2 = "java5"

  pool- str2 => "java5" (1 Object)

  heap - str1 => "java5" (1 Object)

String str3 = new String (str2)

  pool- str2 => "java5" (1 Object)

  heap- str1 => "java5", str3 => "java5" (2 Objects)

String str4 = "java5"

  pool - str2 => str4 => "java5" (1 Object)

  heap - str1 => "java5", str3 => "java5" (2 Objects)

str1non è coinvolto nel valore di str2o di str3o str4in qualsiasi modo.
Marchese di Lorne,

1

Pensa di "bla"essere una fabbrica magica come Strings.createString("bla")(pseudo). La fabbrica contiene un pool di tutte le stringhe ancora create in questo modo.

Se viene richiamato, verifica se nel pool è già presente una stringa con questo valore. Se vero, restituisce questo oggetto stringa, quindi le stringhe ottenute in questo modo sono effettivamente lo stesso oggetto.

In caso contrario, crea un nuovo oggetto stringa internamente, lo salva nel pool e quindi lo restituisce. Pertanto, quando viene richiesto lo stesso valore di stringa la volta successiva, viene restituita la stessa istanza.

La creazione manuale new String("")ignora questo comportamento ignorando il pool letterale di stringhe. Quindi l'uguaglianza dovrebbe sempre essere controllata usando equals()quale confronta la sequenza di caratteri invece dell'uguaglianza di riferimento dell'oggetto.


La "fabbrica magica" a cui ti riferisci non è altro che il compilatore Java. È un errore scrivere di questo processo come se si fosse verificato in fase di esecuzione.
Marchese di Lorne,

1

Di seguito è riportato un modo semplice per comprendere la differenza:

String s ="abc";
String s1= "abc";
String s2=new String("abc");

        if(s==s1){
            System.out.println("s==s1 is true");
        }else{
            System.out.println("s==s1 is false");
        }
        if(s==s2){
            System.out.println("s==s2 is true");
        }else{
            System.out.println("s==s2 is false");
        }

l'output è

s==s1 is true
s==s2 is false

Pertanto new String () creerà sempre una nuova istanza.


1

Qualsiasi valore letterale String viene creato nel pool letterale stringa e il pool non consente duplicati. Pertanto, se due o più oggetti stringa vengono inizializzati con lo stesso valore letterale, tutti gli oggetti punteranno allo stesso valore letterale.

String obj1 = "abc";
String obj2 = "abc";

"obj1" e "obj2" indicheranno lo stesso valore letterale di stringa e il pool letterale di stringa avrà un solo valore letterale "abc".

Quando creiamo un oggetto di classe String utilizzando la nuova parola chiave, la stringa così creata viene archiviata nella memoria dell'heap. Qualsiasi valore letterale di stringa passato come parametro al costruttore della classe String viene comunque archiviato nel pool di stringhe. Se creiamo più oggetti utilizzando lo stesso valore con il nuovo operatore, ogni volta verrà creato un nuovo oggetto nell'heap, a causa di questo nuovo operatore dovrebbe essere evitato.

String obj1 = new String("abc");
String obj2 = new String("abc");

"obj1" e "obj2" indicheranno due diversi oggetti nell'heap e il pool letterale di stringhe avrà un solo letterale "abc".

Inoltre, qualcosa che vale la pena notare per quanto riguarda il comportamento delle stringhe è che ogni nuova assegnazione o concatenazione eseguita sulla stringa crea un nuovo oggetto in memoria.

String str1 = "abc";
String str2 = "abc" + "def";
str1 = "xyz";
str2 = str1 + "ghi";

Nel caso sopra riportato:
Riga 1: il valore letterale "abc" è memorizzato nel pool di stringhe.
Riga 2: il valore letterale "abcdef" viene archiviato nel pool di stringhe.
Riga 3: un nuovo valore letterale "xyz" viene archiviato nel pool di stringhe e "str1" inizia a indicare questo valore letterale.
Riga 4: poiché il valore viene generato aggiungendo a un'altra variabile, il risultato viene archiviato nella memoria dell'heap e il valore letterale aggiunto "ghi" verrà verificato per la sua esistenza nel pool di stringhe e verrà creato poiché non esiste in il caso sopra.


0

Anche se sembra lo stesso dal punto di vista dei programmatori, ha un grande impatto sulle prestazioni. Vorresti usare il primo modulo quasi sempre.


0
String str = new String("hello")

Verificherà se il pool di costanti String contiene già String "ciao"? Se presente, non aggiungerà una voce nel pool di costanti String. Se non presente, verrà aggiunta una voce nel pool di costanti String.

Un oggetto verrà creato in un'area di memoria dell'heap e strpunti di riferimento all'oggetto creato nella posizione della memoria dell'heap.

se si desidera fare strriferimento all'oggetto punto contenente nel pool di costanti String, si deve chiamare esplicitamentestr.intern();

String str = "world";

Verificherà se il pool di costanti String contiene già String "ciao"? Se presente, non aggiungerà una voce nel pool di costanti String. Se non presente, verrà aggiunta una voce nel pool di costanti String.

In entrambi i casi precedenti, i strriferimenti fanno riferimento a String "world"presente nel pool Costante.


'It' è il compilatore Java. Il valore letterale stringa crea una voce univoca nel pool costante, al momento della compilazione. È un errore scrivere questo processo come se si verificasse durante l'esecuzione.
Marchese di Lorne

Puoi spiegare chiaramente cosa non va in questo post?
Jayesh,

Ciò che è sbagliato in questo post è che la stringa letterale è raggruppata al momento del completamento, come ho già detto. Non durante l'esecuzione del codice, come nella tua risposta.
Marchese di Lorne,

@EJP Apprezzo la tua risposta. Potete per favore indicare la riga esatta che è errata nella risposta. Vedo che tutte le risposte sopra sono uguali a quelle che ho scritto. Aiutatemi, voglio correggere la mia comprensione. Grazie.
Jayesh,

Hai scritto sull'intero processo come se tutto avvenga quando la riga di codice viene eseguita, cosa che, come ti ho ripetutamente detto, non è il caso. Non puoi ridurre tutto ciò a una singola "linea esatta" che non va nella tua risposta.
Marchese di Lorne,

0

Quando memorizzi una stringa come

String string1 = "Hello";

direttamente, quindi JVM crea un oggetto String con il prezzo indicato durante un blocco separato di memoria chiamato pool di costanti String.

E ogni volta che abbiamo la tendenza a provare a produrre un'altra stringa come

String string2 = "Hello";

JVM verifica se esiste un oggetto String con prezzo costante all'interno del pool di costanti String, in tal caso, anziché creare un oggetto nuovo di zecca JVM assegna il riferimento dell'oggetto esistente alla nuova variabile.

E quando conserviamo String come

String string = new String("Hello");

utilizzando la nuova parola chiave, viene creato un oggetto nuovo di zecca con il prezzo indicato, indipendentemente dal contenuto del pool di costanti String.

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.