¿Cuál es la diferencia entre "texto" y nueva cadena ("texto")?


195

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

String s = "text";

String s = new String("text");


Alguien por favor responda a esto. Cadena a = "Java"; Cadena b = "Java"; System.out.println (a == b); verdadero // pero System.out.println ("a == b?" + a == b); // falso ...
Energía

no entiendo cuando agregué algún comentario ("a == b?) => mi resultado se vuelve FALSO. ¿por qué?
Energía

@Energy El resultado es falseporque el orden de las operaciones dicta que el operador + vaya primero, concatenando "a == b?" con a para crear una Cadena "a == b? Java". Entonces la expresión se "a==b?Java" == bevalúa como falsa.
Allison B

@AllisonB lo entendió, ¡muchas gracias!
Energía

Respuestas:


187

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

Usted muy raramente va a querer utilizar el new String(anotherString)constructor. De la API:

String(String original): Inicializa un objeto recién creado String 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 de 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


Qué significa 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 equalsson no son necesariamente ==. Por lo general, es incorrecto usarlo ==en tipos de referencia; la mayor parte del tiempo equalsdebe usarse en su lugar.

No obstante, si por alguna razón necesita crear dos equalspero no ==cadenas, puede usar el new String(anotherString)constructor. Sin embargo, debe decirse nuevamente que esto es muy peculiar y rara vez es la intención.

Referencias

Asuntos relacionados


3
Si escribo: String s = new String ("abc"); Y ahora escribo: String s = "abc"; Will String s = "abc"; crear un nuevo literal de cadena en el grupo de cadenas?
Kaveesh Kanwal

¿Por qué nadie responde la pregunta anterior?
Zeds

2
@KaveeshKanwal No, el literal no se duplicará. Como puedes ver hay 2 "abc"s. Solo uno de ellos irá al grupo de cadenas y el otro se referirá a él. Luego está el sque será un nuevo objeto adecuado.
Kayaman

1
@Kaveesh Kanwal - String s = new String ("abc") solo creará un nuevo objeto String con el valor "abc". Y la segunda instrucción verificará si hay algún literal de cadena "abc" que ya esté presente en el Conjunto de cadenas o no. Si ya está presente, se devuelve la referencia al existente y, de lo contrario, se crea un nuevo literal ("abc") en el conjunto de cadenas. Espero que resuelva tu consulta !!
user968813

No hay "poder" al respecto. El compilador debe agrupar literales de cadena. JLS 3.10.5 .
Marqués de Lorne

119

Los literales de cadena irán al conjunto de cadenas constantes .

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

ingrese la descripción de la imagen aquí


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 constantes de cadena. Usando un 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 constante de cadenas

String str3 = new String(str2);

Se crea un nuevo objeto de cadena en el montón con el mismo valor que la referencia de "str2"

String str4 = "java5";

La referencia "str4" apunta al valor ya almacenado en el grupo constante de cadenas

Total de objetos: montón - 2, grupo - 1

Más información sobre la comunidad de Oracle


1
Buena respuesta ... pero ¿quería saber que ahora voy a cambiar el valor de str1 = "java6" y luego cambiará el valor de str4?
CoronaPintu

2
Sí, tuve que comprobar que no va a cambiar el valor de str4
CoronaPintu

@Braj ¿Puede proporcionar documentación de la afirmación de su respuesta?
Basil Bourque

@Braj: ¿Se supone que los encabezados para el 'Montón' y 'pool' en la tabla son inversos?
Rahul Kurup

Incorrecto. El grupo constante se crea en tiempo de compilación, no en tiempo de ejecución. No utilice el formato de comillas para el texto que no se cita.
Marqués de Lorne

15

Uno crea una cadena en el grupo constante de cadenas

String s = "text";

el otro crea una cadena en el grupo constante ( "text") y otra cadena en el espacio de almacenamiento dinámico normal ( s). 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 lo 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 grupo).


Las cadenas en el grupo constante nunca se pierden. ¿Quiso decir que 's' se pierde si luego no se usa?
Marqués de Lorne

@EJP: sí, quise decir "s". Gracias por notarlo. Corregiré la pregunta.

9

JLS

