String
є незмінним *, але це означає лише, що ви не можете його змінити, використовуючи його загальнодоступний API.
Що ви тут робите, це обійти звичайний API, використовуючи рефлексію. Таким же чином, ви можете змінити значення перерахунків, змінити таблицю пошуку, яка використовується в автобоксінгу Integer і т.д.
Тепер причина s1
та s2
зміна значення полягають у тому, що вони обоє посилаються на один інтернований рядок. Укладач робить це (як згадується в інших відповідях).
Причина s3
насправді не була для мене трохи несподіваною, оскільки я думав, що вона поділить value
масив ( це було в попередній версії Java , перед Java 7u6). Однак, дивлячись на вихідний код String
, ми можемо побачити, що value
масив символів для підрядків насправді скопійовано (використовуючи Arrays.copyOfRange(..)
). Ось чому це залишається незмінним.
Ви можете встановити SecurityManager
, щоб уникнути зловмисного коду для таких дій. Але майте на увазі, що деякі бібліотеки залежать від використання подібних прийомів відображення (як правило, інструменти ORM, бібліотеки AOP тощо).
*) Я спочатку писав, що String
s насправді не незмінні, а лише "ефективні незмінні". Це може ввести в оману в поточній реалізації String
, де value
масив дійсно позначений private final
. Однак все-таки варто зауважити, що немає можливості оголосити масив у Java незмінним, тому слід бути обережним, щоб не виставляти його за межі свого класу, навіть із належними модифікаторами доступу.
Оскільки ця тема здається надзвичайно популярною, ось деякі поради щодо подальшого читання: Розмова про божевілля відбиття Хайнца Кабуца з JavaZone 2009, яка висвітлює багато питань ОП, а також інші роздуми ... ну ... божевілля.
Він висвітлює, чому це іноді корисно. І чому, більшість часу, вам слід цього уникати. :-)