Qual é a diferença entre "text" e new String ("text")?


195

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

String s = "text";

String s = new String("text");


Qualquer pessoa, por favor responda a isso. String a = "Java"; String b = "Java"; System.out.println (a == b); true // mas System.out.println ("a == b?" + a == b); // false ...
Energia

eu não entendo quando adicionei algum comentário ("a == b?) => meu resultado se torna FALSO. por quê?
Energia

@Energy O resultado é falseque a ordem das operações determina que o operador + seja o primeiro, concatenando "a == b?" com a para criar uma String "a == b? Java". Em seguida, a expressão é "a==b?Java" == bavaliada como falsa.
Allison B

@AllisonB entendi, muito obrigado!
Energia

Respostas:


187

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

Você raramente gostaria de usar o new String(anotherString)construtor. Na API:

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

Perguntas relacionadas


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 objetos que equalsnão são necessariamente ==. Geralmente, é errado usar ==em tipos de referência; a maior parte do tempo equalsprecisa ser usada.

No entanto, se por qualquer motivo você precisar criar duas, equalsmas não uma ==sequência, poderá usar o new String(anotherString)construtor. É preciso dizer novamente, porém, que isso é muito peculiar e raramente é a intenção.

Referências

Assuntos relacionados


3
Se eu escrever: String s = new String ("abc"); E agora eu escrevo: String s = "abc"; Will String s = "abc"; criar um novo literal String no pool String?
Kaveesh Kanwal

Por que ninguém responde à pergunta anterior?
zeds

2
@KaveeshKanwal Não, o literal não será duplicado. Como você pode ver, existem 2 "abc"s. Apenas um deles irá para o pool String e o outro fará referência a ele. Depois, há o sque será o novo objeto apropriado.
Kayaman

1
@Kaveesh Kanwal - String s = new String ("abc") criará apenas um novo objeto String com o valor "abc". E a segunda instrução verificará se existe algum literal de string "abc" já presente no Pool de String ou não. Se já estiver presente, a referência ao existente será retornada e, se não estiver, um novo literal ("abc") será criado no pool de String. Espero que resolva sua consulta !!
User968813

Não há 'maio' sobre isso. O compilador deve agrupar literais de seqüência de caracteres. JLS 3.10.5 .
Marquês de Lorne

119

Literais de seqüência de caracteres irão para o Pool de Constantes de String .

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

insira a descrição da imagem aqui


Criação de objeto linha por linha:

String str1 = new String("java5");

Usando a string literal "java5" no construtor, um novo valor da string é armazenado no pool constante da string. Usando o novo operador, um novo objeto de sequência é criado no heap com "java5" como valor.

String str2 = "java5"

A referência "str2" está apontada para o valor já armazenado no pool constante de strings

String str3 = new String(str2);

Um novo objeto de string é criado no heap com o mesmo valor da referência por "str2"

String str4 = "java5";

A referência "str4" está apontada para o valor já armazenado no pool constante de strings

Total de objetos: Pilha - 2, Piscina - 1

Leitura adicional na comunidade Oracle


1
Boa resposta .. mas quero saber que agora estou pensando em mudar o valor de str1 = "java6", então ele mudará o valor de str4?
CoronaPintu

2
Sim, eu tive verificar que não irá alterar o valor de str4
CoronaPintu

@Braj Você pode fornecer documentação da afirmação do seu respondente?
Basil Bourque

@Braj: Os cabeçalhos do 'Heap' e 'pool' na tabela devem ser reversos?
Rahul Kurup

Incorreto. O pool constante é criado no tempo de compilação, não no tempo de execução. Não use formatação de citação para texto que não está entre aspas.
Marquês de Lorne

15

Um cria uma string no pool constante de strings

String s = "text";

o outro cria uma sequência no pool constante ( "text") e outra no espaço de pilha normal ( s). Ambas as strings terão o mesmo valor, o de "text".

String s = new String("text");

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

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


Cordas na piscina constante nunca são perdidas. Você quis dizer que 's' é perdido se mais tarde não for usado?
Marquês de Lorne

@ EPJ: sim, eu quis dizer "s". Obrigado por perceber. Eu vou corrigir a pergunta.

9

JLS

O conceito é chamado de "interning" pelo JLS.

Passagem relevante da JLS 7 3.10.5 :

Além disso, um literal de string sempre se refere à mesma instância da classe String. Isso ocorre porque literais de strings - ou, de maneira mais geral, 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 compilação (§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 compilação:

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

produz a saída:

true true true true false true

JVMS

O JVMS 7 5.1 diz :

Um literal de string é uma referência a uma instância da classe String e é derivado de uma estrutura CONSTANT_String_info (§4.4.3) na representação binária de uma classe ou interface. A estrutura CONSTANT_String_info fornece a sequência de pontos de código Unicode que constituem a cadeia literal.

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

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

Para derivar um literal de cadeia, a Java Virtual Machine examina a sequência de pontos de código fornecidos pela estrutura CONSTANT_String_info.

  • Se o método String.intern já foi chamado em uma instância da classe String que contém uma sequência de pontos de código Unicode idênticos àqueles fornecidos pela estrutura CONSTANT_String_info, o resultado da derivação literal da string é uma referência à mesma instância da classe String.

  • Caso contrário, uma nova instância da classe String será criada contendo a sequência de pontos de código Unicode fornecidos pela estrutura CONSTANT_String_info; uma referência a essa instância de classe é o resultado da derivação literal de cadeia de caracteres. Por fim, o método interno da nova instância String é chamado.

Bytecode

Também é instrutivo examinar a implementação de bytecode no OpenJDK 7.

Se decompilarmos:

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);
    }
}

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."<init>":(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:

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

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

  • possui uma estrutura CONSTANT_String_info dedicada , diferente de objetos 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 sequência.

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

Eu fiz testes semelhantes para campos e:

  • static final String s = "abc"aponta para a tabela constante por meio do atributo ConstantValue
  • campos não finais não possuem esse atributo, mas ainda podem ser inicializados com ldc

Conclusão : existe suporte direto de bytecode para o conjunto de strings e a representação de memória é eficiente.

Bônus: compare isso ao pool Inteiro , que não possui suporte direto ao bytecode (ou seja, sem CONSTANT_String_infoanalógico).


2

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

Criação de objeto 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)

