Solucionado (ver solução)
Solucionado
(ver solução)
6
respostas

String referência e valor

Boa noite professor, tem como discorrer mais sobre esta questão de referência e valores? Tem algum exemplo prático onde consigo identificar que uma variável String ou outra classe é somente uma referencia para um determinado objeto alocado em memória? Tipo tem como eu testar de forma prática que é uma referencia e não um valor?

6 respostas

Especificamente a classe String é final e não pode ser alterada, então toda vez que você mexe em uma String na verdade você está jogando o ponteiro para o valor antigo fora, então é difícil de testar.

Para entender um pouco melhor a história de ponteiros você pode fazer um teste com vetores (explicação lá embaixo):

String[] vetorA = {"a1", "a2", "a3"}; //cria um vetor
String[] vetorB = {"b1", "b2", "b3"}; //cria um vetor
String[] vetorC = vetorA; //cria um novo ponteiro do vetorA

System.out.println(vetorA); //a1 a2 a3
System.out.println(vetorB); //b1 b2 b3
System.out.println(vetorC); //a1 a2 a3

vetorC[0] = "c1"; //atribui o valor "c1" na primeira posição do vetorC (que aponta pro vetorA)
vetorB[1] = vetorA[0]; //atribui o valor da primeira posição do vetorA para a segunda do vetorB
vetorC = vetorB; //altera o ponteiro vetorC para apontar para o vetorB
vetorC[2] = "f"; //altera a ultima posicao do vetorC para "f"

System.out.println(vetorA); //c1 a2 a3
System.out.println(vetorB); //b1 c1 f
System.out.println(vetorC); //b1 c1 f

Um ponteiro é tipo uma seta que te direciona para outro lugar. Quando você altera o ponteiro, você mudou a seta de direção. Quando você altera um atributo do ponteiro, você vai até o lugar onde a seta apontava e então altera o valor daquele lugar.

Deu pra entender um pouco melhor?

Aqui ó, executei no jshell pra você poder ver o resultado:

jshell> String[] vetorA = {"a1", "a2", "a3"}; //cria um vetor
vetorA ==> String[3] { "a1", "a2", "a3" }

jshell> String[] vetorB = {"b1", "b2", "b3"}; //cria um vetor
vetorB ==> String[3] { "b1", "b2", "b3" }

jshell> String[] vetorC = vetorA; //cria um novo ponteiro do vetorA
vetorC ==> String[3] { "a1", "a2", "a3" }

jshell> vetorA
vetorA ==> String[3] { "a1", "a2", "a3" }

jshell> vetorB
vetorB ==> String[3] { "b1", "b2", "b3" }

jshell> vetorC
vetorC ==> String[3] { "a1", "a2", "a3" }

jshell> vetorC[0] = "c1"; //atribui o valor "c1" na primeira posição do vetorC (que aponta pro vetorA)
$7 ==> "c1"

jshell> vetorB[1] = vetorA[0]; //atribui o valor da primeira posição do vetorA para a segunda do vetorB
$8 ==> "c1"

jshell> vetorC = vetorB; //altera o ponteiro vetorC para apontar para o vetorB
vetorC ==> String[3] { "b1", "c1", "b3" }

jshell> vetorC[2] = "f"; //altera a ultima posicao do vetorC para "f"
$9 ==> "f"

jshell> vetorA
vetorA ==> String[3] { "c1", "a2", "a3" }

jshell> vetorB
vetorB ==> String[3] { "b1", "c1", "f" }

jshell> vetorC
vetorC ==> String[3] { "b1", "c1", "f" }

jshell>

Boa!!

Olá Marcos, obrigado pelo retorno, a minha dúvida se da na seguinte questão: "Strings são imutáveis". Verifiquei alguns exemplos na internet que mostram este fato conforme código abaixo:

public class TesteString {
    public static void main(String[] args) {
          String nome = "Thadeu de Russo e Carmo";
          nome.toUpperCase();
          System.out.println(nome);
    }
}

De fato não houve alteração no nome impresso na tela. O que eu quero entender é o motivo de não ocorrer a alteração.

Coloque o seu algoritmo no compilador para checar o resultado, abaixo e retorno do compilador:

