Tengo una pregunta simple sobre cadenas en Java. El siguiente segmento de código simple solo concatena dos cadenas y luego las compara ==
.
String str1="str";
String str2="ing";
String concat=str1+str2;
System.out.println(concat=="string");
La expresión de comparación concat=="string"
regresa false
como obvia (entiendo la diferencia entre equals()
y ==
).
Cuando estas dos cadenas se declaran final
así,
final String str1="str";
final String str2="ing";
String concat=str1+str2;
System.out.println(concat=="string");
La expresión de comparación concat=="string"
, en este caso, regresa true
. ¿Por qué hace la final
diferencia? ¿Tiene que ver algo con el grupo de internos o simplemente estoy siendo engañado?
equals()
y==
en el contexto de las cadenas, y está haciendo una pregunta más significativa.String
? Creo que es muy lógico, no tonto, realizar la comparación de contenidoequals
, un método que podemos anular para determinar cuándo consideramos que dos objetos son iguales y hacer que la comparación de identidad se realice por==
. Si la comparación de contenido se hiciera por==
no podríamos anular eso para definir lo que queremos decir con "contenido igual", y tener el significadoequals
y==
revertir solo paraString
s sería una tontería. Además, independientemente de esto, no veo ninguna ventaja de todos modos en==
hacer la comparación de contenido en lugar de hacerloequals
.Respuestas:
Cuando declara una variable
String
(que es inmutable ) comofinal
, y la inicializa con una expresión constante en tiempo de compilación, también se convierte en una expresión constante en tiempo de compilación, y el compilador en el que se utiliza inserta su valor. Entonces, en su segundo ejemplo de código, después de incluir los valores, el compilador traduce la concatenación de cadenas a:que en comparación con
"string"
te darátrue
, porque los literales de cadena están internados .De JLS §4.12.4 -
final
Variables :También de JLS §15.28 - Expresión constante:
Este no es el caso en su primer ejemplo de código, donde las
String
variables no lo sonfinal
. Por lo tanto, no son expresiones constantes en tiempo de compilación. La operación de concatenación allí se retrasará hasta el tiempo de ejecución, lo que conducirá a la creación de un nuevoString
objeto. Puede verificar esto comparando el código de bytes de ambos códigos.El primer ejemplo de código (no
final
versión) se compila con el siguiente código de bytes:Claramente está almacenando
str
ying
en dos variables separadas, y está utilizandoStringBuilder
para realizar la operación de concatenación.Mientras que su segundo ejemplo de código (
final
versión) se ve así:Por lo tanto, alinea directamente la variable final para crear String
string
en tiempo de compilación, que se carga porldc
operación en el paso0
. Luego, el segundo literal de cadena se carga porldc
operación en el paso7
. No implica la creación de ningúnString
objeto nuevo en tiempo de ejecución. La cadena ya se conoce en tiempo de compilación, y están internadas.fuente
true
?String
objeto se acaba de crear (§12.5) a menos que la expresión sea una expresión constante (§15.28). "Literalmente, no está permitido aplicar una optimización en la versión no final, ya que una cadena" recién creada "debe tener una identidad de objeto diferente. No sé si esto es intencional. Después de todo, la estrategia de compilación actual es delegar a una instalación de tiempo de ejecución que no documenta tales restricciones.Según mi investigación, todos
final String
están internados en Java. De una de las publicaciones del blog:Por lo tanto, si llama
String.intern()
puede comparar dos cadenas usando el==
operador. Pero aquíString.intern()
no es necesario porque en Javafinal String
se interna internamente.Puede encontrar más información Comparación de cadenas usando el operador == y Javadoc para el método String.intern () .
Consulte también esta publicación de Stackoverflow para obtener más información.
fuente
Si echas un vistazo a estos métodos
y está descompilado con
javap -c ClassWithTheseMethods
versiones que verásy
Entonces, si las cadenas no son el compilador final, tendrá que usar
StringBuilder
para concatenarstr1
ystr2
asíserá compilado a
lo que significa que
concat
se creará en tiempo de ejecución, por lo que no vendrá del conjunto de cadenas.Además, si las cadenas son finales, el compilador puede suponer que nunca cambiarán, por lo que, en lugar de usarlo
StringBuilder
, puede concatenar sus valores de manera segurase puede cambiar a
y concatenado en
lo que significa que
concate
se convertirá en un sting literal que se internará en el grupo de cadenas y luego se comparará con el mismo literal de cadena de ese grupo en laif
declaración.fuente
Concepto de grupo de conts de pila y cuerda
fuente
Veamos un código de bytes para el
final
ejemplo.En
0:
y2:
,String
"string"
se empuja a la pila (desde el grupo constante) y se almacenaconcat
directamente en la variable local . Puede deducir que el compilador está creando (concatenando) elString
"string"
mismo en el momento de la compilación.El
final
código no byteAquí tiene dos
String
constantes,"str"
y"ing"
que deben concatenarse en tiempo de ejecución con aStringBuilder
.fuente
Sin embargo, cuando crea utilizando la notación literal de String de Java, llama automáticamente al método intern () para poner ese objeto en el conjunto de String, siempre que no esté presente en el conjunto.
El compilador sabe que la variable final nunca cambiará, cuando agregamos estas variables finales, la salida va al conjunto de cadenas debido a
str1 + str2
que la salida de expresión tampoco cambiará, por lo que finalmente el compilador llama al método inter después de la salida de las dos variables finales anteriores. En el caso del compilador de variables no final, no llame al método interno.fuente