String str3 = new String (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)

str1não está envolvido no valor de str2ou str3ou str4de qualquer forma.
Marquês de Lorne

1

Pense em "bla"ser uma fábrica mágica como Strings.createString("bla")(pseudo). A fábrica possui um conjunto de todas as strings criadas dessa maneira.

Se for invocado, ele verifica se já existe uma sequência no pool com esse valor. Se verdadeiro, ele retorna esse objeto de string, portanto, as strings obtidas dessa maneira são realmente o mesmo objeto.

Caso contrário, ele cria um novo objeto de cadeia internamente, o salva no pool e o retorna. Portanto, quando o mesmo valor da string é consultado na próxima vez, ele retorna a mesma instância.

A criação manual new String("")substitui esse comportamento ignorando o pool literal de cadeias. Portanto, a igualdade deve sempre ser verificada usando o equals()que compara a sequência de caracteres em vez da igualdade de referência do objeto.


A 'fábrica mágica' a que você se refere é nada mais ou menos que o compilador Java. É um erro escrever esse processo como se tivesse ocorrido em tempo de execução.
Marquês de Lorne

1

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, novo String () sempre criará uma nova instância.


1

Qualquer literal de String é criado dentro do pool literal de strings e o pool não permite duplicatas. Portanto, se dois ou mais objetos de string forem inicializados com o mesmo valor literal, todos os objetos apontarão para o mesmo literal.

String obj1 = "abc";
String obj2 = "abc";

"obj1" e "obj2" apontarão para a mesma string literal e o pool literal de strings terá apenas um literal "abc".

