¿Cuál es la diferencia entre “texto” y una nueva cadena (“texto”)?

¿Cuál es la diferencia entre estas dos afirmaciones siguientes?

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

new String("text"); explícitamente crea una instancia nueva y referencialmente distinta de un objeto String ; String s = "text"; puede reutilizar una instancia del conjunto de constantes de cadena si hay una disponible.

Muy rara vez querrá utilizar el new String(anotherString) constructor new String(anotherString) . De la API:

String(String original) : Inicializa un objeto String recién creado para que represente la misma secuencia de caracteres que el argumento; en otras palabras, la cadena recién creada es una copia de la cadena del argumento. A menos que se necesite una copia explícita del original, el uso de este constructor es innecesario ya que las cadenas son inmutables.

Preguntas relacionadas

  • Java Strings: “String s = new String (” tonto “);”
  • Las cadenas son objetos en Java, entonces, ¿por qué no usamos ‘nuevo’ para crearlos?

Qué significa la distinción referencial

Examine el siguiente fragmento:

  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 

== en dos tipos de referencia es una comparación de identidad de referencia. Dos objetos que son equals no son necesariamente == . Por lo general, es incorrecto usar == en los tipos de referencia; la mayor parte del tiempo equals a ser usado en su lugar.

No obstante, si por algún motivo necesita crear dos cadenas equals pero no == , puede usar el new String(anotherString) constructor new String(anotherString) . Sin embargo, es necesario decir de nuevo que esto es muy peculiar y rara vez es la intención.

Referencias

  • JLS 15.21.3 Operadores de igualdad de referencia == y! =
  • class Objectclass Object boolean Object(equals)

Asuntos relacionados

  • Java String.equals versus ==
  • ¿Cómo comparo cadenas en Java?

Los literales de cadena entrarán en el conjunto constante de cadenas .

La instantánea siguiente puede ayudarlo a comprenderla visualmente para recordarla por más tiempo.

enter image description here


Creación de objetos línea por línea:

 String str1 = new String("java5"); 

Usando el literal de cadena “java5” en el constructor, se almacena un nuevo valor de cadena en el grupo de constante de cadena. Con el nuevo operador, se crea un nuevo objeto de cadena en el montón con “java5” como valor.

 String str2 = "java5" 

La referencia “str2” apunta al valor ya almacenado en el grupo de constante de cadena

 String str3 = new String(str2); 

Se crea un nuevo objeto de cadena en el montón con el mismo valor como referencia por “str2”

 String str4 = "java5"; 

La referencia “str4” apunta al valor ya almacenado en el grupo de constante de cadena

Total de objetos: Heap – 2, Pool – 1

Lectura adicional sobre la comunidad de Oracle

Uno crea una cadena en el conjunto constante de cadenas

 String s = "text"; 

el otro crea una cadena en el conjunto constante ( "text" ) y otra cadena en el espacio ( s ) de stack normal. Ambas cadenas tendrán el mismo valor, el de “texto”.

 String s = new String("text"); 

s luego se pierde (elegible para GC) si luego no se usa.

Los literales de cadena, por otro lado, se reutilizan. Si usa "text" en varios lugares de su clase, de hecho será una y solo una Cadena (es decir, múltiples referencias a la misma cadena en el conjunto).

JLS

El concepto se llama “internamiento” por el JLS.

Pasaje relevante de JLS 7 3.10.5 :

Además, un literal de cadena siempre se refiere a la misma instancia de clase String. Esto se debe a que los literales de cadena (o, más generalmente, las cadenas que son los valores de las expresiones constantes (§15.28)) son “internados” para compartir instancias únicas, utilizando el método String.intern.

Ejemplo 3.10.5-1. Literales de cuerda

