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);
}
}
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.