[Ljava.lang.String;@7852e922
[Ljava.lang.String;@4e25154f
[Ljava.lang.String;@7852e922
[Ljava.lang.String;@7852e922
[Ljava.lang.String;@4e25154f
[Ljava.lang.String;@4e25154f

Acredito que seja a referência do objeto, e não os valores. Confesso que não consegui entender muito bem. É que ainda estou começando e queria de fato compreender com profundidade este tema. Agradeço muito a ajuda nesta compreensão.

solução!

Vou fazer um exemplo completo com impressão dos vetores pra vc. Mas voltando à pergunta: String são imutáveis. Como pode ser isso?

Você não consegue ALTERAR o valor de uma String, como faz com outro tipo de objeto. Isso foi uma decisão arquitetural dos engenheiros da linguagem, não tem um único motivo mas diversos, e é mais fácil você fazer uma busca sobre o assunto pra entender melhor essa motivação.

Bom, explicando o seu código de exemplo. A String nome não imprimiu o esperado exatamente por causa dessa propriedade imutável.

public class TesteString {
    public static void main(String[] args) {
          String nome = "Thadeu de Russo e Carmo";
          nome.toUpperCase();
          System.out.println(nome);
    }
}

Quando você quer alterar o valor de uma String você deve atribuir um novo valor a ela, um novo objeto ao qual apontar. Por isso se você olhar na documentação da oracle vai reparar que todos os métodos que "alteram" o valor do objeto na verdade retornam outro objeto String. Então você tem que atribuir esse retorno a uma nova variável ou à original. Exemplos:

public class TesteString {
  public static void main(String[] args) {
    String nome = "Thadeu de Russo e Carmo";
    String nomeAlterado = nome.toUpperCase();
    System.out.println(nomeAlterado);
    nomeAlterado = nome.toLowerCase();
    System.out.println(nomeAlterado);
    nome = "Sr. " + nome.concat(", o estudante de Java");
    System.out.println(nome);
  }
}

Se você não atribuir o retorno ou utilizar imediatamente, ele se perde e sua operação foi em vão, como no exemplo que você mostrou. Isso é ótimo para alterações pontuais, e um ótimo exemplo é o nome.

Você pode por exemplo adicionar um pronome de tratamento só no momento de impressão na tela, sem alterar o valor da variável. Pode transformar tudo em maiúsculas pra fazer comparação sem correr o risco de perder o valor original.

Deu pra entender?

Bom, como prometido aqui vai um programa funcional pra você ver os exemplos aí na sua máquina:

class TesteString {
  public static void main(String[] args) {
    System.out.println("***** TESTANDO VETORES *****");

    String[] vetorA = {"a1", "a2", "a3"}; //cria um vetor
    String[] vetorB = {"b1", "b2", "b3"}; //cria um vetor
    String[] vetorC = vetorA; //cria um novo ponteiro do vetorA

    imprimeVetor(vetorA); //a1 a2 a3
    imprimeVetor(vetorB); //b1 b2 b3
    imprimeVetor(vetorC); //a1 a2 a3

    vetorC[0] = "c1"; //atribui o valor "c1" na primeira posição do vetorC (que aponta pro vetorA)
    vetorB[1] = vetorA[0]; //atribui o valor da primeira posição do vetorA para a segunda do vetorB
    vetorC = vetorB; //altera o ponteiro vetorC para apontar para o vetorB
    vetorC[2] = "f"; //altera a ultima posicao do vetorC para "f"

    imprimeVetor(vetorA); //c1 a2 a3
    imprimeVetor(vetorB); //b1 c1 f
    imprimeVetor(vetorC); //b1 c1 f
    System.out.println("******* FIM  VETORES *******");


    System.out.println("*** TESTANDO ATRIBUICOES ***");

    String nome = "Marco Salles"; //declarando nome
    // usando o método .concat(), que concatena uma string na outra em sequencia
    // armazenando de volta na variável nome para "alterar" seu valor
    nome = "Sr. ".concat(nome); //prefixando "Sr. "
    System.out.println(nome); //Sr. Marco Salles
    // usando o operador +, que faz o mesmo que o método concat()
    System.out.println(nome + ", instrutor da Alura"); //Sr. Marco Salles, instrutor da Alura
    // não teve alteração do valor pois não teve atribuição
    System.out.println(nome); //Sr. Marco Salles

    String nomeInstrutor = nome + ", instrutor da Alura"; //exemplificando o operador + com atribuição
    System.out.println(nomeInstrutor); //Sr. Marco Salles, instrutor da Alura

    System.out.println(nome += ", Supremo Senhor do Universo"); //Sr. Marco Salles, Supremo Senhor do Universo
    // operador +=
    // o compilador transforma isso imediatamente em (nome = nome + "valor x"), do mesmo como que com números
    // olha o resultado: teve atribuição!
    System.out.println(nome); //Sr. Marco Salles, Supremo Senhor do Universo

    System.out.println("***** FIM  ATRIBUICOES *****");
  }

  // metodo pra imprimir os valores do vetor
  public static void imprimeVetor(String[] vetor) {
    for (String elemento : vetor) {
      System.out.printf(" " + elemento);
    }
    System.out.println();
  }
}