El progtwig que consiste en la unidad de comstackción (§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"; } 

y la unidad de comstackción:

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

produce la salida:

 true true true true false true 

JVMS

JVMS 7 5.1 dice :

Un literal de cadena es una referencia a una instancia de clase String, y se deriva de una estructura CONSTANT_String_info (§4.4.3) en la representación binaria de una clase o interfaz. La estructura CONSTANT_String_info proporciona la secuencia de puntos de código Unicode que constituyen la cadena literal.

El lenguaje de progtwigción Java requiere que los literales de cadena idénticos (es decir, los literales que contienen la misma secuencia de puntos de código) se refieran a la misma instancia de la clase String (JLS §3.10.5). Además, si se llama al método String.intern en cualquier cadena, el resultado es una referencia a la misma instancia de clase que se devolvería si esa cadena apareciera como un literal. Por lo tanto, la siguiente expresión debe tener el valor verdadero:

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

Para derivar un literal de cadena, la Máquina Virtual Java examina la secuencia de puntos de código dada por la estructura CONSTANT_String_info.

  • Si el método String.intern ha sido invocado previamente en una instancia de clase String que contiene una secuencia de puntos de código Unicode idénticos a los de la estructura CONSTANT_String_info, el resultado de la derivación literal de cadena es una referencia a esa misma instancia de clase String.

  • De lo contrario, se crea una nueva instancia de clase String que contiene la secuencia de puntos de código Unicode dada por la estructura CONSTANT_String_info; una referencia a esa instancia de clase es el resultado de la derivación literal de cadena. Finalmente, se invoca el método interno de la nueva instancia de String.

Bytecode

También es instructivo observar la implementación de bytecode en OpenJDK 7.

Si descomstackmos:

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

tenemos en el grupo constante:

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

y 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 

Tenga en cuenta cómo:

  • 0 y 3 : se carga la misma constante ldc #2 (los literales)
  • 12 : se crea una nueva instancia de cadena (con #2 como argumento)
  • 35 : a y c se comparan como objetos regulares con if_acmpne

La representación de cadenas constantes es bastante mágica en el bytecode:

  • tiene una estructura CONSTANT_String_info dedicada, a diferencia de los objetos normales (por ejemplo, new String )
  • la estructura apunta a una estructura CONSTANT_Utf8_info que contiene los datos. Ese es el único dato necesario para representar la cadena.

y la cita de JVMS anterior parece decir que siempre que el Utf8 señalado es el mismo, entonces instancias idénticas son cargadas por ldc .

He hecho pruebas similares para los campos, y:

  • static final String s = "abc" apunta a la tabla de constantes a través del atributo ConstantValue
  • los campos no finales no tienen ese atributo, pero aún se pueden inicializar con ldc

Conclusión : existe una compatibilidad directa con bytecode para el grupo de cadenas, y la representación de la memoria es eficiente.

Bonificación: compare eso con el conjunto de enteros , que no tiene soporte directo de bytecode (es decir, ningún análogo CONSTANT_String_info ).

Piense en "bla" como una fábrica de magia como Strings.createString("bla") (pseudo). La fábrica tiene un conjunto de todas las cadenas aún creadas de esta manera.

Si se invoca, comprueba si ya hay cadena en el grupo con este valor. Si es verdadero, devuelve este objeto de cadena, por lo tanto, las cadenas obtenidas de esta manera son de hecho el mismo objeto.

De lo contrario, crea un nuevo objeto de cadena internamente, lo guarda en el grupo y luego lo devuelve. Por lo tanto, cuando se consulta el mismo valor de cadena la próxima vez, devuelve la misma instancia.

La creación manual de un new String("") anula este comportamiento omitiendo el grupo de cadenas literales. Por lo tanto, siempre se debe verificar la equals() usando equals() que compara la secuencia de caracteres en lugar de la igualdad de referencia del objeto.

Una forma simple de entender la diferencia es a continuación:

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

salida es

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

Por lo tanto, el nuevo String () siempre creará una nueva instancia.

@ Braj: creo que has mencionado al revés. Por favor, corríjame si estoy equivocado

Creación de objetos línea por línea:

String str1 = new String (“java5”)

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

Str2 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) 

Aunque tiene el mismo aspecto desde el punto de vista de los progtwigdores, tiene un gran impacto en el rendimiento. Desearía usar la primera forma casi siempre.

 String str = new String("hello") 

Verificará si el conjunto constante de cadenas ya contiene la cadena “hola”. Si está presente, entonces no agregará una entrada en el conjunto de constantes de cadena. Si no está presente, agregará una entrada en el grupo de constante de cadena.

Se creará un objeto en un área de memoria de montón y puntos de referencia str a objeto creado en la ubicación de memoria de montón.

si quiere una referencia str al objeto de punto que contiene el conjunto de constante de cadena, entonces tiene que llamar explícitamente a str.intern();

 String str = "world"; 

Verificará si el conjunto constante de cadenas ya contiene la cadena “hola”. Si está presente, entonces no agregará una entrada en el conjunto de constantes de cadena. Si no está presente, agregará una entrada en el grupo de constante de cadena.

En ambos casos, los puntos de referencia str a String "world" presentes en el grupo Constant.