Hay un código de la siguiente manera:
String s = new String("1"); s.intern(); String s2 = "1"; System.out.println(s == s2); String s3 = new String("1")+new String("1"); s3.intern(); String s4 = "11"; System.out.println(s3 == s4);La salida del código anterior es:
false true Sé que s y s2 son objetos diferentes, por lo que el resultado se evalúa como falso, pero el segundo resultado se evalúa como verdadero. ¿Alguien puede decirme la diferencia?
Esto es lo que está pasando:
String s1 = new String("1"); s1.intern(); String s2 = "1";"1" (pasado al constructor de String ) está internado en la dirección A .s1 se crea en la dirección B porque no es una expresión literal o constante.intern() no tiene efecto. La cadena "1" ya está interna y el resultado de la operación no se vuelve a asignar a s1 .s2 con el valor "1" se recupera del grupo de cadenas, por lo que apunta a la dirección A . Resultado: las cadenas s1 y s2 apuntan a direcciones diferentes .
String s3 = new String("1") + new String("1"); s3.intern(); String s4 = "11";s3 se crea en la dirección C.intern() agrega la cadena con el valor "11" en la dirección C al grupo de cadenas.s4 con el valor "11" se recupera del grupo de cadenas, por lo que apunta a la dirección C . Resultado: las cadenas s3 y s4 apuntan a la misma dirección.
La cadena "1" se interna antes de que se realice la llamada a intern() , en virtud de su presencia en la llamada al constructor s1 = new String("1") .
Cambiar esa llamada al constructor a s1 = new String(new char[]{'1'}) hará que la comparación de s1 == s2 se evalúe como verdadera porque ambos ahora se referirán a la cadena que se internó explícitamente llamando a s1.intern() .
(Utilicé el código de esta respuesta para obtener información sobre las ubicaciones de memoria de las cadenas).
Para el escenario 1:
String s = new String("1"); s.intern(); String s2 = "1"; System.out.println(s == s2);con código de bytes :
0: new #2 // class java/lang/String 3: dup 4: ldc #3 // String 1 6: invokespecial #4 // Method java/lang/String."<init>":(Ljava/lang/String;)V 9: astore_1 10: aload_1 11: invokevirtual #5 // Method java/lang/String.intern:()Ljava/lang/String; 14: pop 15: ldc #3 // String 1 para String s = new String("1"); creará un nuevo objeto String , tendrá una nueva dirección con "1" que ya está en String Pool :
ldc #3 // String 1
y para s2 , como el código de bytes:
15: ldc #3 // String 1
s2 apunta a la variable String Pool : "1" , por lo que s y s2 tienen direcciones diferentes y el resultado es false .
Para el escenario 2:
String s3 = new String("1")+new String("1"); s3.intern(); String s4 = "11"; System.out.println(s3 == s4);con código de bytes :
0: new #2 // class java/lang/StringBuilder 3: dup 4: invokespecial #3 // Method java/lang/StringBuilder."<init>":()V 7: astore_1 8: aload_1 9: ldc #4 // String 1 11: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 14: ldc #4 // String 1 16: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 19: invokevirtual #6 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 22: astore_2 23: aload_2 24: invokevirtual #7 // Method java/lang/String.intern:()Ljava/lang/String; 27: astore_3 28: ldc #8 // String 11 Como código de bytes , puede ver new String("1")+new String("1"); se crea usando StringBuilder
new #2 // class java/lang/StringBuilder
es totalmente un objeto nuevo sin la variable String Pool .
y después de s3.intern() , este método agregará s3 actual al grupo de cadenas de memoria y 8: aload_1 .
y s4 está intentando cargar desde
ldc #8 // String 11
por lo tanto, las direcciones s3 y s4 deben ser iguales y el resultado es verdadero.