terça-feira, 14 de agosto de 2012

Instâncias de String na JVM

Alguns programadores, inclusive eu, acham besteira estudar instância de objetos no java. A prova SCJP trabalha com assuntos deste paradigma, porém retrata situações que ficamos confusos. Por esse motivo resolvi fazer um rápido tutorial estilo "pegadinha" onde eu mostra um questão típica do teste e você se descobre com dúvidas referentes a um conteúdo tão macificado (ou achamos que está macificado).

Veja o programa abaixo:

public class Test9 {
  static String s1 = "Guilherme";
  String s2 = "Guilherme";
  public static void main(String args[]) {
    Test9 a = new Test9();
    String s3 = "Guilherme";
    String s4 = new String(s1);
    String s5 = s1;
    String s6 = a.s2;
    // Aqui aparecem as dúvidas
    System.out.println( s1 == a.s2 );
    System.out.println( s1 == s3 );
    System.out.println( s1 == s4 );
    System.out.println( s1 == s5 );
    System.out.println( a.s2 == s3 );
    System.out.println( a.s2 == s4 );
    System.out.println( a.s2 == s5 );
    System.out.println( a.s2 == s6 );
    System.out.println( s4 == s5 );

    System.out.println( s1.equals(a.s2) );
    System.out.println( s1.equals(s6) );
  }

}


Qual será a saída do programa (true, false...)?
Antes de continuar o artigo, tente responder (mentalmente, se possível, pois na prova seria assim).

Agora que você já respondeu, vamos às explicações:

Primeiro detalhe: Você precisa entender (e acho que entende) que ao comparar Strings com ==, você está comparando os objetos instânciados, ou seja, a variável de instância s1 referencia o mesmo objeto que a variável de instância s2. Assim que se lê a comparação com ==.

Segundo detalhe: Ao se comparar Strings com o método equals(), o valor da String que é comparado (veja a API java). Isso você já sabe, tenho certeza! Atente-se que a comparação é sensitive case.

Terceiro detalhe: As variáveis estáticas são instâncias normais também, exceto pelo fato de que não estão associadas a determinada instância da classe a qual pertence. Ela pertence à classe. Por exemplo, a instância a de Test9 permitiu o acesso à variável de instância interna s2, mas a variável de instância estática s1 já existia, e seu valor pode ser acessado independentemente da instância a. Isso você também já conhece!

Quarto detalhe: Esse é legal! Se você criar Strings sem utilizar o comando new, e essas Strings possuírem o mesmo valor, por exemplo "Guilherme", o JVM só instancia uma String e todas as variáveis de instância referenciam esse objeto. Não entendi! Seguinte: Observe que s1 possui o valor "Guilherme" e que s2 também possui o valor "Guilherme". Pelo fato do valor "Guilherme" ser o mesmo para ambas variáveis, o JVM instância um único objeto e s1 e s2 apontam para ele. Isso também é válido para s3. Porque ocorre isso? Não está claro! Se fossem 2000 variáveis de instância s's apontando para o mesmo valor, por que a JVM gastaria uma cacetada de memória com isso! Já que é o mesmo valor, ela cria um único objeto e todas as variáveis de instância apontam para ele. O JVM sabe economizar memória (ainda mais nesses dias de crise financeira!).

Quinto detalhe: No caso onde s5 recebe s1, os objetos, mais do que explicitamente, são os mesmos. O mesmo é válido para s6 = a.s2. Isso significa que a variável de instância s6 está apontando para o mesmo objeto que a variável de instância a.s2.

Sexto detalhe: Observe que quando s4 recebeu um novo objeto com a sintaxe "new String(s1)", o termo new foi usado. Desta forma, o JVM é obrigado a instânciar outra String na memória, mesmo que os valores sejam iguais. Isso significa que s4 != s1.

Bom, tento ressaltado esses detalhes, vamos ao que interessa: qual será a saída do Test9?

true
true
false
true
true
false
true
true
false
true
true


Se sua saída foi igual à mostrada acima, parabéns! Você está dominando a instânciação dos objetos, inclusive as particularidades da String.
Ahh... minha saída ficou parecida! Cara, sinceramente, em programação ou "é" ou "não é"! O "parecida" não vale, pow! ... Bom, se a saída foi distinta, procure entender o detalhes que discutimos acima e veja a explicação completa abaixo.

Entendendo o Test9
Vamos lá! s1 recebe o valor "Guilherme". Quando s2 está recebendo o valor "Guilherme", o JVM pensa: perai! Esse objeto já existe! Eu não vou usar mais espaço para guardar esse mesmo objeto só porque s2 tá querendo! s2, toma esse objeto mesmo, ele é igualzinho, ninguém vai reparar! Então s2 recebe o mesmo objeto de s1. Quando s3 é definida com "Guilherme", o JVM, mais uma vez, reflete: Caramba! Esse pessoal tá querendo me sacanear. Vê só! Outro objeto com esse nome!? Quem é esse tal de Guilherme afinal? Não estou nem ai! s3 vai receber o mesmo objeto de s1 - ele não vai nem perceber! Mais uma vez, a variável de instância s3 aponta para o mesmo objeto de s1. Agora veja o que ocorre na JVM quando s4 recebe uma nova String: $%#@$! Esse tal de s4 é safado mesmo! Ele sabe que eu já tenho um objeto "Guilherme" na memória e pediu um novo (new). Não vai ter jeito, esse ai eu vou ter que criar um novo objeto. s4 toma ai seu novo objeto "Guilherme" - que pena ele é igualzinho ao outro! Então, s4 aponta para um outro objeto String. No caso de s5 e s6, eles estão recebendo a mesma instância de s1 e a.s2, então, obviamente, também possuem o mesmo objeto.

Vamos às saídas
Em "s1 == a.s2" o resultado é true poque ambos apontam para Guilherme.
Em "s1 == s3", o resultado também é true, pelo mesmo motivo.
Em "s1 == s4", como s4 soliciou uma nova instância com o comando new, a saída é false.
Em "s1 == s5", obviamente, é true.
Em "a.s2 == s3", a saída é true, pois ambos compartilham o mesmo objeto.
Em "a.s2 == s4", o resultado é false.
Em "a.s2 == s5", o resultado é true.
Em "a.s2 == s6", o resultado é true.
Em "s4 == s5", a saída é false, pois s4 pediu um novo objeto.
Em "s1.equals(a.s2)", a saída só poderia ser true, pois s1 == s2, então obrigatoriamente seus valores também são iguais.
Em "s1.equals(s6)", a saída também é true.
Entendeu?!
Bixo! Se tu não entendeu até agora, aconselho ir tirar uma certificação de outra coisa! De tricô, talvez!
Brincadeirinha! Se você ainda não entendeu! Dê uma olhadinha nas referências deste artigo. Existem muito material legal sobre essas características na Internet também. Procure no Google e seja feliz! Qualquer coisa me manda um e-mail.

Referências
http://java.sun.com/j2se/1.3/docs/api/java/lang/Object.html

Espero ter ajudado!
Guilherme Pontes

Nenhum comentário:

Postar um comentário