Strings (i)mutáveis?
Por Thadeu Russo em 21/05/07Todos 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.
“Pelo que conhecemos, independentemente do conteúdo da função alteraString(String), nunca conseguiríamos alterar o conteúdo referenciado por nome, certo? Errado!!!!”
Errado mesmo…pelo que conhecemos a função alteraString(String) pode fazer o que quiser com o objeto nome de maneira muito simples sem uso de nenhuma API de reflexão. Como por exemplo:
public void alteraString(String str){ str = "Outro valor"; }Eu acho que o que o Thadeu quis mostrar foi algo como:
main(String[] args){ mutaString(); System.out.println("Thadeu de Russo"); }Onde quando todos esperam que o output seja “Thadeu de Russo” na verdade pode ser qualquer outra coisa se no método mutaString() foi usada a API de reflexão.
Mais detalhes dessa brincadeira com Strings pode ser visto no site Sulafricano http://www.javaspecialists.co.za/archive/newsletter.do?issue=014&locale=en_US
Comment by Guilherme Santos — May 21, 2007 @ 1:55 pm
Oi Guilherme. O Thadeu esta correto, a funcao que voce passou nao altera, de forma alguma, a String:
class X { static void alteraString(String str){ str = “Outro valor”; } }se voce fizer no main da classe X:
String str = "Guilherme"
alteraString(str);
System.out.println(str);
Vai imprimir Guilherme. Faça voce mesmo o teste. Creio que voce nao tenha lido corretamente o Kabutz, la ele diz praticamente a mesma coisa que o Thadeu fala aqui.
Comment by Paulo Silveira — May 21, 2007 @ 2:23 pm
Ola guilherme, faca o teste que o paulo colocou
Comment by Thadeu — May 21, 2007 @ 3:40 pm
Cara, pelo amor de deus, não fica espalhando essas coisas por aí que vai ter gente que vai achar isso bonito e vai querer usar, hehehe
Comment by Galmeida — May 21, 2007 @ 7:27 pm
A discussão é muito interessante…
Contudo, para os novatos, que pretendem tirar a certificação SCJP, há dois bons caminhos:
- manter fixa na cabeça a idéia de que Strings são eternamente imutáveis;
- aprender a fundo os recursos de Reflection e, ainda assim, manter a idéia de que Strings são eternamente imutáveis (já que na prova de certificação, basta saber que Strings são imutáveis!).
Enfim, a redundância que usei no que disse acima é para que os que estão iniciando e buscam dicas para a certificação não esqueçam que Strings são imutáveis!
Abraços a todos.
Comment by Lennon Jesus — May 23, 2007 @ 3:01 pm
Isto não é nada, imagina se alguem resolve fazer algo assim:
public class Teste { public static void main(String[] args) throws Exception { String str = "Rodrigo Urubatan"; mudaString(str); System.out.println("Rodrigo Urubatan"); } private static void mudaString(String str) throws Exception { String ref = str.intern(); Field value = String.class.getDeclaredField("value"); // 1 value.setAccessible(true); // 2 char[] charsDaString = (char[]) value.get(ref); // 3 charsDaString[0] = 'r'; charsDaString[1] = 'O'; charsDaString[2] = 'D'; charsDaString[3] = 'R'; charsDaString[4] = 'I'; } }Ou seja, brincar exatamente com o pool interno de strings do Java, que foi mencionado pelo Thadeu. E é bem parecido com o artigo, mas é de deixar o cidadão maluquinho
Comment by Rodrigo Urubatan — May 23, 2007 @ 3:02 pm
HACKERS ! ehehhe
Comment by Antonio Kantek — May 25, 2007 @ 8:27 am
Hahaha… vou pregar uma peça em alguém usando isso =) hahaha
Comment by Dimas — May 26, 2007 @ 2:15 am
“…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.”
Ainda para os que nao conhecem, um ponto a considerar: setar atributos via reflection tem um overhead consideravel, especialmente por causa da checagem de validacao de acesso. .
Mas parar para pensar em como fazer uma String mutavel, isso e muito filosofia! Legal!
Comment by Kimie Nakahara — May 29, 2007 @ 1:56 am
[...] um exemplo, do blog do Caelum http://blog.caelum.com.br/2007/05/21/strings-imutaveis/ package [...]
Pingback by Strings em java… « É ou não é… — August 13, 2009 @ 5:55 pm