Qual é a diferença entre “texto” e nova String (“texto”)?

Qual é a diferença entre essas duas declarações a seguir?

String s = "text"; String s = new String("text"); 

new String("text"); cria explicitamente uma instância nova e referencialmente distinta de um object String ; String s = "text"; pode reutilizar uma instância do conjunto constante de cadeias, se houver uma disponível.

Muito raramente você iria querer usar o new String(anotherString) construtor new String(anotherString) . Da API:

String(String original) : Inicializa um object String recém-criado para que ele represente a mesma seqüência de caracteres que o argumento; Em outras palavras, a string recém-criada é uma cópia da string de argumento. A menos que seja necessária uma cópia explícita do original, o uso deste construtor é desnecessário, pois as strings são imutáveis.

Perguntas relacionadas

  • Java Strings: “String s = new String (” bobo “);”
  • Strings são objects em Java, então por que não usamos ‘new’ para criá-los?

O que significa distinção referencial

Examine o seguinte trecho:

  String s1 = "foobar"; String s2 = "foobar"; System.out.println(s1 == s2); // true s2 = new String("foobar"); System.out.println(s1 == s2); // false System.out.println(s1.equals(s2)); // true 

== em dois tipos de referência é uma comparação de identidade de referência. Dois objects que são equals não são necessariamente == . Geralmente é errado usar == em tipos de referência; Na maioria das vezes, os equals precisam ser usados ​​no lugar.

No entanto, se por alguma razão você precisar criar dois equals mas não == string, você pode usar o new String(anotherString) construtor new String(anotherString) . No entanto, é preciso dizer novamente que isso é muito peculiar e raramente é a intenção.

Referências

  • JLS 15.21.3 Operadores de Igualdade de Referência == e! =
  • class Objectclass Object boolean Object(equals)

Assuntos relacionados

  • Java String.equals versus ==
  • Como faço para comparar strings em Java?

Os literais de string irão para o String Constant Pool .

O instantâneo abaixo pode ajudá-lo a entender visualmente para lembrá-lo por mais tempo.

insira a descrição da imagem aqui


Criação de objects linha por linha:

 String str1 = new String("java5"); 

Usando string literal “java5” no construtor, um novo valor de string é armazenado no conjunto de constantes de string. Usando o novo operador, um novo object de cadeia é criado no heap com “java5” como valor.

 String str2 = "java5" 

A referência “str2” é apontada para o valor já armazenado no conjunto constante de cadeias

 String str3 = new String(str2); 

Um novo object string é criado no heap com o mesmo valor da referência por “str2”

 String str4 = "java5"; 

A referência “str4” é apontada para o valor já armazenado no conjunto constante de cadeias

Total de Objetos: Heap – 2, Pool – 1

Outras leituras na comunidade Oracle

Um cria uma String no Conjunto Constante de Cadeias

 String s = "text"; 

o outro cria uma string no pool constante ( "text" ) e outra string no ( s ) espaço ( s ) normal ( s ). Ambas as cadeias terão o mesmo valor, o de “texto”.

 String s = new String("text"); 

s é então perdido (elegível para GC) se depois não for utilizado.

Os literais de string, por outro lado, são reutilizados. Se você usar "text" em vários lugares da sua class, será de fato uma e somente uma String (ou seja, várias referências à mesma string no pool).

JLS

O conceito é chamado de “interning” pelo JLS.

Passagem relevante do JLS 7 3.10.5 :

Além disso, uma string literal sempre se refere à mesma instância da class String. Isso ocorre porque literais de string – ou, mais geralmente, strings que são os valores de expressões constantes (§15.28) – são “internados” para compartilhar instâncias exclusivas, usando o método String.intern.

Exemplo 3.10.5-1. Literais de cordas

O programa que consiste na unidade de compilation (§7.3):

 package testPackage; class Test { public static void main(String[] args) { String hello = "Hello", lo = "lo"; System.out.print((hello == "Hello") + " "); System.out.print((Other.hello == hello) + " "); System.out.print((other.Other.hello == hello) + " "); System.out.print((hello == ("Hel"+"lo")) + " "); System.out.print((hello == ("Hel"+lo)) + " "); System.out.println(hello == ("Hel"+lo).intern()); } } class Other { static String hello = "Hello"; } 

e a unidade de compilation:

 package other; public class Other { public static String hello = "Hello"; } 

produz a saída:

 true true true true false true 

JVMS

JVMS 7 5.1 diz :

Uma string literal é uma referência a uma instância da class String e é derivada de uma estrutura CONSTANT_String_info (§4.4.3) na representação binária de uma class ou interface. A estrutura CONSTANT_String_info fornece a seqüência de pontos de código Unicode que constituem o literal de seqüência de caracteres.

A linguagem de programação Java requer que literais de string idênticos (isto é, literais que contenham a mesma sequência de pontos de código) devem referir-se à mesma instância da class String (JLS §3.10.5). Além disso, se o método String.intern for chamado em qualquer sequência, o resultado será uma referência à mesma instância de class que seria retornada se essa sequência aparecesse como um literal. Assim, a seguinte expressão deve ter o valor true:

 ("a" + "b" + "c").intern() == "abc" 

