Le stringhe sono oggetti in Java, quindi perché non usiamo "new" per crearle?


104

Normalmente creiamo oggetti utilizzando la newparola chiave, come:

Object obj = new Object();

Le stringhe sono oggetti, ma non le usiamo newper crearle:

String str = "Hello World";

Perchè è questo? Posso creare una stringa con new?


Dovresti anche dare un'occhiata a questa domanda stackoverflow.com/questions/456575/java-wrapper-equality-test
modificato il

1
Perché i letterali stringa sono già oggetti.
Marchese di Lorne

1
Si noti che new String(...)è stato utilizzato per aggirare un dettaglio di implementazione quando si creano sottostringhe stringhe di grandi dimensioni. Questo problema è stato risolto in Java 7 e non è più necessario.
Thorbjørn Ravn Andersen

Sono il centesimo liker di questo post. :)
Mukit09

Risposte:


130

Oltre a quanto già detto, le stringhe letterali [cioè stringhe simili "abcd"ma non simili new String("abcd")] in Java sono internate - questo significa che ogni volta che fai riferimento ad "abcd", ottieni un riferimento a una singola Stringistanza, piuttosto che a una nuova ogni volta. Quindi avrai:

String a = "abcd";
String b = "abcd";

a == b; //True

ma se lo avessi fatto

String a = new String("abcd");
String b = new String("abcd");

allora è possibile avere

a == b; // False

(e nel caso qualcuno abbia bisogno di ricordarlo, usa sempre .equals()per confrontare le stringhe; ==test per l'uguaglianza fisica).

L'internamento dei valori letterali String è utile perché spesso vengono utilizzati più di una volta. Ad esempio, considera il codice (artificioso):

for (int i = 0; i < 10; i++) {
  System.out.println("Next iteration");
}

Se non avessimo l'interning di stringhe, "Next iteration" dovrebbe essere istanziato 10 volte, mentre ora verrà istanziato solo una volta.


1
Utilizzando String a = new String ("abcd"), significa che due stringhe con contenuto simile sono presenti in memoria.
modificato il

1
Giusto: il compilatore non controllerà necessariamente se una stringa di questo tipo è già stata internata (anche se potresti certamente scriverne una che lo abbia fatto).
Danben

sì, questa ottimizzazione è possibile perché le stringhe sono immutabili e quindi possono essere condivise senza problemi. la gestione condivisa "asdf" è un'implementazione del modello di progettazione 'Flyweight'.
manuel aldana

Nessuno ha detto che non è possibile, solo che non è garantito. Era questo il tuo voto negativo?
Danben

Cosa intendi con "== test per l'uguaglianza degli oggetti"? Questo non mi sembra vero, ma forse intendevi qualcosa di diverso da quello che sembra significare.
Dawood ibn Kareem

32

Le stringhe sono oggetti "speciali" in Java. I progettisti Java hanno saggiamente deciso che le stringhe vengono utilizzate così spesso che avevano bisogno della propria sintassi e di una strategia di memorizzazione nella cache. Quando dichiari una stringa dicendo:

String myString = "something";

myString è un riferimento all'oggetto String con un valore "qualcosa". Se successivamente dichiari:

String myOtherString = "something";

Java è abbastanza intelligente da capire che myString e myOtherString sono la stessa cosa e le memorizzerà in una tabella String globale come lo stesso oggetto. Si basa sul fatto che non è possibile modificare le stringhe per farlo. Ciò riduce la quantità di memoria richiesta e può rendere i confronti più veloci.

Se invece scrivi

String myOtherString = new String("something");

Java creerà un oggetto nuovo di zecca per te, distinto dall'oggetto myString.


Ehi ... non richiede "infinita saggezza" per riconoscere la necessità di un qualche tipo di supporto sintattico per stringhe letterali. Quasi tutti gli altri linguaggi di programmazione seri supportano un qualche tipo di stringa letterale.
Stephen C

12
L'iperbole è stata ridotta allo stordimento, capitano :)
Jamie McCrindle

16
String a = "abc"; // 1 Object: "abc" added to pool

String b = "abc"; // 0 Object: because it is already in the pool

String c = new String("abc"); // 1 Object

String d = new String("def"); // 1 Object + "def" is added to the Pool

String e = d.intern(); // (e==d) is "false" because e refers to the String in pool

String f = e.intern(); // (f==e) is "true" 

//Total Objects: 4 ("abc", c, d, "def").

Spero che questo chiarisca alcuni dubbi. :)