Quando criamos um objeto de classe String usando a nova palavra-chave, a string criada é armazenada na memória heap. Qualquer literal de cadeia passada como parâmetro para o construtor da classe String, no entanto, é armazenada no conjunto de cadeias. Se criarmos vários objetos usando o mesmo valor com o novo operador, um novo objeto será criado na pilha a cada vez, por causa desse novo operador deve ser evitado.

String obj1 = new String("abc");
String obj2 = new String("abc");

"obj1" e "obj2" apontarão para dois objetos diferentes no heap e o pool literal de cadeias terá apenas um literal "abc".

Também algo que vale a pena observar com relação ao comportamento das strings é que qualquer nova atribuição ou concatenação feita na string cria um novo objeto na memória.

String str1 = "abc";
String str2 = "abc" + "def";
str1 = "xyz";
str2 = str1 + "ghi";

Agora, no caso acima:
Linha 1: literal "abc" é armazenado no pool de cadeias.
Linha 2: o literal "abcdef" é armazenado no pool de strings.
Linha 3: Um novo literal "xyz" é armazenado no pool de strings e "str1" começa a apontar para esse literal.
Linha 4: Como o valor é gerado anexando a outra variável, o resultado é armazenado na memória heap e o literal que está sendo anexado "ghi" será verificado quanto à sua existência no pool de strings e será criado, uma vez que não existe no o caso acima.


0

Embora pareça o mesmo do ponto de vista dos programadores, ele tem um grande impacto no desempenho. Você deseja usar o primeiro formulário quase sempre.


0
String str = new String("hello")

Ele irá verificar se o pool constante de String já contém String "hello"? Se presente, ele não adicionará uma entrada no pool constante de String. Se não estiver presente, ele adicionará uma entrada no pool constante de String.

Um objeto será criado em uma área de memória de heap e strpontos de referência para o objeto criado no local da memória de heap.

se você deseja fazer strreferência ao objeto de ponto que contém no pool constante de String, é necessário chamar explicitamentestr.intern();

String str = "world";

Ele irá verificar se o pool constante de String já contém String "hello"? Se presente, ele não adicionará uma entrada no pool constante de String. Se não estiver presente, ele adicionará uma entrada no pool constante de String.

Nos dois casos acima, a strreferência aponta para String "world"presente no pool Constant.


'It' sendo o compilador Java. A literal da cadeia de caracteres cria uma entrada exclusiva no pool constante, no momento da compilação. É um erro deacribe este processo como se isso acontece em tempo de execução ..
Marquês de Lorne

Você pode explicar claramente o que está errado neste post?
Jayesh

O que há de errado neste post é que a string literal é agrupada em tempo de execução, como eu já disse. Não ao executar o código, como em sua resposta.
Marquês de Lorne

@EJP Agradeço sua resposta. Você pode apontar a linha exata que está errada na resposta. Vejo que todas as respostas acima são iguais às que escrevi. Por favor, ajude, quero corrigir meu entendimento. Obrigado.
Jayesh

Você escreveu sobre todo o processo como se tudo acontecesse quando a linha de código fosse executada, o que, como já lhe disse várias vezes, não é o caso. Você não pode reduzir tudo isso a uma única 'linha exata' que está errada em sua resposta.
Marquês de Lorne

0

Quando você armazena uma String como

String string1 = "Hello";

diretamente, a JVM cria um objeto String com o preço especificado durante um bloco de memória separado chamado pool constante de String.

E sempre que temos a tendência de tentar produzir outra String como

String string2 = "Hello";

A JVM verifica se existe algum objeto String com preço constante no conjunto de constantes String, se houver, em vez de criar um novo objeto, a JVM atribui a referência do objeto existente à nova variável.

E quando armazenamos String como

String string = new String("Hello");

usando a nova palavra-chave, um novo objeto com o preço especificado é criado, independentemente do conteúdo do pool constante de String.

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.