studio programacion para móviles libro edición desarrollo desarrollar curso aprende aplicaciones java string

programacion - ¿Qué es el conjunto de cadenas de Java y cómo es "s" diferente de la nueva Cadena("s")?



manual de programacion android pdf (6)

JLS

Como mencionó Andrew , 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 programa que consiste en la unidad de compilació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 compilació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 programació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 descompilamos:

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."<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

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

Esta pregunta ya tiene una respuesta aquí:

¿Qué se entiende por String Pool ? Y cuál es la diferencia entre las siguientes declaraciones:

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

¿Hay alguna diferencia entre el almacenamiento de estas dos cadenas por JVM?


El grupo de cadenas es la implementación particular de la JVM del concepto de prácticas de cadenas :

En ciencias de la computación, el internamiento de cadenas es un método para almacenar solo una copia de cada valor de cadena distinto, que debe ser inmutable. Las cadenas de internados hacen que algunas tareas de procesamiento de cadenas sean más eficientes en tiempo o espacio a costa de requerir más tiempo cuando la cadena se crea o se interna. Los valores distintos se almacenan en un grupo interno de cadena.

Básicamente, un grupo interno de cadenas permite que el tiempo de ejecución ahorre memoria al preservar cadenas inmutables en un grupo de modo que las áreas de la aplicación puedan reutilizar instancias de cadenas comunes en lugar de crear múltiples instancias de la misma.

Como una nota interesante, el internamiento de cuerdas es un ejemplo del patrón de diseño de peso mosca :

Flyweight es un patrón de diseño de software. Un flyweight es un objeto que minimiza el uso de memoria al compartir tantos datos como sea posible con otros objetos similares; es una forma de usar objetos en grandes cantidades cuando una representación repetida simple usaría una cantidad inaceptable de memoria.


El grupo de cadenas permite reutilizar las constantes de cadena, lo cual es posible porque las cadenas en Java son inmutables. Si repite la misma constante de cadena en todo el lugar en su código Java, en realidad puede tener una sola copia de esa cadena en su sistema, que es una de las ventajas de este mecanismo.

Cuando utiliza String s = "string constant"; obtienes la copia que está en el grupo de cadenas. Sin embargo, cuando haces String s = new String("string constant"); obligas a asignar una copia.


Es desconcertante que nadie respondió directamente la pregunta, pero la mayoría de las respuestas tienen muchos votos ascendentes.

En pocas palabras, el primero crea una entrada en el String Pool, que puede ser reutilizado (más eficiente debido a los enlaces anteriores sobre la inmutabilidad), y el segundo crea un nuevo objeto String (más costoso).

Ambos objetos viven en el Heap. Las referencias a ambos estarán en la pila del hilo.

http://www.journaldev.com/797/what-is-java-string-pool da una idea clara de cómo se logra esto


La JVM realiza algunos trucos al crear instancias de cadenas literales para aumentar el rendimiento y disminuir la sobrecarga de memoria. Para reducir el número de objetos String creados en la JVM, la clase String mantiene un conjunto de cadenas. Cada vez que su código crea un literal de cadena, la JVM comprueba primero el grupo de cadenas literales. Si la cadena ya existe en el conjunto, vuelve a aparecer una referencia a la instancia agrupada. Si la cadena no existe en el conjunto, un nuevo objeto String crea una instancia, luego se coloca en el grupo.

public class Program { public static void main(String[] args) { String str1 = "Hello"; String str2 = "Hello"; System.out.print(str1 == str2); } }

Salida: verdadero

Desafortunadamente, cuando usas

String a=new String("Hello");

El objeto String se crea a partir del grupo literal String, incluso si ya existe una cadena igual en el grupo.

public class Program { public static void main(String[] args) { String str1 = "Hello"; String str2 = new String("Hello"); System.out.print(str1 == str2 ); } }

Salida: falsa


Los objetos de cadena son básicamente envoltorios alrededor de literales de cadena. Los objetos de cadena únicos se agrupan para evitar la creación innecesaria de objetos, y la JVM puede decidir agrupar literales de cadena internamente. También hay soporte directo de bytecode para las constantes String a las que se hace referencia varias veces, siempre que el compilador lo admita.

