String jest niezmienny *, ale oznacza to tylko, że nie można go zmienić za pomocą jego publicznego interfejsu API.
To, co tu robisz, to obchodzenie normalnego API za pomocą odbicia. W ten sam sposób możesz zmienić wartości wyliczeń, zmienić tabelę odnośników używaną w autoboksowaniu liczb całkowitych itp.
Powodem s1i s2wartością zmiany jest to, że oba odnoszą się do tego samego łańcucha wewnętrznego. Kompilator to robi (jak wspomniano w innych odpowiedziach).
Powodem s3jest nie był faktycznie nieco zaskakujące dla mnie, jak myślałem, że dzielić valuearray ( miało to miejsce w poprzedniej wersji Java , zanim 7u6 Java). Jednak patrząc na kod źródłowy String, widzimy, że valuetablica znaków dla podłańcucha jest faktycznie kopiowana (za pomocą Arrays.copyOfRange(..)). Dlatego pozostaje niezmieniony.
Możesz zainstalować SecurityManager, aby uniknąć złośliwego kodu, aby robić takie rzeczy. Pamiętaj jednak, że niektóre biblioteki zależą od korzystania z tego rodzaju sztuczek refleksyjnych (zazwyczaj narzędzia ORM, biblioteki AOP itp.).
*) Początkowo napisałem, że Stringtak naprawdę nie są niezmienne, tylko „skuteczne niezmienne”. Może to wprowadzać w błąd w obecnej implementacji String, gdzie valuetablica jest rzeczywiście oznaczona private final. Warto jednak zauważyć, że nie ma sposobu, aby zadeklarować tablicę w Javie jako niezmienną, dlatego należy zachować ostrożność, aby nie ujawniać jej poza klasą, nawet przy odpowiednich modyfikatorach dostępu.
Ponieważ ten temat wydaje się niezwykle popularny, oto kilka sugerowanych lektur: Dyskusja Heinza Kabutza Reflection Madness z JavaZone 2009, która obejmuje wiele problemów w OP, wraz z innymi refleksjami ... cóż ... szaleństwem.
Obejmuje to, dlaczego czasami jest to przydatne. I dlaczego w większości przypadków należy tego unikać. :-)