Eu tenho uma pergunta simples sobre cadeias de caracteres em Java. O seguinte segmento de código simples apenas concatena duas seqüências e as compara ==
.
String str1="str";
String str2="ing";
String concat=str1+str2;
System.out.println(concat=="string");
A expressão de comparação concat=="string"
retorna false
como óbvia (eu entendo a diferença entre equals()
e ==
).
Quando essas duas cadeias são declaradas final
assim,
final String str1="str";
final String str2="ing";
String concat=str1+str2;
System.out.println(concat=="string");
A expressão de comparação concat=="string"
, neste caso, retorna true
. Por que final
faz a diferença? Isso tem a ver com o pool de estagiários ou estou sendo enganado?
equals()
e==
no contexto de strings e está fazendo uma pergunta mais significativa.String
? Eu acho que é muito lógico, não bobo, fazer a comparação de conteúdoequals
, um método que podemos substituir para dizer quando consideramos dois objetos iguais e ter a comparação de identidade feita por==
. Se a comparação de conteúdo fosse feita por==
nós, não poderíamos substituir isso para definir o que queremos dizer com "conteúdo igual", e ter o significado deequals
e==
revertido apenas paraString
s seria bobagem. Além disso, independentemente disso, não vejo nenhuma vantagem em==
fazer a comparação de conteúdo em vez deequals
.Respostas:
Quando você declara uma variável
String
(que é imutável ) comofinal
e a inicializa com uma expressão constante em tempo de compilação, ela também se torna uma expressão constante em tempo de compilação e seu valor é incorporado pelo compilador em que é usado. Portanto, no seu segundo exemplo de código, após incluir os valores, a concatenação de strings é convertida pelo compilador em:que, quando comparado a
"string"
, fornece a vocêtrue
, porque os literais de string são internados .Do JLS §4.12.4 -
final
Variáveis :Também de JLS §15.28 - Expressão constante:
Este não é o caso no seu primeiro exemplo de código, onde as
String
variáveis não sãofinal
. Portanto, eles não são expressões constantes em tempo de compilação. A operação de concatenação será adiada até o tempo de execução, levando à criação de um novoString
objeto. Você pode verificar isso comparando o código de bytes dos dois códigos.O primeiro exemplo de código (não
final
versão) é compilado no seguinte código de bytes:Claramente, ele está armazenando
str
eing
em duas variáveis separadas e usandoStringBuilder
para executar a operação de concatenação.Visto que o seu segundo exemplo de código (
final
versão) se parece com o seguinte:Por isso, alinha diretamente a variável final para criar String
string
no tempo de compilação, que é carregado pelaldc
operação na etapa0
. Em seguida, a segunda string literal é carregada pelaldc
operação na etapa7
. Não envolve a criação de nenhum novoString
objeto em tempo de execução. A String já é conhecida no momento da compilação e elas são internadas.fonte
true
?String
objeto foi criado recentemente (§12.5), a menos que a expressão seja uma expressão constante (§15.28). ”Literalmente, a aplicação de uma otimização na versão não final não é permitida, pois uma sequência de caracteres“ recém-criada ”deve ter uma identidade de objeto diferente. Não sei se isso é intencional. Afinal, a estratégia de compilação atual é delegar para um recurso de tempo de execução que não documenta essas restrições.De acordo com minha pesquisa, todos
final String
estão internados em Java. De uma das postagens do blog:Portanto, se você ligar,
String.intern()
poderá comparar duas cadeias usando o==
operador. Mas aquiString.intern()
não é necessário porque em Javafinal String
são internamente internados.Você pode encontrar mais informações Comparação de strings usando o operador == e Javadoc para o método String.intern () .
Consulte também esta postagem do Stackoverflow para obter mais informações.
fonte
Se você der uma olhada nesses métodos
e é descompilado com
javap -c ClassWithTheseMethods
versões que você veráe
Portanto, se Strings não forem o compilador final, terá que usar
StringBuilder
para concatenarstr1
estr2
assimserá compilado para
o que significa que
concat
será criado em tempo de execução e, portanto, não virá do pool de String.Além disso, se Strings são finais, o compilador pode assumir que nunca mudará, em vez de usá-
StringBuilder
lo pode concatenar com segurança seus valores,pode ser alterado para
e concatenado em
o que significa que
concate
se tornará literal de picada, que será internado no pool de strings e, em seguida, comparado com a mesma literal de string desse pool naif
instrução.fonte
Conceito de pool de cones de pilha e string
fonte
Vamos ver um código de bytes para o
final
exemploAt
0:
e2:
, oString
"string"
é empurrado para a pilha (do pool constante) e armazenadoconcat
diretamente na variável local . Você pode deduzir que o compilador está criando (concatenando) aString
"string"
si mesmo no momento da compilação.O
final
código não byteAqui você tem duas
String
constantes"str"
e"ing"
que precisam ser concatenadas em tempo de execução com aStringBuilder
.fonte
Entretanto, quando você cria usando a notação literal String de Java, ele chama automaticamente o método intern () para colocar esse objeto no pool de String, desde que ele não estivesse presente no pool.
O compilador sabe que a variável final nunca muda, quando adicionamos essas variáveis finais, a saída vai para String Pool por causa da
str1 + str2
saída da expressão também nunca vai mudar. Então, finalmente, o compilador chama o método inter após a saída das duas variáveis finais acima. No caso de compilador variável não final, não chame o método interno.fonte