Imutabilidade de strings em Java


218

Considere o seguinte exemplo.

String str = new String();

str  = "Hello";
System.out.println(str);  //Prints Hello

str = "Help!";
System.out.println(str);  //Prints Help!

Agora, em Java, os objetos String são imutáveis. Então, como é strpossível atribuir ao objeto o valor "Ajuda!". Isso não está contradizendo a imutabilidade de strings em Java? Alguém pode me explicar o conceito exato de imutabilidade?

Editar:

Está bem. Agora estou conseguindo, mas apenas uma pergunta de acompanhamento. E o código a seguir:

String str = "Mississippi"; 
System.out.println(str); // prints Mississippi 

str = str.replace("i", "!"); 
System.out.println(str); // prints M!ss!ss!pp! 

Isso significa que dois objetos são criados novamente ("Mississippi" e "M! Ss! Ss! Pp!") E a referência straponta para um objeto diferente após o replace()método?


str é apenas uma referência não objeto em si
ahmednabil88

Respostas:


316

strnão é um objeto, é uma referência a um objeto. "Hello"e "Help!"são dois Stringobjetos distintos . Assim, str aponta para uma string. Você pode mudar para o que aponta , mas não para o que aponta .

Veja este código, por exemplo:

String s1 = "Hello";
String s2 = s1;
// s1 and s2 now point at the same string - "Hello"

Agora, não há nada 1 que poderíamos fazer para s1que afetaria o valor de s2. Eles se referem ao mesmo objeto - a string "Hello"- mas esse objeto é imutável e, portanto, não pode ser alterado.

Se fizermos algo assim:

s1 = "Help!";
System.out.println(s2); // still prints "Hello"

Aqui vemos a diferença entre alterar um objeto e alterar uma referência. s2ainda aponta para o mesmo objeto que definimos inicialmente s1. Definir s1como "Help!"apenas altera a referência , enquanto o Stringobjeto a que se referiu originalmente permanece inalterado.

Se as strings fossem mutáveis, poderíamos fazer algo assim:

String s1 = "Hello";
String s2 = s1;
s1.setCharAt(1, 'a'); // Fictional method that sets character at a given pos in string
System.out.println(s2); // Prints "Hallo"

Editar para responder à edição do OP:

Se você olhar o código fonte para String.replace (char, char) (também disponível em src.zip no diretório de instalação do JDK - uma dica profissional é procurar sempre que você se perguntar como algo realmente funciona), poderá ver o que faz é o seguinte:

  • Se houver uma ou mais ocorrências de oldCharna sequência atual, faça uma cópia da sequência atual em que todas as ocorrências de oldCharsão substituídas newChar.
  • Se oldCharnão estiver presente na cadeia atual, retorne a cadeia atual.

Então, sim, "Mississippi".replace('i', '!')cria um novo Stringobjeto. Novamente, o seguinte vale:

String s1 = "Mississippi";
String s2 = s1;
s1 = s1.replace('i', '!');
System.out.println(s1); // Prints "M!ss!ss!pp!"
System.out.println(s2); // Prints "Mississippi"
System.out.println(s1 == s2); // Prints "false" as s1 and s2 are two different objects

Por enquanto, sua tarefa de casa é ver o que o código acima faz se você mudar s1 = s1.replace('i', '!');para s1 = s1.replace('Q', '!');:)


1 Na verdade, é possível alterar as strings (e outros objetos imutáveis). Requer reflexão e é muito, muito perigoso e nunca deve ser usado, a menos que você esteja realmente interessado em destruir o programa.


15
+1 para o método fictício, demonstra a diferença entre objeto imutável e outros.
Zappi

1
Obrigado gustafc pelos exemplos corretos e explicação clara .... Mas você pode apenas responder a parte editada da pergunta, por favor? Isso tornará claro meu entendimento.
Light_handle 12/10/09

17
Eu nunca vi uma resposta como essa antes. Discutimos todos os detalhes.
Michael 'Maik' Ardan

+1 Aqui está uma idéia, objetos imutáveis ​​em java são como copiar por valor, você pode ter 2 referências a uma String, mas você deve considerá-las 2 Strings separadas, pois é imutável, e trabalhar com um deles não afetará o outro
Khaled.K 27/03