Stringa d = nuova stringa ("def"); // 1 oggetto + "def" viene aggiunto al pool -> qui "def" verrà aggiunto al pool solo se non è ancora lì
southerton

@southerton Senza significato. È già in piscina. È stato posizionato lì dal compilatore.
Marchese di Lorne

@EJP perché (e == d) è falso qui? si riferiscono entrambi allo stesso oggetto "def" nella piscina giusto?
Raja

Stringa c = nuova stringa ("abc"); // 1 Oggetto ... questa affermazione è corretta? Se "abc" fa già riferimento dal pool di costanti, qual è l'uso del metodo inter?
Prashanth Debbadwar,

6

È una scorciatoia. In origine non era così, ma Java lo ha cambiato.

Questa FAQ ne parla brevemente. La guida alle specifiche Java ne parla anche. Ma non riesco a trovarlo online.


2
Collegamento interrotto e non sono a conoscenza di altre prove che sia mai stato modificato.
Marchese di Lorne

1
@EJP È ancora nella macchina del ritorno se è utile.
Arjan

6

La stringa è soggetta a un paio di ottimizzazioni (in mancanza di una frase migliore). Nota che String ha anche l'overloading dell'operatore (per l'operatore +), a differenza di altri oggetti. Quindi è un caso molto speciale.


1
Il + è in realtà un operatore che viene tradotto in una chiamata StringBuilder.append (..).
whiskeysierra

5

Di solito usiamo stringhe letterali per evitare di creare oggetti non necessari. Se usiamo un nuovo operatore per creare un oggetto String, creerà un nuovo oggetto ogni volta.

Esempio:

String s1=“Hello“;
String s2=“Hello“;
String s3= new String(“Hello“);
String s4= new String(“Hello“);

Per il codice sopra in memoria:

inserisci qui la descrizione dell'immagine


2

In Java, le stringhe sono un caso speciale, con molte regole che si applicano solo alle stringhe. Le virgolette doppie fanno sì che il compilatore crei un oggetto String. Poiché gli oggetti String non sono modificabili, ciò consente al compilatore di internare più stringhe e creare un pool di stringhe più grande. Due costanti String identiche avranno sempre lo stesso riferimento all'oggetto. Se non vuoi che sia così, puoi usare new String ("") e questo creerà un oggetto String in fase di esecuzione. Il metodo intern () era comune, per far sì che le stringhe create dinamicamente venissero confrontate con la tabella di ricerca delle stringhe. Una volta che una stringa è stata internata, il riferimento all'oggetto punterà all'istanza di String canonica.

    String a = "foo";
    String b = "foo";
    System.out.println(a == b); // true
    String c = new String(a);
    System.out.println(a == c); // false
    c = c.intern();
    System.out.println(a == c); // true

Quando il classloader carica una classe, tutte le costanti String vengono aggiunte al pool String.


"Le virgolette doppie fanno sì che il compilatore crei un oggetto String." commento sottovalutato
csguy

0

Zucchero sintattico. Il

String s = new String("ABC");

la sintassi è ancora disponibile.


1
Questo non è del tutto corretto. s = new String ("ABC") non darà gli stessi risultati di s = "ABC". Vedi il commento di danben.
Steve B.

2
Inoltre, un po 'ironicamente, creerà prima un'istanza String che rappresenta "ABC" inline - e poi la passerà come argomento alla chiamata del costruttore che creerà un ritorno a String di valore identico.
Andrzej Doyle

1
Il caso d'uso valido per questo costruttore è String small = new String(huge.substring(int, int));, che consente di riciclare il grande sottostante char[]dalla hugestringa originale .
Pascal Thivent

1
@PascalThivent sì, ma non più con Java 8. Non condivide più gli array (in preparazione per altre ottimizzazioni come la deduplicazione automatica delle stringhe con G1 o l'imminente compressione delle stringhe).
eckes

@AndrzejDoyle Non corretto. Il compilatore crea l'oggetto per il letterale.
Marchese di Lorne

0

Puoi ancora usare new String("string"), ma sarebbe più difficile creare nuove stringhe senza stringhe letterali ... dovresti usare matrici di caratteri o byte :-) Le stringhe letterali hanno una proprietà aggiuntiva: tutte le stesse stringhe letterali da qualsiasi classe puntano alla stessa stringa istanza (sono internati).


0

Non è quasi necessario creare una nuova stringa poiché il letterale (i caratteri tra virgolette) è già un oggetto String creato quando viene caricata la classe host. È perfettamente legale invocare metodi su un letterale e don, la distinzione principale è la comodità fornita dai letterali. Sarebbe un grosso problema e spreco di tempo se dovessimo creare un array di caratteri e riempirlo carattere per carattere e loro facendo una nuova stringa (array di caratteri).


0

Sentiti libero di creare una nuova stringa con

String s = new String("I'm a new String");

La solita notazione s = "new String";è più o meno una comoda scorciatoia, che dovrebbe essere utilizzata per motivi di prestazioni eccetto per quei casi piuttosto rari, in cui hai davvero bisogno di stringhe che si qualificano per l'equazione

(string1.equals(string2)) && !(string1 == string2)

MODIFICARE

In risposta al commento: questo non voleva essere un consiglio ma solo una risposta diretta alla tesi dell'interrogante, che non usiamo la parola chiave "nuova" per le stringhe, che semplicemente non è vera. Spero che questa modifica (incluso quanto sopra) lo chiarisca un po '. BTW - ci sono un paio di risposte buone e molto migliori alla domanda sopra su SO.


4
-1 - Cattivo consiglio. NON dovresti "sentirti libero" di usare A new String(...)MENO che la tua applicazione NON RICHIEDE di creare una stringa con un'identità distinta.
Stephen C

1
Lo so. Modificato il post per chiarimenti.
Andreas Dolk

0

Il pool letterale contiene tutte le stringhe create senza utilizzare la parola chiave new.

C'è una differenza: la stringa senza un nuovo riferimento viene archiviata nel pool letterale String e la stringa con new indica che si trovano nella memoria heap.

Le stringhe con new sono altrove nella memoria proprio come qualsiasi altro oggetto.


0

Perché String è una classe immutabile in java.

Ora perché è immutabile? Poiché String è immutabile, può essere condiviso tra più thread e non è necessario sincronizzare esternamente l'operazione String. As String viene utilizzato anche nel meccanismo di caricamento delle classi. Quindi, se String fosse mutabile, java.io.writer avrebbe potuto essere cambiato in abc.xyz.mywriter


0
TString obj1 = new TString("Jan Peter");                
TString obj2 = new TString("Jan Peter");                    

if (obj1.Name == obj2.Name)                 
    System.out.println("True");
else                
    System.out.println("False");

Produzione:

Vero

Ho creato due oggetti separati, entrambi hanno un campo (ref) 'Nome'. Quindi anche in questo caso "Jan Peter" è condiviso, se ho capito come funziona java ..


0

Ebbene lo StringPool è implementato usando The Hashmap in java. Se creiamo sempre con una nuova parola chiave non è possibile cercare nel pool di stringhe e creare una nuova memoria che potrebbe essere necessaria in seguito se abbiamo un'operazione ad alta intensità di memoria in esecuzione e se stiamo creando tutte le stringhe con una nuova parola chiave che influirebbe sulle prestazioni della nostra applicazione. Quindi è consigliabile non utilizzare nuove parole chiave per la creazione di stringhe perché solo allora andrà al pool di stringhe che a sua volta è un Hashmap, (memoria salvata, immagina se abbiamo molte stringhe create con una nuova parola chiave) qui verrà memorizzato e se la stringa esiste già, il riferimento ad essa (che normalmente risiede nella memoria Stack) verrebbe restituito alla stringa appena creata. Quindi è fatto per migliorare le prestazioni.

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.