Caelum | Ensino e Inovação - Cursos de Java, Scrum, Ruby on Rails


Strings (i)mutáveis?

Por Thadeu Russo em 21/05/07

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 [] argsthrows 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 [] argsthrows 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 [] argsthrows Exception {
    String nome = "Thadeu de Russo e Carmo";
    alteraString(nome);
    System.out.println(nome);
  }
  
  public static void alteraString(String nomethrows 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.

  • Share/Bookmark

10 Comments »

  1. “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

  2. 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

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

    Comment by Thadeu — May 21, 2007 @ 3:40 pm

  4. 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

  5. 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

  6. 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

    Comment by Rodrigo Urubatan — May 23, 2007 @ 3:02 pm

  7. HACKERS ! ehehhe

    Comment by Antonio Kantek — May 25, 2007 @ 8:27 am

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

    Comment by Dimas — May 26, 2007 @ 2:15 am

  9. “…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

  10. [...] 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

RSS feed for comments on this post. TrackBack URL

Leave a comment




Caelum | Ensino e Inovação
São Paulo: Rua Vergueiro, 3185, cj. 87, próximo ao Metrô Vila Mariana   |   Tel. (11) 5571-2751
Rio de Janeiro: Rua Senador Dantas, 80, cj. 307/308 - Centro   |   Tel. (21) 2220-4156 ou 2297-0033
Brasília: SCS Qd. 8 Bl. B-50, Sala 521 - Ed. Venâncio 2000   |   Tel. (61) 3039-4222