1
Java - quanto mais você pensa que sabe, menos você realmente sabe.
zeeshan

23

O objeto que faz strreferência pode mudar, mas os próprios Stringobjetos reais não podem.

Os Stringobjetos que contêm a string "Hello"e "Help!"não podem alterar seus valores, portanto, são imutáveis.

A imutabilidade dos Stringobjetos não significa que as referências que apontam para o objeto não podem mudar.

Uma maneira de impedir que a strreferência seja alterada é declará-la como final:

final String STR = "Hello";

Agora, tentar atribuir outro Stringa STRcausará um erro de compilação.


Mas, nesse caso, o objeto String é 'str', que primeiro contém o valor 'Hello' e, em seguida, recebe um novo valor 'Help!'. O que exatamente você quer dizer com "Os objetos String que contêm a string" Hello "e" Help! "Não podem alterar seus valores, portanto, são imutáveis." Perdoe-me se esta é uma pergunta boba. Mas iv tem que limpá-la ...
Light_handle

2
você já tentou todos os programas em C? Basta ler a cartilha sobre ponteiros e você entenderá perfeitamente a resposta do coobird.
Ryan Fernandes

Veja ... É isso que eu quero evitar ... Eu sei que você é um ótimo programador ... e estou apenas tentando aprender java aqui ... Então, se você puder responder minha pergunta corretamente, responda .. .
Light_handle

Você está confundindo referências e objetos - strnão é o "objeto", é uma referência ao objeto. Se você String str = "Hello";seguiu String anotherReference = str;você não tem 2 objetos String, você tem um objeto (o literal "Hello") e 2 referências a ele ( stre anotherReference).
Nate

Eu não tenho o direito de editar, mas, se o fizesse, editaria a primeira frase dos coobirds para: "As referências str podem mudar, mas os objetos String atuais não podem".
j3App 21/11

10

Light_handle Eu recomendo que você leia o Tamanho da xícara - uma história sobre variáveis e Valor por passagem por valor (tamanho da xícara continuação) . Isso ajudará bastante ao ler as postagens acima.

Você leu eles? Sim. Boa.

String str = new String();

Isso cria um novo "controle remoto" chamado " str" e define isso para o valor new String()(ou "").

por exemplo, na memória, isso cria:

str --- > ""

str  = "Hello";

Isso muda o controle remoto " str", mas não modifica a string original "".

por exemplo, na memória, isso cria:

str -+   ""
     +-> "Hello"

str = "Help!";

Isso altera o controle remoto " str", mas não modifica a seqüência original ""ou o objeto para o qual o controle remoto aponta atualmente.

por exemplo, na memória, isso cria:

str -+   ""
     |   "Hello"
     +-> "Help!"

"" E "Olá" coletam lixo?
Prabin Timsina

@PrabinTimsina Esta deve realmente ser uma nova pergunta. É respondido: stackoverflow.com/questions/15324143/…
Michael Lloyd Lee mlk

9

Vamos dividi-lo em algumas partes

String s1 = "hello";

Esta instrução cria uma string contendo hello e ocupa espaço na memória, ou seja, no Constant String Pool e a atribui ao objeto de referência s1

String s2 = s1;

Esta instrução atribui a mesma string hello à nova referência s2

         __________
        |          |
s1 ---->|  hello   |<----- s2
        |__________| 

Ambas as referências estão apontando para a mesma string, portanto, imprima o mesmo valor da seguinte maneira.

out.println(s1);    // o/p: hello
out.println(s2);    // o/p: hello

Embora String seja imutável , a atribuição pode ser possível, portanto o s1 agora se refere à nova pilha de valores .

s1 = "stack";    
         __________
        |          |
s1 ---->|  stack   |
        |__________|

Mas e quanto ao objeto s2 que está apontando para olá, ele será como é?

         __________
        |          |
s2 ---->|  hello   |
        |__________|

out.println(s1);    // o/p: stack
out.println(s2);    // o/p: hello

Como a String é imutável, a Java Virtual Machine não nos permitirá modificar a string s1 por seu método. Ele criará todo o novo objeto String no pool da seguinte maneira.

s1.concat(" overflow");

                 ___________________
                |                   |
