Perché String è immutabile in Java?


177

In un'intervista mi è stato chiesto perché String sia immutabile

Ho risposto così:

Quando creiamo una stringa in Java come String s1="hello";allora un oggetto verrà creato nel pool di stringhe (ciao) e s1 punterà a ciao . Ora, se lo facciamo di String s2="hello";nuovo, non verrà creato un altro oggetto ma s2 punterà a hello perché JVM controllerà prima se lo stesso oggetto è presente nel pool di stringhe oppure no. Se non presente, ne viene creato solo uno nuovo, altrimenti.

Ora se supponiamo che java consenta la modifica della stringa, se cambiamo s1 in hello worldallora il valore di s2 sarà anche hello worldcosì java String è immutabile.

Qualcuno può dirmi se la mia risposta è giusta o sbagliata ?


46
Perché è sempre difficile rispondere. La risposta più corretta è probabilmente: poiché i progettisti del linguaggio hanno pensato che fosse una buona idea.
Keppil,

1
vedi anche questa risposta

3
La tua risposta non è sul punto. Il C ++ std::stringè mutabile, ma hanno anche un pool di stringhe (bene, più correttamente, pool di array di caratteri).
Siyuan Ren,

1
@rocking Ad essere onesti, che sia giusto o meno dipende da come lo leggono. Il fatto è che Java può avere un pool di stringhe perché le stringhe sono immutabili. Se avessero deciso di rendere mutabili le stringhe, non avrebbero usato un pool di stringhe; quindi potrebbe non essere preciso dire "pool di stringhe, quindi stringhe immutabili"; è più il contrario. Le ragioni per scegliere stringhe immutabili sono descritte di seguito e il pool di stringhe è una strategia funzionante a causa di ciò. Tuttavia, la tua risposta non è errata , non sembra completa. Devi solo aspettare e vedere cosa dicono.
Jason C

34
Semplicemente non riesco a capire perché questa domanda sia stata chiusa. La presunta risposta correlata non riguarda nemmeno Java e non affronta l'argomento principale di questa domanda, che è "perché". Per me questo è uno di quei casi di una comunità irresponsabile che agisce su una questione di cui non sanno nulla. L'ho nominato per riaprire.
Edwin Dalorzo,

Risposte:


163

String è immutabile per diversi motivi, ecco un riassunto:

  • Sicurezza : i parametri sono in genere rappresentati come Stringnelle connessioni di rete, negli URL di connessione al database, nei nomi utente / password ecc. Se fosse mutabile, questi parametri potrebbero essere facilmente modificati.
  • Sincronizzazione e concorrenza: rendere String immutabile rende automaticamente i thread sicuri, risolvendo così i problemi di sincronizzazione.
  • Caching : quando il compilatore ottimizza i tuoi oggetti String, vede che se due oggetti hanno lo stesso valore (a = "test" e b = "test") e quindi hai bisogno di un solo oggetto stringa (sia per a che per questi due puntare allo stesso oggetto).
  • Caricamento di classe : Stringviene utilizzato come argomento per il caricamento di classe. Se mutabile, potrebbe comportare il caricamento di una classe errata (poiché gli oggetti mutabili cambiano il loro stato).

Detto questo, l'immutabilità Stringsignifica solo che non puoi cambiarlo usando la sua API pubblica. Puoi infatti bypassare l'API normale usando reflection. Vedi la risposta qui .

Nel tuo esempio, se Stringfosse mutabile, considera il seguente esempio:

  String a="stack";
  System.out.println(a);//prints stack
  a.setValue("overflow");
  System.out.println(a);//if mutable it would print overflow

14
Come potrebbe influire sulla sicurezza?
Archit Maheshwari,

2
Qualcuno può spiegare il caricamento della classe con un esempio se possibile?
Viraj,

