Una stringa Java è davvero immutabile?



6 Answers

In Java, se due variabili primitive stringa vengono inizializzate allo stesso valore letterale, assegna lo stesso riferimento a entrambe le variabili:

String Test1="Hello World";
String Test2="Hello World";
System.out.println(test1==test2); // true

Questa è la ragione per cui il confronto restituisce il vero. La terza stringa viene creata usando la substring() che crea una nuova stringa invece di puntare allo stesso.

Quando accedi a una stringa usando reflection, ottieni il puntatore attuale:

Field field = String.class.getDeclaredField("value");
field.setAccessible(true);

Quindi cambiare a questo cambierà la stringa mantenendo un puntatore ad esso, ma siccome s3 viene creato con una nuova stringa dovuta alla substring() non cambierebbe.

Question

Sappiamo tutti che String è immutabile in Java, ma controlla il seguente codice:

String s1 = "Hello World";  
String s2 = "Hello World";  
String s3 = s1.substring(6);  
System.out.println(s1); // Hello World  
System.out.println(s2); // Hello World  
System.out.println(s3); // World  

Field field = String.class.getDeclaredField("value");  
field.setAccessible(true);  
char[] value = (char[])field.get(s1);  
value[6] = 'J';  
value[7] = 'a';  
value[8] = 'v';  
value[9] = 'a';  
value[10] = '!';  

System.out.println(s1); // Hello Java!  
System.out.println(s2); // Hello Java!  
System.out.println(s3); // World  

Perché questo programma funziona così? E perché il valore di s1 e s2 cambiato, ma non s3 ?




Le stringhe vengono create nell'area permanente della memoria heap JVM. Quindi sì, è davvero immutabile e non può essere modificato dopo essere stato creato. Perché nella JVM ci sono tre tipi di memoria heap: 1. Giovane generazione 2. Vecchia generazione 3. Generazione permanente.

Quando viene creato un oggetto, passa all'area dell'heap della nuova generazione e all'area PermGen riservata per il pooling delle stringhe.

Ecco maggiori dettagli puoi andare e ottenere maggiori informazioni da: How Garbage Collection funziona in Java .




In base al concetto di raggruppamento, tutte le variabili String contenenti lo stesso valore punteranno allo stesso indirizzo di memoria. Pertanto s1 e s2, entrambi contenenti lo stesso valore di "Hello World", punteranno verso la stessa posizione di memoria (ad esempio M1).

D'altra parte, s3 contiene "World", quindi indicherà una diversa allocazione di memoria (ad esempio M2).

Quindi, ora ciò che sta accadendo è che il valore di S1 ​​viene modificato (usando il valore char []). Quindi il valore nella posizione di memoria M1 puntato sia da s1 che s2 è stato modificato.

Quindi, come risultato, è stata modificata la posizione di memoria M1 che causa il cambiamento nel valore di s1 e s2.

Ma il valore della posizione M2 rimane inalterato, quindi s3 contiene lo stesso valore originale.




Ci sono davvero due domande qui:

  1. Le stringhe sono davvero immutabili?
  2. Perché s3 non è cambiato?

Al punto 1: Tranne che per la ROM non c'è memoria immutabile nel tuo computer. Al giorno d'oggi anche la ROM è talvolta scrivibile. C'è sempre del codice da qualche parte (indipendentemente dal fatto che sia il kernel o il codice nativo a trascurare l'ambiente gestito) che può scrivere sull'indirizzo di memoria. Quindi, nella "realtà", no non sono assolutamente immutabili.

Per il punto 2: questo perché la sottostringa sta probabilmente allocando una nuova istanza di stringa, che probabilmente copia l'array. È possibile implementare la sottostringa in modo che non faccia una copia, ma ciò non significa che lo faccia. Ci sono dei compromessi coinvolti.

Ad esempio, se si tiene un riferimento a reallyLargeString.substring(reallyLargeString.length - 2) si mantiene in vita una grande quantità di memoria o solo pochi byte?

Questo dipende da come viene implementata la sottostringa. Una copia profonda manterrà meno memoria in vita, ma funzionerà leggermente più lentamente. Una copia superficiale manterrà più memoria viva, ma sarà più veloce. L'utilizzo di una copia profonda può anche ridurre la frammentazione dell'heap, poiché l'oggetto stringa e il relativo buffer possono essere allocati in un blocco, anziché in due allocazioni dell'heap separate.

In ogni caso, sembra che la tua JVM abbia scelto di utilizzare copie profonde per le sottostringhe.




Stai utilizzando la reflection per accedere ai "dettagli di implementazione" dell'oggetto stringa. L'immutabilità è la caratteristica dell'interfaccia pubblica di un oggetto.




La stringa è immutabile, ma tramite la riflessione è possibile modificare la classe String. Hai appena ridefinito la classe String come mutabile in tempo reale. Potresti ridefinire i metodi per essere pubblici o privati ​​o statici, se lo desideri.




L'immutabilità delle stringhe è dal punto di vista dell'interfaccia. Si sta utilizzando reflection per ignorare l'interfaccia e modificare direttamente i componenti interni delle istanze String.

s1 e s2 sono entrambi cambiati poiché sono entrambi assegnati alla stessa istanza String "interna". Puoi scoprire qualcosa in più su questa parte di questo articolo sull'equità delle stringhe e sull'internamento. Potresti essere sorpreso di scoprire che nel tuo codice di esempio, s1 == s2 restituisce true !






Related