Strings (i)mutáveis?

Postado em 21. mai, 2007 por Thadeu Russo em Java

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.

Thadeu Russo

11 Respostas para “Strings (i)mutáveis?”

  1. Guilherme Santos

    21. mai, 2007

    “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

  2. Paulo Silveira

    21. mai, 2007

    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.

  3. Thadeu

    21. mai, 2007

    Ola guilherme, faca o teste que o paulo colocou :)

  4. Galmeida

    21. mai, 2007

    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

  5. Lennon Jesus

    23. mai, 2007

    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.

  6. Rodrigo Urubatan

    23. mai, 2007

    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 :D

  7. Antonio Kantek

    25. mai, 2007

    HACKERS ! ehehhe

  8. Dimas

    26. mai, 2007

    Hahaha… vou pregar uma peça em alguém usando isso =) hahaha

  9. Kimie Nakahara

    29. mai, 2007

    “…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!

Trackbacks/Pingbacks

  1. Strings em java… « É ou não é… - agosto 13, 2009

    [...] um exemplo, do blog do Caelum http://blog.caelum.com.br/2007/05/21/strings-imutaveis/ package [...]

  2. Pool e imutabilidade de Strings em Java « diga programação - setembro 3, 2012

    [...] http://blog.caelum.com.br/strings-imutaveis/ Share this:TwitterFacebookGostar disso:GosteiSeja o primeiro a gostar disso. « Previous [...]

Deixar uma Resposta