6
Per quanto riguarda la sicurezza, se sono interessato a modificare i parametri di connessione, è semplice in fase di esecuzione (con un debugger, ecc.). Per quanto riguarda il caricamento della classe, se Stringè modificabile, il caricatore di classe prenderà la stringa passata, ne farebbe una copia e non cambierebbe la sua copia. Quando pensi a un problema con i mutabili java.lang.String, pensa a come C ++ risolve questo problema (dal momento che ha mutabili std::stringi.
Espiazione limitata

Per quanto riguarda la sicurezza, come può essere modificata una stringa mutabile quando il programma è in esecuzione?
MasterJoe2,

Poiché String è immutabile, il suo hashcode viene memorizzato nella cache al momento della creazione e non deve essere nuovamente calcolato.
Abdul Alim Shakir,

45

Gli sviluppatori Java decidono che le stringhe sono immutabili a causa del seguente aspetto , efficienza e sicurezza .

Le stringhe di progettazione vengono create in un'area di memoria speciale nell'heap java noto come "String Intern pool". Durante la creazione di una nuova stringa (non nel caso dell'utilizzo del costruttore String () o di qualsiasi altra funzione String che utilizza internamente il costruttore String () per creare un nuovo oggetto String; il costruttore String () crea sempre una nuova costante stringa nel pool a meno che non chiama la variabile metodo intern () ) che cerca nel pool per verificare se esiste già. Se esiste, restituisce il riferimento dell'oggetto String esistente. Se la stringa non è immutabile, cambiando la stringa con un riferimento si otterrà un valore errato per gli altri riferimenti.

Secondo questo articolo su DZone:

La stringa di sicurezza è ampiamente utilizzata come parametro per molte classi java, ad es. Connessione di rete, apertura di file, ecc. Se la stringa non fosse immutabile, una connessione o un file verrebbero modificati e porterebbe a gravi minacce alla sicurezza. Le stringhe mutabili potrebbero causare problemi di sicurezza anche in Reflection, poiché i parametri sono stringhe.

Efficienza L'hashcode della stringa viene spesso utilizzato in Java. Ad esempio, in una HashMap. Essere immutabili garantisce che l'hashcode sarà sempre lo stesso, in modo che possa essere memorizzato nella cache senza preoccuparsi delle modifiche. Ciò significa che non è necessario calcolare l'hashcode ogni volta che viene utilizzato.


7
La tua comprensione del pool di stringhe non è corretta. Le costanti di stringa vengono create nel pool interno, ma è perfettamente possibile avere più di un oggetto stringa con lo stesso testo. Sono d'accordo sul fatto che le stringhe essendo immutabili consentono il pooling, ma non c'è così tanto pooling come hai affermato.
Jon Skeet,

@JonSkeet Hai ragione. String s1 = new String ("test"); crea una nuova costante di stringa nel pool interno a meno che non chiamiamo il metodo intern (). Grazie per approfondire le mie conoscenze sul pool di stagisti di stringhe.
Alex Mathew,

2
È molto più che usare semplicemente il costruttore di stringhe: quasi tutto ciò che crea una nuova stringa, ad esempio sottostringa, divisione, concat, ecc, creerà nuove stringhe. Le costanti del tempo di compilazione sono il caso speciale qui, non la norma ...
Jon Skeet,

@JonSkeet substring (), concat (), replace () ecc. Utilizzano internamente il costruttore String per creare un nuovo oggetto stringa. Grazie per aver migliorato la mia risposta.
Alex Mathew,

2
@JonSkeet - Tutte queste risposte affermano che l'immutabilità migliora la "sicurezza", ma non spiega come. Tutti si collegano a un vago articolo sulla Dzone che non aiuta neanche. Le risposte / link non spiegano come una stringa mutabile possa essere cambiata quando il codice è in esecuzione. Potresti spiegare per favore?
MasterJoe2,

25

Non possiamo essere sicuri di cosa stessero realmente pensando i progettisti Java durante la progettazione, Stringma possiamo solo concludere queste ragioni sulla base dei vantaggi che otteniamo dall'immutabilità delle stringhe, alcune delle quali sono

1. Esistenza del pool di costanti di stringhe

Come discusso nell'articolo Perché String è memorizzato nell'articolo String Constant Pool , ogni applicazione crea troppi oggetti stringa e al fine di salvare JVM dalla prima creazione di molti oggetti stringa e quindi dalla loro immondizia. JVM memorizza tutti gli oggetti stringa in un'area di memoria separata chiamata Pool costante di stringhe e riutilizza gli oggetti da quel pool memorizzato nella cache.

Ogni volta che creiamo una stringa JVM letterale per prima cosa vede se quel letterale è già presente nel pool costante o meno e se è presente, un nuovo riferimento inizierà a puntare allo stesso oggetto in SCP.

String a = "Naresh";
String b = "Naresh";
String c = "Naresh";

Supra oggetto stringa esempio con valore Nareshandranno creato nel SCP sola volta e ogni riferimento a, b, cpunterà allo stesso oggetto ma se si cerca di apportare cambiamenti in aes a.replace("a", "").

Idealmente, adovrebbe avere valore Nreshma b, cdovrebbe rimanere invariato perché come utente finale stiamo effettuando la modifica asolo. E sappiamo a, b, ctutti stanno indicando lo stesso oggetto quindi se facciamo un cambiamento a, altri dovrebbe anche riflettere il cambiamento.

Ma l'immutabilità delle stringhe ci salva da questo scenario e, a causa dell'immutabilità dell'oggetto stringa, l'oggetto stringa Nareshnon cambierà mai. Pertanto, quando apportiamo modifiche aanziché modifiche nell'oggetto stringa, NareshJVM crea un nuovo oggetto assegnandolo ae quindi apportando le modifiche in tale oggetto.

Quindi il pool di stringhe è possibile solo a causa dell'immutabilità di String e se String non fosse stato immutabile, la memorizzazione nella cache degli oggetti stringa e il loro riutilizzo non avrebbero possibilità perché qualsiasi variabile avrebbe cambiato il valore e danneggiato altri.

Ed è per questo che è gestito da JVM in modo molto speciale e ha ricevuto un'area di memoria speciale.

2. Sicurezza del filo

Un oggetto viene chiamato thread-safe quando più thread ci stanno operando, ma nessuno di essi è in grado di corromperne lo stato e l'oggetto mantiene lo stesso stato per ogni thread in qualsiasi momento.

Dato che un oggetto immutabile non può essere modificato da nessuno dopo la sua creazione, il che rende ogni oggetto immutabile sicuro per impostazione predefinita. Non è necessario applicare alcuna misura di sicurezza del thread come la creazione di metodi sincronizzati.

Quindi, a causa della sua natura immutabile, l'oggetto stringa può essere condiviso da più thread e anche se viene manipolato da molti thread non cambierà il suo valore.

3. Sicurezza

In ogni applicazione, è necessario passare diversi segreti, ad esempio nome utente \ password, URL di connessione e, in generale, tutte queste informazioni vengono passate come oggetto stringa.

Supponiamo ora che String non sarebbe stato immutabile in natura, quindi causerebbe una grave minaccia alla sicurezza dell'applicazione perché questi valori possono essere modificati e se è consentito, questi potrebbero essere modificati a causa di un codice scritto erroneamente o di qualsiasi altra persona che avere accesso ai nostri riferimenti variabili.

4. Caricamento classe

Come discusso in Creazione di oggetti tramite Reflection in Java con Esempio , possiamo usare il Class.forName("class_name")metodo per caricare una classe in memoria che chiama nuovamente altri metodi per farlo. E anche JVM utilizza questi metodi per caricare le classi.

Ma se vedi chiaramente tutti questi metodi accetta il nome della classe come oggetto stringa, quindi le stringhe vengono utilizzate nel caricamento della classe java e l'immutabilità garantisce la sicurezza da cui viene caricata la classe corretta ClassLoader.

Supponiamo che String non sia immutabile e stiamo provando a caricare i java.lang.Objectquali vengono cambiati org.theft.OurObjectnel mezzo e ora tutti i nostri oggetti hanno un comportamento che qualcuno può usare per cose indesiderate.

5. HashCode nella cache

Se eseguiremo operazioni correlate all'hash su qualsiasi oggetto, dovremo sovrascrivere il hashCode()metodo e provare a generare un hashcode accurato utilizzando lo stato dell'oggetto. Se lo stato di un oggetto viene modificato, significa che dovrebbe cambiare anche il suo hashcode.

Poiché String è immutabile, quindi il valore di un oggetto stringa non verrà mai modificato, il che significa che anche il suo hashcode non cambierà, dando alla classe String l'opportunità di memorizzare nella cache il suo hashcode durante la creazione dell'oggetto.

Sì, l'oggetto String memorizza nella cache il suo hashcode al momento della creazione dell'oggetto, il che lo rende il candidato ideale per le operazioni correlate all'hash perché non è necessario calcolare nuovamente l'hashcode che ci fa risparmiare un po 'di tempo. Questo è il motivo per cui String è usato principalmente come HashMapchiavi.

Maggiori informazioni sul motivo per cui String è immutabile e finale in Java .


1
Per quanto riguarda la sicurezza: come può essere modificato in memoria il valore della stringa mutabile? Come può un'altra persona accedere ai nostri riferimenti variabili?
MasterJoe2,

Non si tratta di come si può accedere ai riferimenti, ma se qualcuno ha accesso a quelli? come accennato "se String non fosse stato immutabile in natura, ciò causerebbe una grave minaccia alla sicurezza dell'applicazione poiché questi valori possono essere modificati e, se consentito, potrebbero essere modificati a causa di codice scritto erroneamente o di qualsiasi altra persona che hanno accesso ai nostri riferimenti variabili ".
Naresh Joshi,

Il come conta qui. È possibile ottenere l'accesso ai riferimenti oppure no. Se possibile, allora puoi nominare 1-2 tecniche *** (ovvero il come) che possono essere utilizzate per farlo? Se non è possibile, il punto sulla sicurezza non è applicabile. *** Esempio: nomina una tecnica per attaccare il DB di un'app Web -> SQL Injection. Conosci qualche tecnica come questa per attaccare i riferimenti?
MasterJoe2,

Come accennato, "Può accadere a causa di un codice scritto erroneamente o di eventuali modifiche apportate da un'altra persona che ha accesso ai nostri riferimenti variabili". es. Supponiamo che String sia mutabile e tu stia scrivendo un metodo che sta usando una stringa segreta e di nuovo quella stringa viene passata a diversi altri metodi tra loro e uno di quei metodi non è scritto da te e quel metodo ha apportato alcune modifiche la stringa ora dopo aver chiamato tutti questi metodi di controllo ha restituito il metodo e la stringa viene nuovamente utilizzata ma è stata modificata.
Naresh Joshi,

2
Si prega di rivelare eventuali affiliazioni e non utilizzare il sito come mezzo per promuovere il proprio sito attraverso la pubblicazione. Vedi Come posso scrivere una buona risposta? .
Yvette,

21

La ragione più importante secondo questo articolo su DZone:

Pool di costanti stringa ... Se la stringa è mutabile, la modifica della stringa con un riferimento comporterà un valore errato per gli altri riferimenti.

Sicurezza

String è ampiamente usato come parametro per molte classi java, ad es. Connessione di rete, apertura di file, ecc. Se String non fosse immutabile, una connessione o un file verrebbero modificati e porterebbe a gravi minacce alla sicurezza. ...

Spero che ti possa aiutare.


@JasonC Voglio solo sapere se la mia risposta è sbagliata o no. Avevo già partecipato al colloquio e aspettavo il risultato. Se la risposta ha detto loro è giusto, allora sarò selezionato
scuotendo il

1
Per quanto ne so, la tua risposta è corretta, ma immutabile significa che il riferimento non cambierà mai la posizione di puntamento. Tutto il meglio per il tuo colloquio.
JDGuide

1
Se accetta il punto n. 1, tutti gli oggetti dovrebbero essere immutabili.
Nicomp,

Ciao JDeveloper, ho modificato la tua risposta per dare la giusta attribuzione alla fonte della tua risposta. Ricorda di usare sempre le virgolette per le copie testuali del contenuto. Grazie!
NickL

L'articolo di DZone contiene importanti errori sul funzionamento del pool Strign. È solo per le costanti. Ergo la logica dichiarata non è valida.
Marchese di Lorne,

4

Ho letto questo post Perché String è immutabile o finale in Java e suppongo che il seguente possa essere il motivo più importante:

String è immutabile in Java perché gli oggetti String sono memorizzati nella cache del pool String . Poiché i valori letterali String memorizzati nella cache sono condivisi tra più client, esiste sempre un rischio, in cui l'azione di un client influirebbe su un altro client.


1

Hai ragione. Stringin java usa il concetto di String Poolletterale. Quando viene creata una stringa e se la stringa esiste già nel pool, verrà restituito il riferimento della stringa esistente, invece di creare un nuovo oggetto e restituirne il riferimento. Se una stringa non è immutabile, modificando la stringa con un riferimento verrà portare a un valore errato per gli altri riferimenti.

Vorrei aggiungere un'altra cosa, poiché Stringè immutabile, è sicuro per il multi threading e una singola istanza String può essere condivisa tra thread diversi. Questo evita l'uso della sincronizzazione per la sicurezza dei thread, le stringhe sono implicitamente thread safe.


0

Classe di stringa FINALsignifica che non è possibile creare alcuna classe per ereditarla e modificare la struttura di base e rendere mutevole la puntura.

Un'altra variabile di istanza e i metodi della classe String forniti sono tali che non è possibile modificare l' Stringoggetto una volta creato.

Il motivo che hai aggiunto non rende affatto la stringa immutabile. Tutto ciò dice come la stringa è memorizzata nell'heap. Anche il pool di stringhe fa l'enorme differenza nelle prestazioni


11
Se la classe dichiarata come definitiva, significa che la classe non può essere ereditata, ma ciò non significa che i campi delle istanze della classe non possano essere modificati e quindi la classe è immutabile.
Dmitry Bychenko

@Zeeshan: le classi di esempio che dai sono tutte immutabili.
Siyuan Ren,

0

La stringa viene data come immutabile dai micro sistemi Sun, poiché può essere utilizzata per archiviare come chiave nella raccolta mappe. StringBuffer è modificabile. Questo è il motivo, non può essere utilizzato come chiave nell'oggetto mappa


0

Il motivo più importante per cui una stringa viene resa immutabile in Java è la considerazione della sicurezza . Il prossimo sarebbe il caching .

Credo che altri motivi qui indicati, come l'efficienza, la concorrenza, il design e il pool di stringhe derivino dal fatto che String è diventato immutabile. Per es. String Pool potrebbe essere creato perché String era immutabile e non viceversa.

Controlla qui la trascrizione dell'intervista a Gosling

Da un punto di vista strategico, tendono ad essere più spesso senza problemi. E di solito ci sono cose che puoi fare con immutabili che non puoi fare con cose mutabili, come cache il risultato. Se si passa una stringa a un metodo di apertura file o se si passa una stringa a un costruttore per un'etichetta in un'interfaccia utente, in alcune API (come in molte API di Windows) si passa in una matrice di caratteri. Il destinatario di quell'oggetto deve davvero copiarlo, perché non sa nulla della sua durata di archiviazione. E non sanno cosa sta succedendo all'oggetto, se sta cambiando sotto i loro piedi.

Finisci per essere quasi costretto a replicare l'oggetto perché non sai se puoi possederlo o meno. E una delle cose belle degli oggetti immutabili è che la risposta è "Sì, certo che lo fai." Perché la questione della proprietà, che ha il diritto di cambiarla, non esiste.

Una delle cose che ha costretto le stringhe a essere immutabili è stata la sicurezza. Hai un metodo di apertura file. Gli passi una stringa. E poi sta facendo tutti i tipi di controlli di autenticazione prima di iniziare a fare la chiamata del sistema operativo. Se riesci a fare qualcosa che ha effettivamente mutato la stringa, dopo il controllo di sicurezza e prima della chiamata del sistema operativo, poi boom, sei dentro. Ma le stringhe sono immutabili, quindi quel tipo di attacco non funziona. Questo esempio preciso è ciò che ha veramente richiesto che le stringhe fossero immutabili


0

Oltre alle ottime risposte, volevo aggiungere alcuni punti. Come le stringhe, l'array contiene un riferimento all'avvio dell'array, quindi se si creano due array arr1e se si arr2fa qualcosa del genere arr2 = arr1si farà il riferimento allo arr2stesso, arr1quindi la modifica del valore in uno di essi comporterà il cambio dell'altro, ad esempio

public class Main {
    public static void main(String[] args) {
        int[] a = {1, 2, 3, 4};
        int[] b = a;
        a[0] = 8;
        b[1] = 7;
        System.out.println("A: " + a[0] + ", B: " + b[0]);
        System.out.println("A: " + a[1] + ", B: " + b[1]);
        //outputs
        //A: 8, B: 8
        //A: 7, B: 7
    }
}

Non solo causerebbe bug nel codice ma può (e sarà) essere sfruttato da utenti malintenzionati. Supponiamo che tu abbia un sistema che modifica la password dell'amministratore. L'utente deve prima inserire il newPassworde poi il oldPasswordse oldPasswordè uguale adminPassal programma cambiare la password di adminPass = newPassword. diciamo che la nuova password ha lo stesso riferimento della password dell'amministratore, quindi un programmatore errato può creare una tempvariabile per conservare la password dell'amministratore prima che l'utente inserisca i dati se oldPasswordè uguale tempcambia la password altrimentiadminPass = temp. Qualcuno sa che potrebbe facilmente inserire la nuova password e non inserire mai la vecchia password e abracadabra che ha accesso come amministratore. Un'altra cosa che non ho capito quando ho appreso sulle stringhe perché JVM non crea una nuova stringa per ogni oggetto e ha un posto unico in memoria per esso e puoi semplicemente farlo usando new String("str");Il motivo che non vorresti usare sempre newè perché non è efficiente in termini di memoria ed è più lento nella maggior parte dei casi leggi di più .


0

Se HELLOè la vostra String allora non si può cambiare HELLOa HILLO. Questa proprietà si chiama proprietà di immutabilità.

Puoi avere più variabili String puntatore per puntare HELLO String.

Ma se HELLO è Char array, allora puoi cambiare HELLO in HILLO. Per esempio,

char[] charArr = 'HELLO';
char[1] = 'I'; //you can do this

Risposta:

I linguaggi di programmazione hanno variabili di dati immutabili in modo che possano essere utilizzati come chiavi nella chiave, coppia di valori. Le variabili stringa sono usate come chiavi / indici, quindi sono immutabili .


-1

Dal Securitypunto di vista possiamo usare questo esempio pratico:

DBCursor makeConnection(String IP,String PORT,String USER,String PASS,String TABLE) {

    // if strings were mutable IP,PORT,USER,PASS can be changed by validate function
    Boolean validated = validate(IP,PORT,USER,PASS);

    // here we are not sure if IP, PORT, USER, PASS changed or not ??
    if (validated) {
         DBConnection conn = doConnection(IP,PORT,USER,PASS);
    }

    // rest of the code goes here ....
}
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.