Para derivar uma string literal, a Java Virtual Machine examina a seqüência de pontos de código dada pela estrutura CONSTANT_String_info.

  • Se o método String.intern tiver sido chamado anteriormente em uma instância da class String contendo uma seqüência de pontos de código Unicode idêntica à fornecida pela estrutura CONSTANT_String_info, o resultado da derivação literal de string será uma referência à mesma instância da class String.

  • Caso contrário, uma nova instância da class String é criada contendo a seqüência de pontos de código Unicode fornecida pela estrutura CONSTANT_String_info; uma referência a essa instância de class é o resultado da derivação literal de string. Finalmente, o método interno da nova instância String é chamado.

Bytecode

Também é instrutivo observar a implementação do bytecode no OpenJDK 7.

Se descompilarmos:

 public class StringPool { public static void main(String[] args) { String a = "abc"; String b = "abc"; String c = new String("abc"); System.out.println(a); System.out.println(b); System.out.println(a == c); } } 

nós temos na piscina constante:

 #2 = String #32 // abc [...] #32 = Utf8 abc 

e main :

  0: ldc #2 // String abc 2: astore_1 3: ldc #2 // String abc 5: astore_2 6: new #3 // class java/lang/String 9: dup 10: ldc #2 // String abc 12: invokespecial #4 // Method java/lang/String."":(Ljava/lang/String;)V 15: astore_3 16: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream; 19: aload_1 20: invokevirtual #6 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 23: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream; 26: aload_2 27: invokevirtual #6 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 30: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream; 33: aload_1 34: aload_3 35: if_acmpne 42 38: iconst_1 39: goto 43 42: iconst_0 43: invokevirtual #7 // Method java/io/PrintStream.println:(Z)V 

Observe como:

  • 0 e 3 : a mesma constante ldc #2 é carregada (os literais)
  • 12 : uma nova instância de string é criada (com #2 como argumento)
  • 35 : if_acmpne são comparados como objects regulares com if_acmpne

A representação de strings constantes é bastante mágica no bytecode:

  • tem uma estrutura CONSTANT_String_info dedicada, diferente de objects regulares (por exemplo, new String )
  • o struct aponta para uma estrutura CONSTANT_Utf8_info que contém os dados. Esses são os únicos dados necessários para representar a string.

e a citação do JVMS acima parece dizer que sempre que o Utf8 apontou é o mesmo, então instâncias idênticas são carregadas pelo ldc .

Eu fiz testes semelhantes para campos e:

  • static final String s = "abc" aponta para a tabela constante através do atributo ConstantValue
  • campos não finais não têm esse atributo, mas ainda podem ser inicializados com ldc

Conclusão : há suporte direto ao bytecode para o pool de strings, e a representação da memory é eficiente.

Bônus: compare isso com o pool Integer , que não tem suporte direto para bytecode (ou seja, sem o CONSTANT_String_info analógico).

Pense em "bla" sendo uma fábrica mágica como Strings.createString("bla") (pseudo). A fábrica mantém um pool de todas as strings criadas dessa maneira.

Se ele for chamado, ele verificará se já existe uma cadeia no conjunto com esse valor. Se true, retorna este object de string, portanto, as strings obtidas dessa maneira são, na verdade, o mesmo object.

Caso contrário, ele cria um novo object de string internamente, salva-o no pool e o retorna. Assim, quando o mesmo valor de sequência é consultado na próxima vez, ele retorna a mesma instância.

Criar manualmente new String("") substitui esse comportamento ignorando o pool literal de cadeias de caracteres. Portanto, igualdade deve sempre ser verificada usando equals() que compara a seqüência de caracteres em vez da igualdade de referência do object.

Uma maneira simples de entender a diferença está abaixo:

 String s ="abc"; String s1= "abc"; String s2=new String("abc"); if(s==s1){ System.out.println("s==s1 is true"); }else{ System.out.println("s==s1 is false"); } if(s==s2){ System.out.println("s==s2 is true"); }else{ System.out.println("s==s2 is false"); } 

saída é

 s==s1 is true s==s2 is false 

Assim, o novo String () sempre criará uma nova instância.

@Braj: Eu acho que você mencionou o contrário. Por favor corrija-me se eu estiver errado

Criação de objects linha por linha:

String str1 = new String (“java5”)

  Pool- "java5" (1 Object) Heap - str1 => "java5" (1 Object) 

String str2 = “java5”

  pool- str2 => "java5" (1 Object) heap - str1 => "java5" (1 Object) 

Cadeia str3 = new Cadeia (str2)

  pool- str2 => "java5" (1 Object) heap- str1 => "java5", str3 => "java5" (2 Objects) 

String str4 = “java5”

  pool - str2 => str4 => "java5" (1 Object) heap - str1 => "java5", str3 => "java5" (2 Objects) 

Embora pareça o mesmo do ponto de vista de um programador, ele tem um grande impacto no desempenho. Você gostaria de usar a primeira forma quase sempre.

 String str = new String("hello") 

Ele irá verificar se o conjunto constante String já contém String “hello”? Se presente, ele não adicionará uma input no conjunto constante de String. Se não estiver presente, ele adicionará uma input no conjunto constante de cadeias.

Um object será criado em uma área de memory de heap e pontos de referência str para o object criado no local da memory de heap.

se você quiser que a referência str contenha o object que contém o conjunto constante String, então é preciso chamar explicitamente str.intern();

 String str = "world"; 

Ele irá verificar se o conjunto constante String já contém String “hello”? Se presente, ele não adicionará uma input no conjunto constante de String. Se não estiver presente, ele adicionará uma input no conjunto constante de cadeias.

Nos dois casos acima, a referência str aponta para String "world" presente no pool Constante.