Cuando usas un literal, di String str = "abc"; , se usa el objeto en el grupo. Si usa String str = new String("abc"); , se crea un objeto nuevo, pero el literal de cadena existente puede reutilizarse en el nivel de JVM o en el nivel de bytecode (en tiempo de compilación).

Puede comprobarlo usted mismo creando muchas cadenas en un ciclo for y utilizando el operador == para verificar la igualdad de objetos. En el siguiente ejemplo, string.value es privado para String y contiene el literal de cadena utilizado. Debido a que es privado, se debe acceder a través de la reflexión.

public class InternTest { public static void main(String[] args) { String rehi = "rehi"; String rehi2 = "rehi"; String rehi2a = "not rehi"; String rehi3 = new String("rehi"); String rehi3a = new String("not rehi"); String rehi4 = new String(rehi); String rehi5 = new String(rehi2); String rehi6 = new String(rehi2a); String[] arr = new String[] { rehi, rehi2, rehi2a, rehi3, rehi3a, rehi4, rehi5, rehi6 }; String[] arr2 = new String[] { "rehi", "rehi (2)", "not rehi", "new String(/"rehi/")", "new String(/"not rehi/")", "new String(rehi)", "new String(rehi (2))", "new String(not rehi)" }; Field f; try { f = String.class.getDeclaredField("value"); f.setAccessible(true); } catch (NoSuchFieldException | SecurityException e) { throw new IllegalStateException(e); } for (int i = 0; i < arr.length; i++) { for (int j = 0; j < arr.length; j++) { System.out.println("i: " +arr2[i]+", j: " +arr2[j]); System.out.println("i==j: " + (arr[i] == arr[j])); System.out.println("i equals j: " + (arr[i].equals(arr[j]))); try { System.out.println("i.value==j.value: " + (f.get(arr[i]) == f.get(arr[j]))); } catch (IllegalArgumentException | IllegalAccessException e) { throw new IllegalStateException(e); } System.out.println("========"); } } } }

Salida:

i: rehi, j: rehi i==j: true i equals j: true i.value==j.value: true ======== i: rehi, j: rehi (2) i==j: true i equals j: true i.value==j.value: true ======== i: rehi, j: not rehi i==j: false i equals j: false i.value==j.value: false ======== i: rehi, j: new String("rehi") i==j: false i equals j: true i.value==j.value: true ======== i: rehi, j: new String("not rehi") i==j: false i equals j: false i.value==j.value: false ======== i: rehi, j: new String(rehi) i==j: false i equals j: true i.value==j.value: true ======== i: rehi, j: new String(rehi (2)) i==j: false i equals j: true i.value==j.value: true ======== i: rehi, j: new String(not rehi) i==j: false i equals j: false i.value==j.value: false ======== i: rehi (2), j: rehi i==j: true i equals j: true i.value==j.value: true ======== i: rehi (2), j: rehi (2) i==j: true i equals j: true i.value==j.value: true ======== i: rehi (2), j: not rehi i==j: false i equals j: false i.value==j.value: false ======== i: rehi (2), j: new String("rehi") i==j: false i equals j: true i.value==j.value: true ======== i: rehi (2), j: new String("not rehi") i==j: false i equals j: false i.value==j.value: false ======== i: rehi (2), j: new String(rehi) i==j: false i equals j: true i.value==j.value: true ======== i: rehi (2), j: new String(rehi (2)) i==j: false i equals j: true i.value==j.value: true ======== i: rehi (2), j: new String(not rehi) i==j: false i equals j: false i.value==j.value: false ======== i: not rehi, j: rehi i==j: false i equals j: false i.value==j.value: false ======== i: not rehi, j: rehi (2) i==j: false i equals j: false i.value==j.value: false ======== i: not rehi, j: not rehi i==j: true i equals j: true i.value==j.value: true ======== i: not rehi, j: new String("rehi") i==j: false i equals j: false i.value==j.value: false ======== i: not rehi, j: new String("not rehi") i==j: false i equals j: true i.value==j.value: true ======== i: not rehi, j: new String(rehi) i==j: false i equals j: false i.value==j.value: false ======== i: not rehi, j: new String(rehi (2)) i==j: false i equals j: false i.value==j.value: false ======== i: not rehi, j: new String(not rehi) i==j: false i equals j: true i.value==j.value: true ======== i: new String("rehi"), j: rehi i==j: false i equals j: true i.value==j.value: true ======== i: new String("rehi"), j: rehi (2) i==j: false i equals j: true i.value==j.value: true ======== i: new String("rehi"), j: not rehi i==j: false i equals j: false i.value==j.value: false ======== i: new String("rehi"), j: new String("rehi") i==j: true i equals j: true i.value==j.value: true ======== i: new String("rehi"), j: new String("not rehi") i==j: false i equals j: false i.value==j.value: false ======== i: new String("rehi"), j: new String(rehi) i==j: false i equals j: true i.value==j.value: true ======== i: new String("rehi"), j: new String(rehi (2)) i==j: false i equals j: true i.value==j.value: true ======== i: new String("rehi"), j: new String(not rehi) i==j: false i equals j: false i.value==j.value: false ======== i: new String("not rehi"), j: rehi i==j: false i equals j: false i.value==j.value: false ======== i: new String("not rehi"), j: rehi (2) i==j: false i equals j: false i.value==j.value: false ======== i: new String("not rehi"), j: not rehi i==j: false i equals j: true i.value==j.value: true ======== i: new String("not rehi"), j: new String("rehi") i==j: false i equals j: false i.value==j.value: false ======== i: new String("not rehi"), j: new String("not rehi") i==j: true i equals j: true i.value==j.value: true ======== i: new String("not rehi"), j: new String(rehi) i==j: false i equals j: false i.value==j.value: false ======== i: new String("not rehi"), j: new String(rehi (2)) i==j: false i equals j: false i.value==j.value: false ======== i: new String("not rehi"), j: new String(not rehi) i==j: false i equals j: true i.value==j.value: true ======== i: new String(rehi), j: rehi i==j: false i equals j: true i.value==j.value: true ======== i: new String(rehi), j: rehi (2) i==j: false i equals j: true i.value==j.value: true ======== i: new String(rehi), j: not rehi i==j: false i equals j: false i.value==j.value: false ======== i: new String(rehi), j: new String("rehi") i==j: false i equals j: true i.value==j.value: true ======== i: new String(rehi), j: new String("not rehi") i==j: false i equals j: false i.value==j.value: false ======== i: new String(rehi), j: new String(rehi) i==j: true i equals j: true i.value==j.value: true ======== i: new String(rehi), j: new String(rehi (2)) i==j: false i equals j: true i.value==j.value: true ======== i: new String(rehi), j: new String(not rehi) i==j: false i equals j: false i.value==j.value: false ======== i: new String(rehi (2)), j: rehi i==j: false i equals j: true i.value==j.value: true ======== i: new String(rehi (2)), j: rehi (2) i==j: false i equals j: true i.value==j.value: true ======== i: new String(rehi (2)), j: not rehi i==j: false i equals j: false i.value==j.value: false ======== i: new String(rehi (2)), j: new String("rehi") i==j: false i equals j: true i.value==j.value: true ======== i: new String(rehi (2)), j: new String("not rehi") i==j: false i equals j: false i.value==j.value: false ======== i: new String(rehi (2)), j: new String(rehi) i==j: false i equals j: true i.value==j.value: true ======== i: new String(rehi (2)), j: new String(rehi (2)) i==j: true i equals j: true i.value==j.value: true ======== i: new String(rehi (2)), j: new String(not rehi) i==j: false i equals j: false i.value==j.value: false ======== i: new String(not rehi), j: rehi i==j: false i equals j: false i.value==j.value: false ======== i: new String(not rehi), j: rehi (2) i==j: false i equals j: false i.value==j.value: false ======== i: new String(not rehi), j: not rehi i==j: false i equals j: true i.value==j.value: true ======== i: new String(not rehi), j: new String("rehi") i==j: false i equals j: false i.value==j.value: false ======== i: new String(not rehi), j: new String("not rehi") i==j: false i equals j: true i.value==j.value: true ======== i: new String(not rehi), j: new String(rehi) i==j: false i equals j: false i.value==j.value: false ======== i: new String(not rehi), j: new String(rehi (2)) i==j: false i equals j: false i.value==j.value: false ======== i: new String(not rehi), j: new String(not rehi) i==j: true i equals j: true i.value==j.value: true ========