El concepto es llamado "internado" 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 la clase String. Esto se debe a que los literales de cadena, o, más generalmente, las cadenas que son valores de expresiones constantes (§15.28), están "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 el literal de cadena.

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) deben referirse a la misma instancia de la clase Cadena (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 se ha llamado previamente en una instancia de clase String que contiene una secuencia de puntos de código Unicode idénticos a los proporcionados por 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:

  • 0y 3: ldc #2se carga la misma constante (los literales)
  • 12: se crea una nueva instancia de cadena (con #2 argumento)
  • 35: ay cse comparan como objetos normales conif_acmpne

La representación de cadenas constantes es bastante mágica en el código de bytes:

  • tiene una estructura dedicada CONSTANT_String_info , a diferencia de los objetos normales (por ejemplo new String)
  • la estructura apunta a una estructura CONSTANT_Utf8_info que contiene los datos. Esos son los únicos datos necesarios para representar la cadena.

y la cita JVMS anterior parece decir que siempre que el Utf8 apuntado es el mismo, se cargan instancias idénticas ldc.

He realizado pruebas similares para campos y:

  • static final String s = "abc"apunta a la tabla constante 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 un soporte directo de bytecode para el conjunto de cadenas, y la representación de la memoria es eficiente.

Bonificación: compárelo con el grupo Integer , que no tiene soporte directo de bytecode (es decir, no CONSTANT_String_infoanalógico).


2

@Braj: creo que has mencionado lo contrario. 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)

String str2 = "java5"

  pool- str2 => "java5" (1 Object)

  heap - str1 => "java5" (1 Object)

String str3 = nueva cadena (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)

str1no está involucrado en el valor de str2ostr3 , o str4de cualquier manera.
Marqués de Lorne

1

Piensa en "bla"ser una fábrica mágica como Strings.createString("bla")(pseudo). La fábrica posee un conjunto de todas las cadenas creadas de esta manera.

Si se invoca, comprueba si ya hay una 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 new String("")anula este comportamiento al omitir el grupo literal de cadenas. Por lo tanto, la igualdad siempre debe verificarse utilizando el equals()que compara la secuencia de caracteres en lugar de la igualdad de referencia del objeto.


La 'fábrica mágica' a la que te refieres no es más ni menos que el compilador de Java. Es un error escribir sobre este proceso como si ocurriera en tiempo de ejecución.
Marqués de Lorne

1

Una manera simple de entender la diferencia es la siguiente:

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

la salida es

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

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


1

Cualquier literal de cadena se crea dentro del grupo de literales de cadena y el grupo no permite duplicados. Por lo tanto, si dos o más objetos de cadena se inicializan con el mismo valor literal, todos los objetos apuntarán al mismo literal.

String obj1 = "abc";
String obj2 = "abc";

"obj1" y "obj2" apuntarán al mismo literal de cadena y el grupo de literales de cadena solo tendrá un literal "abc".

Cuando creamos un objeto de clase String usando la nueva palabra clave, la cadena así creada se almacena en la memoria de almacenamiento dinámico. Sin embargo, cualquier cadena literal que se pase como parámetro al constructor de la clase String se almacena en el grupo de cadenas. Si creamos múltiples objetos usando el mismo valor con el nuevo operador, se creará un nuevo objeto en el montón cada vez, debido a este nuevo operador se debe evitar.

String obj1 = new String("abc");
String obj2 = new String("abc");

"obj1" y "obj2" apuntarán a dos objetos diferentes en el montón y el grupo de cadenas literales solo tendrá un literal "abc".

También algo que vale la pena señalar con respecto al comportamiento de las cadenas es que cualquier nueva asignación o concatenación realizada en una cadena crea un nuevo objeto en la memoria.

String str1 = "abc";
String str2 = "abc" + "def";
str1 = "xyz";
str2 = str1 + "ghi";

Ahora en el caso anterior:
Línea 1: el literal "abc" se almacena en el conjunto de cadenas.
Línea 2: el literal "abcdef" se almacena en el conjunto de cadenas.
Línea 3: se almacena un nuevo literal "xyz" en el conjunto de cadenas y "str1" comienza a apuntar a este literal.
Línea 4: dado que el valor se genera al agregar a otra variable, el resultado se almacena en la memoria de almacenamiento dinámico y el literal que se agrega "ghi" se verificará para su existencia en el conjunto de cadenas y se creará ya que no existe en El caso anterior.


0

Aunque se ve igual desde el punto de vista de los programadores, tiene un gran impacto en el rendimiento. Desearía usar la primera forma casi siempre.


0
String str = new String("hello")

¿Verificará si el grupo constante de cadenas ya contiene la cadena "hola"? Si está presente, no agregará una entrada en el grupo constante de cadenas. Si no está presente, agregará una entrada en el grupo constante de cadenas.

Se creará un objeto en un área de memoria de almacenamiento dinámico y str puntos de referencia al objeto creado en la ubicación de memoria de almacenamiento dinámico.

si desea strreferencia al objeto de punto que contiene en el grupo constante de Cadena, entonces uno tiene que llamar explícitamentestr.intern();

String str = "world";

¿Verificará si el grupo constante de cadenas ya contiene la cadena "hola"? Si está presente, no agregará una entrada en el grupo constante de cadenas. Si no está presente, agregará una entrada en el grupo constante de cadenas.

En ambos casos, los strpuntos de referencia a Cadena están "world"presentes en el grupo Constante.


'It' es el compilador de Java. El literal de cadena crea una entrada única en el grupo constante, en tiempo de compilación. Es un error describir este proceso como si ocurriera en tiempo de ejecución ..
Marqués de Lorne

¿Puedes explicar claramente qué está mal en esta publicación?
Jayesh

Lo que está mal en esta publicación es que el literal de cadena se agrupa en tiempo de compilación, como ya dije. No al ejecutar el código, como en su respuesta.
Marqués de Lorne

@EJP Agradezco su respuesta. ¿Puede señalar la línea exacta que está mal en la respuesta? Veo que todas las respuestas anteriores son las mismas que escribí. Por favor ayuda, quiero corregir mi comprensión. Gracias.
Jayesh

Ha escrito sobre todo el proceso como si todo tuviera lugar cuando se ejecuta la línea de código, lo cual, como le he dicho repetidamente, no es el caso. No puede reducir todo eso a una sola "línea exacta" incorrecta en su respuesta.
Marqués de Lorne

0

Cuando almacena una cadena como

String string1 = "Hello";

directamente, entonces JVM crea un objeto String con el precio dado durante un bloque de memoria separado llamado grupo constante de String.

Y siempre que tengamos la tendencia de intentar producir otra Cadena como

String string2 = "Hello";

JVM verifica si existe o no un objeto String con precio constante dentro del grupo de constantes String, en caso afirmativo, en lugar de crear un objeto completamente nuevo, JVM asigna la referencia del objeto existente a la nueva variable.

Y cuando almacenamos String como

String string = new String("Hello");

usando la nueva palabra clave, se crea un nuevo objeto con el precio dado sin importar el contenido del conjunto constante de cadenas.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.