s1.concat ----> |  stack overflow   |
                |___________________|

out.println(s1);    // o/p: stack
out.println(s2);    // o/p: hello
out.println(s1.concat); // o/p: stack overflow

Observe que se String seria mutável, a saída teria sido

out.println(s1);    // o/p: stack overflow

Agora você pode se surpreender por que o String possui métodos como o concat () para modificar. O fragmento a seguir limpará sua confusão.

s1 = s1.concat(" overflow");

Aqui estamos atribuindo o valor modificado da string de volta à referência s1 .

         ___________________
        |                   |
s1 ---->|  stack overflow   |
        |___________________|


out.println(s1);    // o/p: stack overflow
out.println(s2);    // o/p: hello

É por isso que Java decidiu que String é uma classe final. Caso contrário, qualquer pessoa pode modificar e alterar o valor da string. Espero que isso ajude um pouco.


6

O objeto de sequência que foi referenciado pela primeira vez strnão foi alterado, tudo o que você fez foi fazer strreferência a um novo objeto de sequência.


5

A String não muda, a referência a ela muda. Você está confundindo imutabilidade com o conceito de finalcampos. Se um campo for declarado como final, uma vez atribuído, não poderá ser reatribuído.


5

Em relação à parte de substituição da sua pergunta, tente o seguinte:

String str = "Mississippi"; 
System.out.println(str); //Prints Mississippi 

String other = str.replace("i", "!"); 
System.out.println(str); //still prints Mississippi 
System.out.println(other);  // prints M!ss!ss!pp!

4

Embora o java tente ignorá-lo, strnada mais é do que um ponteiro. Isso significa que, quando você escreve pela primeira vez str = "Hello";, cria um objeto que straponta para. Quando você reatribui strpor escrito str = "Help!";, um novo objeto é criado e o "Hello"objeto antigo é coletado como lixo sempre que o java quiser.


3

Imutabilidade implica que o valor de um objeto instanciado não pode ser alterado; você nunca pode transformar "Olá" em "Ajuda!".

A variável str é uma referência a um objeto. Quando você atribui um novo valor a str, você não está alterando o valor do objeto a que se refere, mas referenciando um objeto diferente.


3

A classe String é imutável e você não pode alterar o valor do objeto imutável. Porém, no caso de String, se você alterar o valor de string, ele criará uma nova string no pool de strings e a referência de string a esse valor, e não ao valor mais antigo. então, dessa maneira, a string é imutável. Vamos dar o seu exemplo,

String str = "Mississippi";  
System.out.println(str); // prints Mississippi 

ele criará uma string "Mississippi" e a adicionará ao pool de String. Agora, str está apontando para o Mississippi.

str = str.replace("i", "!");  
System.out.println(str); // prints M!ss!ss!pp! 

Mas após a operação acima, uma outra string será criada "M! Ss! Ss! Pp!" e será adicionado ao pool de String. e agora str está apontando para M! ss! ss! pp !, não para o Mississippi.

portanto, dessa maneira, quando você alterar o valor do objeto string, ele criará mais um objeto e o adicionará ao pool de strings.

Vamos ter mais um exemplo

String s1 = "Hello"; 
String s2 = "World"; 
String s = s1 + s2;

isso acima das três linhas adicionará três objetos de string ao pool de strings.
1) Olá
2) Mundo
3) Olá Mundo


2

Usar:

String s = new String("New String");
s.concat(" Added String");
System.out.println("String reference -----> "+s); // Output: String reference -----> New String

Se você vê aqui, eu uso o concatmétodo para alterar a string original, ou seja, "New String" com uma string "Added String", mas ainda assim obtive a saída como anterior, portanto isso prova que você não pode alterar a referência do objeto da classe String, mas se você fizer isso pela classe StringBuilder, ele funcionará. Está listado abaixo.

StringBuilder sb = new StringBuilder("New String");
sb.append(" Added String");
System.out.println("StringBuilder reference -----> "+sb);// Output: StringBuilder reference -----> New String Added String

2

Como Linus Tolvards disse:

Falar é fácil. Mostre-me o código

Dê uma olhada neste:

public class Test{
    public static void main(String[] args){

        String a = "Mississippi";
        String b = "Mississippi";//String immutable property (same chars sequence), then same object

        String c = a.replace('i','I').replace('I','i');//This method creates a new String, then new object
        String d = b.replace('i','I').replace('I','i');//At this moment we have 3 String objects, a/b, c and d

        String e = a.replace('i','i');//If the arguments are the same, the object is not affected, then returns same object

        System.out.println( "a==b? " + (a==b) ); // Prints true, they are pointing to the same String object

        System.out.println( "a: " + a );
        System.out.println( "b: " + b );

        System.out.println( "c==d? " + (c==d) ); // Prints false, a new object was created on each one

        System.out.println( "c: " + c ); // Even the sequence of chars are the same, the object is different
        System.out.println( "d: " + d );

        System.out.println( "a==e? " + (a==e) ); // Same object, immutable property
    }
}

A saída é

a==b? true
a: Mississippi
b: Mississippi
c==d? false
c: Mississippi
d: Mississippi
a==e? true

Então, lembre-se de duas coisas:

  • As strings são imutáveis ​​até você aplicar um método que manipula e cria um novo (casos de C & D).
  • O método Replace retorna o mesmo objeto String se os dois parâmetros forem os mesmos

0

Para aqueles que querem saber como quebrar a imutabilidade de String em Java ...

Código

import java.lang.reflect.Field;

