Strings (i)mutáveis?

Strings (i)mutáveis?
trusso
trusso

Compartilhe

Todos sabemos que, em Java, Strings são objetos imutáveis. Sendo assim, qualquer chamada de método em uma referência para String NÃO irá alterar o valor da referenciada. Uma sugestão aqui é uma pesquisa sobre o pattern Flyweight que é utilizado nesta classe e justifica tal imutabilidade.

Exemplo:

 public class Teste {

public static void main(String \[\] args) throws Exception { String nome = "Thadeu de Russo e Carmo"; nome.toUpperCase(); System.out.println(nome); } } 
Banner da Escola de Programação: Matricula-se na escola de Programação. Junte-se a uma comunidade de mais de 500 mil estudantes. Na Alura você tem acesso a todos os cursos em uma única assinatura; tem novos lançamentos a cada semana; desafios práticos. Clique e saiba mais!

O output será Thadeu de Russo e Carmo.

Nos gabamos para nossos colegas quem estão no início de carreira em Java sobre tal conhecimento, que é cobrado inclusive na prova de certificação de programador. Poderiamos também criar uma função que "executasse" algumas alterações.

Algo como:

 public class Teste {

public static void main(String \[\] args) throws Exception { String nome = "Thadeu de Russo e Carmo"; alteraString(nome); System.out.println(nome); } } 

O output será Thadeu de Russo e Carmo.

Pelo que conhecemos, independentemente do conteúdo da função alteraString(String), nunca conseguiríamos alterar o conteúdo referenciado por nome, certo? Errado!!!!

Utilizando os recursos da API de Reflection, conseguimos facilmente realizar tal alteração. Mãos a obra!

 import java.lang.reflect.Field;

public class Teste {

public static void main(String \[\] args) throws Exception { String nome = "Thadeu de Russo e Carmo"; alteraString(nome); System.out.println(nome); } public static void alteraString(String nome) throws Exception { Field value = String.class.getDeclaredField("value"); //1 value.setAccessible(true); //2 char \[\] charsDaString = (char \[\]) value.get(nome); // 3 charsDaString\[0\] = 't'; charsDaString\[1\] = 'H'; charsDaString\[2\] = 'A'; charsDaString\[3\] = 'D'; charsDaString\[4\] = 'E'; } } 

Uow!!!! Mágica? Vamos ao que esta acontecendo nas linhas numeradas.

linha 1 - Estamos pegando uma referencia para um objeto do tipo java.lang.reflect.Field para o atributo "value" da classe String. linha 2 - Este é um campo privado (basta olhar o fonte da classe String) e para conseguirmos acesso a ele, temos que "nos dar acesso". (Olha o encapsulamento indo para o buraco!!) linha 3 - Na instancia que eu tenho (nome), eu desejo pegar o field value (que é o array onde os caracteres são armazenados).

Nas demais linhas nós simplesmente trocamos os valores dos caracteres. O que será que teremos impresso? Ta-da!! "tHADEu de Russo e Carmo". Opa, algo diferente do que haviamos afirmado mais acima. Vale afirmar que fazer tal modificação em uma String é uma péssima idéia, dado o pool de Strings: alguém pode estar compartilhando essa String com você, e enxergará essa alteração sem ter previsto tal acontecimento!

O que fizemos no código acima foi, simplemente brincar com a API de Reflection. API esta que permite coisas como injeção de dependência como no EJB3, vRaptor, JBoss Seam, etc. Aqueles que já trabalharam com os velhos applets devem estar pensando: "Achei uma falha de segurança no Java!". Isso não é verdade pois dentro do método setAccessible(boolean) da classe Field, existe uma validação junto ao SecurityManager.

Vale atentar ao fato de que é possível alterar tais propriedades no arquivo java.policy que fica sob a pasta %JAVA_HOME%\jre\lib\security.

Finalizando, aqueles que não conheciam ou não sabiam o poder da API de Reflection, viram que com um simples código, conseguimos "mutar" uma String, imaginem com um pouco mais de código o que não é possível de se fazer.

Veja outros artigos sobre Programação