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, который охватывает многие проблемы в OP, а также другие размышления ... ну ... безумие.
Это объясняет, почему это иногда полезно. И почему, в большинстве случаев, вам следует избегать этого. :-)