String è immutabile * ma ciò significa solo che non è possibile modificarlo utilizzando la sua API pubblica.
Quello che stai facendo qui è aggirare l'API normale, usando la riflessione. Allo stesso modo, è possibile modificare i valori degli enum, modificare la tabella di ricerca utilizzata nell'autoboxing intero, ecc.
Ora, la ragione s1e il s2valore di modifica, è che entrambi si riferiscono alla stessa stringa internata. Il compilatore fa questo (come indicato da altre risposte).
Il motivo s3non è stato in realtà un po 'sorprendente per me, poiché pensavo che avrebbe condiviso l' valuearray ( lo faceva nella versione precedente di Java , prima di Java 7u6). Tuttavia, osservando il codice sorgente di String, possiamo vedere che l' valuearray di caratteri per una sottostringa viene effettivamente copiato (usando Arrays.copyOfRange(..)). Questo è il motivo per cui rimane invariato.
È possibile installare un SecurityManager, per evitare che codice dannoso esegua tali operazioni. Ma tieni presente che alcune librerie dipendono dall'uso di questo tipo di trucchi di riflessione (in genere strumenti ORM, librerie AOP ecc.).
*) Inizialmente ho scritto che Stringnon sono veramente immutabili, solo "efficaci immutabili". Ciò potrebbe essere fuorviante nell'attuale implementazione di String, dove l' valuearray è effettivamente contrassegnato private final. Vale comunque la pena notare che non c'è modo di dichiarare immutabile un array in Java, quindi bisogna fare attenzione a non esporlo al di fuori della sua classe, anche con i modificatori di accesso appropriati.
Dato che questo argomento sembra estremamente popolare, ecco alcuni suggerimenti suggeriti da leggere ulteriormente: il discorso Reflection Madness di Heinz Kabutz da JavaZone 2009, che copre molti problemi dell'OP, insieme ad altre riflessioni ... beh ... follia.
Copre il motivo per cui a volte questo è utile. E perché, il più delle volte, dovresti evitarlo. :-)