public class StringImmutability {
    public static void main(String[] args) {
        String str1 = "I am immutable";
        String str2 = str1;

        try {
            Class str1Class = str1.getClass();
            Field str1Field = str1Class.getDeclaredField("value");

            str1Field.setAccessible(true);
            char[] valueChars = (char[]) str1Field.get(str1);

            valueChars[5] = ' ';
            valueChars[6] = ' ';

            System.out.println(str1 == str2);
            System.out.println(str1);
            System.out.println(str2);           
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (SecurityException e) {
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

    }
}

Resultado

true
I am   mutable
I am   mutable

0

String é imutável . O que significa que só podemos mudar a referência .


String a = "a";
System.out.println("String a is referencing to "+a); // Output: a

a.concat("b");
System.out.println("String a is referencing to "+a); // Output: a

a = a.concat("b");
System.out.println("String a has created a new reference and is now referencing to "+a); // Output: ab

0

Em Java, os objetos geralmente são acessados ​​por referências. No seu trecho de código, str é uma referência que é atribuída primeiro a "Hello" (um objeto criado automaticamente ou obtido a partir de um pool constante ) e, em seguida, você atribui outro objeto "Help!" para a mesma referência. Um ponto a ser observado é que a referência é a mesma e modificada, mas os objetos são diferentes. Mais uma coisa no seu código, você acessou três objetos,

  1. Quando você chamou new String ().
  2. Quando você atribuiu "olá".
  3. Quando você atribuiu "ajuda!".

Chamar new String () cria um novo objeto, mesmo que exista no pool de strings; portanto, geralmente não deve ser usado. Para colocar uma string criada a partir de new String () no pool de strings, você pode tentar o intern()método

Eu espero que isso ajude.


0

A imutabilidade que posso dizer é que você não pode alterar a própria String. Suponha que você tenha a String x, cujo valor é "abc". Agora você não pode alterar a String, ou seja, não pode alterar nenhum caractere / s em "abc".

Se você precisar alterar algum caractere na String, poderá usar uma matriz de caracteres e modificá-la ou usar o StringBuilder.

String x = "abc";
x = "pot";
x = x + "hj";
x = x.substring(3);
System.out.println(x);

char x1[] = x.toCharArray();
x1[0] = 's';
String y = new String(x1);
System.out.println(y);

Resultado:

hj
sj

0

Ou você pode tentar:

public class Tester
{
public static void main(String[] args)
{
 String str = "Mississippi"; 
 System.out.println(str); // prints Mississippi 
 System.out.println(str.hashCode());

 str = str.replace("i", "!"); 
 System.out.println(str); // prints M!ss!ss!pp! 
 System.out.println(str.hashCode());
 }
 }

Isso mostrará como o código de hash muda.


0

String é imutável significa que você não pode alterar o objeto em si, mas pode alterar a referência ao objeto. Quando você chamou a = "ty", na verdade você está alterando a referência de a para um novo objeto criado pela literal String "ty". Alterar um objeto significa usar seus métodos para alterar um de seus campos (ou os campos são públicos e não finais, para que possam ser atualizados de fora sem acessá-los por métodos), por exemplo:

Foo x = new Foo("the field");
x.setField("a new field");
System.out.println(x.getField()); // prints "a new field"

Enquanto estiver em uma classe imutável (declarada como final, para impedir a modificação por herança) (seus métodos não podem modificar seus campos e também os campos são sempre particulares e recomenda-se que sejam finais), por exemplo, String, você não pode alterar a String atual, mas você pode retornar uma nova String, ou seja:

String s = "some text";
s.substring(0,4);
System.out.println(s); // still printing "some text"
String a = s.substring(0,4);
System.out.println(a); // prints "some"

0

Aqui imutabilidade significa que a instância pode apontar para outra referência, mas o conteúdo original da string não seria modificado na referência original. Deixe-me explicar pelo primeiro exemplo dado por você. O primeiro str está apontando para "Hello", está tudo bem com isso. Segunda vez, está apontando para "Ajuda!". Aqui str começou a apontar para "Ajuda!" e a referência da string "Hello" é perdida e não podemos recuperá-la.

De fato, quando str tentaria modificar o conteúdo existente, outra nova string será gerada e str começará a apontar para essa referência. Portanto, vemos que a string na referência original não é modificada, mas é segura em sua referência e a instância do objeto começou a apontar para uma referência diferente, para que a imutabilidade seja preservada.


0

Tarde demais para a resposta, mas queria colocar uma mensagem concisa do autor da classe String em Java

Strings são constantes; seus valores não podem ser alterados após serem criados. Buffers de string suportam strings mutáveis. Como os objetos String são imutáveis, eles podem ser compartilhados.

Pode-se derivar desta documentação que qualquer coisa que mude a string retorne um objeto diferente (que pode ser novo ou interno e antigo). A dica não tão sutil sobre isso deve vir da assinatura da função. Pense nisso: 'Por que eles fizeram uma função em um objeto retornar um objeto em vez de status?'.

public String replace(char oldChar, char newChar) 

Também mais uma fonte que torna esse comportamento explícito (na documentação da função de substituição)

Retorna uma nova string resultante da substituição de todas as ocorrências de oldChar nessa string por newChar.

Fonte: https://docs.oracle.com/javase/7/docs/api/java/lang/String.html#replace(char,%20char)

  • autor Lee Boynton
  • autor Arthur van Hoff
  • autor Martin Buchholz
  • autor Ulf Zibis

Fonte: JavaDoc de String.


0

A sequência de objetos - métodos em si é feita para ser "imutável". Esta ação não produz alterações: "letters.replace (" bbb "," aaa ");"

Mas atribuir dados causa alterações no conteúdo das Strings:

    letters = "aaa";
    letters=null;
    System.out.println(letters);
    System.out.println(oB.hashCode());
    System.out.println(letters);
    letters = "bbbaaa";
    System.out.println(oB.hashCode());
    System.out.println(letters);

// O código de hash da string Object não muda.


0

Se HELLOé sua String, você não pode mudar HELLOpara HILLO. Essa propriedade é chamada propriedade de imutabilidade.

Você pode ter várias variáveis ​​String de ponteiro para apontar HELLO String.

Mas se HELLO for char Array, você poderá alterar HELLO para HILLO. Por exemplo,

char[] charArr = 'HELLO';
char[1] = 'I'; //you can do this

As linguagens de programação possuem variáveis ​​de dados imutáveis, para que possam ser usadas como chaves no par de chaves e valores.


0

Eu explicaria isso com um exemplo simples


considere qualquer matriz de caracteres: por exemplo, char a [] = {'h', 'e', ​​'l', 'l', 'o'}; e uma string: String s = "olá";


na matriz de caracteres, podemos executar operações como imprimir apenas as últimas três letras usando a iteração da matriz; mas na string precisamos criar um novo objeto String e copiar a substring necessária e seu endereço estará no novo objeto string.

por exemplo

***String s="hello";
String s2=s.substrig(0,3);***

então s2 terá "hel";


-1

String em Java em Immutable e Final significa apenas que não pode ser alterado ou modificado:

Caso 1:

class TestClass{  
 public static void main(String args[]){  
   String str = "ABC";  
   str.concat("DEF");  
   System.out.println(str);  
 }  
} 

Saída: ABC

Motivo: a referência do objeto str não é alterada, de fato, um novo objeto "DEF" é criado, que está no pool e não possui nenhuma referência (isto é, perdido).

Caso 2:

class TestClass{  
 public static void main(String args[]){  
   String str="ABC";  
   str=str.concat("DEF");  
   System.out.println(str);  
 }  
}  

Saída: ABCDEF

Razão: Nesse caso, str agora está se referindo a um novo objeto "ABCDEF", portanto, ele imprime ABCDEF, ou seja, o objeto str anterior "ABC" é perdido no pool sem referência.


-1

Como String é imutável, portanto, as alterações não ocorrerão se você não atribuir o valor retornado da função à string.Então, em sua pergunta, atribua o valor da função swap ao valor retornado a s.

s = swap (s, n1, n2); o valor da string s será alterado.

Eu também estava obtendo o valor inalterado quando estava escrevendo o programa para obter algumas seqüências de permutações (embora não esteja fornecendo todas as permutações, mas isso é, por exemplo, para responder à sua pergunta)

Aqui está um exemplo.

> import java.io.*;  public class MyString { public static void
> main(String []args)throws IOException {  BufferedReader br=new
> BufferedReader(new InputStreamReader(System.in));  String
> s=br.readLine().trim(); int n=0;int k=0;  while(n!=s.length()) {
> while(k<n){  swap(s,k,n); System.out.println(s); swap(s,k,n); k++; }
> n++; } }  public static void swap(String s,int n1,int n2) { char temp;
> temp=s.charAt(n1); StringBuilder sb=new StringBuilder(s);
> sb.setCharAt(n1,s.charAt(n2)); sb.setCharAt(n2,temp); s=sb.toString();
> } }

Mas eu não estava obtendo os valores permutados da string acima do code.So, eu atribui o valor retornado da função swap à string e obtive valores alterados da string. depois de atribuir o valor retornado, obtive os valores permutados da string.

/import java.util.*; import java.io.*; public class MyString { public static void main(String []args)throws IOException{
BufferedReader br=new BufferedReader(new InputStreamReader(System.in)); 
String s=br.readLine().trim(); int n=0;int k=0; 
while(n!=s.length()){ while(k<n){ s=swap(s,k,n); 
System.out.println(s); s=swap(s,k,n); k++; } n++; } } 
public static String swap(String s,int n1,int n2){
char temp; temp=s.charAt(n1); StringBuilder sb=new StringBuilder(s); sb.setCharAt(n1,s.charAt(n2)); sb.setCharAt(n2,temp); s=sb.toString(); return s; } }

-1
    public final class String_Test {

    String name;
    List<String> list=new ArrayList<String>();

    public static void main(String[] args) {

        String_Test obj=new String_Test();
        obj.list.add("item");//List will point to a memory unit- i.e will have one Hashcode value #1234

        List<String> list2=obj.list; //lis1 also will point to same #1234

        obj.list.add("new item");//Hashcode of list is not altered- List is mutable, so reference remains same, only value in that memory location changes

        String name2=obj.name="Myname"; // name2 and name will point to same instance of string -Hashcode #5678
        obj.name = "second name";// String is Immutable- New String HAI is created and name will point to this new instance- bcoz of this Hashcode changes here #0089

        System.out.println(obj.list.hashCode());
        System.out.println(list2.hashCode());
        System.out.println(list3.hashCode());

        System.out.println("===========");
        System.out.println(obj.name.hashCode());
        System.out.println(name2.hashCode());
    }
}

Produzirá colocar algo assim

1419358369 1419358369

103056 65078777

O objetivo do objeto Imutável é que seu valor não deva ser alterado depois de atribuído. Ele retornará um novo objeto sempre que você tentar alterá-lo com base na implementação. Nota: Stringbuffer em vez de string pode ser usado para evitar isso.

Para sua última pergunta: u terá uma referência e duas strings no pool de strings. Exceto que a referência apontará para m! Ss! Ss! Pp!

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.