Perché String è immutabile in Java?

1. Introduzione

In Java, le stringhe sono immutabili. Una domanda ovvia che è abbastanza prevalente nelle interviste è "Perché le stringhe sono progettate come immutabili in Java?"

James Gosling, il creatore di Java, una volta è stato chiesto in un'intervista quando si dovrebbe usare immutabili, a cui risponde:

Userei un immutabile ogni volta che posso.

Supporta ulteriormente la sua argomentazione affermando le funzionalità fornite dall'immutabilità, come la memorizzazione nella cache, la sicurezza, il facile riutilizzo senza replica, ecc.

In questo tutorial, esploreremo ulteriormente il motivo per cui i progettisti del linguaggio Java hanno deciso di mantenere String immutabile.

2. Che cos'è un oggetto immutabile?

Un oggetto immutabile è un oggetto il cui stato interno rimane costante dopo che è stato interamente creato . Ciò significa che una volta che l'oggetto è stato assegnato a una variabile, non possiamo né aggiornare il riferimento né modificare lo stato interno in alcun modo.

Abbiamo un articolo separato che discute in dettaglio gli oggetti immutabili. Per ulteriori informazioni, leggi l'articolo Immutable Objects in Java.

3. Perché la stringa è immutabile in Java?

I principali vantaggi di mantenere questa classe immutabile sono la memorizzazione nella cache, la sicurezza, la sincronizzazione e le prestazioni.

Parliamo di come funzionano queste cose.

3.1. Introduzione a String Pool

La stringa è la struttura dati più utilizzata. La memorizzazione nella cache dei valori letterali String e il loro riutilizzo consente di risparmiare molto spazio nell'heap perché variabili String diverse si riferiscono allo stesso oggetto nel pool String . Il pool di stagisti di stringhe serve esattamente a questo scopo.

Java String Pool è la regione di memoria speciale in cui le stringhe vengono memorizzate dalla JVM . Poiché le stringhe non sono modificabili in Java, la JVM ottimizza la quantità di memoria allocata per esse memorizzando solo una copia di ogni stringa letterale nel pool. Questo processo è chiamato interning:

String s1 = "Hello World"; String s2 = "Hello World"; assertThat(s1 == s2).isTrue();

A causa della presenza del pool String nell'esempio precedente, due variabili diverse puntano allo stesso oggetto String dal pool, risparmiando così una risorsa di memoria cruciale.

Abbiamo un articolo separato dedicato a Java String Pool. Per ulteriori informazioni, vai a quell'articolo.

3.2. Sicurezza

La stringa è ampiamente utilizzata nelle applicazioni Java per memorizzare informazioni sensibili come nomi utente, password, URL di connessione, connessioni di rete, ecc. È anche ampiamente utilizzata dai programmi di caricamento classi JVM durante il caricamento delle classi.

Quindi proteggere la classe String è cruciale per la sicurezza dell'intera applicazione in generale. Ad esempio, considera questo semplice snippet di codice:

void criticalMethod(String userName) { // perform security checks if (!isAlphaNumeric(userName)) { throw new SecurityException(); } // do some secondary tasks initializeDatabase(); // critical task connection.executeUpdate("UPDATE Customers SET Status = 'Active' " + " WHERE UserName = '" + userName + "'"); }

Nello snippet di codice precedente, diciamo di aver ricevuto un oggetto String da una fonte inaffidabile. Inizialmente stiamo eseguendo tutti i controlli di sicurezza necessari per verificare se la stringa è solo alfanumerica, seguita da altre operazioni.

Ricorda che il nostro metodo chiamante sorgente inaffidabile fa ancora riferimento a questo oggetto userName .

Se le stringhe fossero mutabili, nel momento in cui eseguiremo l'aggiornamento, non possiamo essere sicuri che la stringa che abbiamo ricevuto, anche dopo aver eseguito i controlli di sicurezza, sarebbe al sicuro. Il metodo del chiamante inaffidabile ha ancora il riferimento e può modificare la stringa tra i controlli di integrità. In questo caso, rendendo la nostra query soggetta a iniezioni SQL. Pertanto, stringhe mutevoli potrebbero portare al degrado della sicurezza nel tempo.

Potrebbe anche accadere che la stringa userName sia visibile a un altro thread, che potrebbe quindi modificare il suo valore dopo il controllo di integrità.

In generale, l'immutabilità viene in nostro soccorso in questo caso perché è più facile operare con codice sensibile quando i valori non cambiano perché ci sono meno intrecci di operazioni che potrebbero influenzare il risultato.

3.3. Sincronizzazione

Essere immutabili automaticamente rende il thread String sicuro poiché non verranno modificati quando si accede da più thread.

Quindi gli oggetti immutabili, in generale, possono essere condivisi tra più thread in esecuzione simultaneamente. Sono anche thread-safe perché se un thread cambia il valore, invece di modificare lo stesso, verrebbe creata una nuova stringa nel pool di stringhe . Quindi, le stringhe sono sicure per il multi-threading.

3.4. Caching hashcode

Poiché gli oggetti String sono ampiamente utilizzati come struttura dati, sono anche ampiamente utilizzati in implementazioni hash come HashMap , HashTable , HashSet , ecc. Quando si opera su queste implementazioni hash, il metodo hashCode () viene chiamato abbastanza frequentemente per il bucket.

L'immutabilità garantisce alle stringhe che il loro valore non cambierà. Quindi il metodo hashCode () viene sovrascritto nella classe String per facilitare la memorizzazione nella cache, in modo tale che l'hash venga calcolato e memorizzato nella cache durante la prima chiamata hashCode () e da allora viene restituito lo stesso valore.

Questo, a sua volta, migliora le prestazioni delle raccolte che usano implementazioni hash quando vengono gestite con oggetti String .

D'altra parte, stringhe mutabili produrrebbe due diversi codici hash al momento dell'inserimento e del recupero se il contenuto di String fosse modificato dopo l'operazione, perdendo potenzialmente il valore dell'oggetto nella mappa .

3.5. Prestazione

Come abbiamo visto in precedenza, il pool di stringhe esiste perché le stringhe sono immutabili. A sua volta, migliora le prestazioni risparmiando memoria heap e un accesso più rapido alle implementazioni hash quando utilizzato con stringhe.

Poiché String è la struttura dati più utilizzata, il miglioramento delle prestazioni di String ha un effetto considerevole sul miglioramento delle prestazioni dell'intera applicazione in generale.

4. Conclusione

Attraverso questo articolo, possiamo concludere che le stringhe sono immutabili proprio in modo che i loro riferimenti possano essere trattati come una normale variabile e si possa passarli in giro, tra metodi e thread, senza preoccuparsi se l'effettivo oggetto String a cui punta cambierà.

Abbiamo anche appreso quali potrebbero essere gli altri motivi che hanno spinto i progettisti del linguaggio Java a rendere questa classe immutabile.