2011-09-10 13 views
6

tengo el siguiente código Java:¿Cuántos objetos de cadena se creará

public String makinStrings() { 
    String s = "Fred"; 
    s = s + "47"; 
    s = s.substring(2, 5); 
    s = s.toUpperCase(); 
    return s.toString(); 
} 

La pregunta es de alguna manera sencilla: ¿cuántos objetos String se crea cuando se invoca este método?

Al principio me contestaron que se crean 5 objetos String, pero la respuesta de mi libro dice que sólo 3 objetos se crean y se le dio ninguna explicación (esta es una pregunta SCJP).

Desde mi punto de vista hay 5 objetos: "Fred", "47", "Fred47", "ed4", "ED4".

También encontré esta pregunta en un examen de simulación SCJP, con la misma respuesta 3.

+3

Me imagino que el compilador inline los dos primeros estados y eliminar el último (redundante) Método de llamada. Eso te deja con "Fred47", "ed4", "ED4". –

+1

@Jared 's' no es una * expresión constante en tiempo de compilación *, así que eso no sucede. (Compile el código y use 'javap -c', o incluso' cadenas'). –

+0

posible duplicado de [Java - ¿Cuántos String Objects?] (Http://stackoverflow.com/questions/17898236/java-how-many -string-objects) –

Respuesta

15

"Fred" y "47" vendrá de la piscina literal de cadena. Como tal, no se creará cuando se invoca el método. En su lugar, se colocarán allí cuando se cargue la clase (o antes, si otras clases usan constantes con el mismo valor).

"Fred47", "ed4" y "ED4" son los objetos 3 String que se crearán en cada invocación de método.

+0

¿No estarán "Fred47", "ed4" y "ED4" en el grupo literal de cadena después de la invocación del primer método, también? Creo que la respuesta/comentario de Jared es correcta (+1), porque todos los compiladores que conozco hacen estos simples entramados. Es por eso que la concatenación de cuerdas ya no es tan costosa (aunque de alguna manera). – DaveFar

+1

Es [aparentemente legal] (http://java.sun.com/docs/books/vmspec/2nd-edition/html/Concepts.doc.html#19124) para crear una instancia de una JVM literales de cadena cuando son cargados primero del grupo constante, en lugar de cargarlos fácilmente en el tiempo de carga de la clase. No creo que este detalle de implementación sea realmente relevante para la pregunta original, pero es una interesante trivia. –

+0

Observe que si las dos primeras líneas se escribieron como una sola instrucción, "Fred47" se usaría directamente como '" Fred "+" 47 "' es una * expresión constante en tiempo de compilación *. Además, es posible que la biblioteca se implemente de manera diferente y produzca una cantidad diferente de objetos de cadena. –

2

programas tienden a contener una gran cantidad de literales de cadena en su código. En Java, estas constantes se recopilan en algo llamado tabla de cadenas para mayor eficiencia. Por ejemplo, si se utiliza la cadena "Name: " en diez lugares diferentes, la JVM (por lo general) tiene sólo una instancia de esa cadena y en los diez lugares donde se consume, las referencias apuntan a que una instancia. Esto ahorra memoria.

Esta optimización es posible porque String es inmutable. Si fuera posible cambiar una Cadena, cambiarla un lugar significaría que también cambia en las otras nueve. Es por eso que cualquier operación que cambie una cadena devuelve una nueva instancia. Es por eso que si usted hace esto:

String s = "boink"; 
s.toUpperCase(); 
System.out.println(s); 

imprime boink, no BOINK.

Ahora hay una más poco complicado: varias instancias de java.lang.Stringpuede apuntan a la misma subyacente char[] por sus datos de caracteres, en otras palabras, pueden ser diferentes vistas en el mismo char[], utilizando sólo una rebanada de la matriz. De nuevo, una optimización para la eficiencia. El método substring() es uno de los casos donde esto sucede.

s1 = "Fred47"; 

//String s1: data=[ 'F', 'r', 'e', 'd', '4', '7'], offset=0, length=6 
//     ^........................^ 

s2 = s1.substring(2, 5); 

//String s2: data=[ 'F', 'r', 'e', 'd', '4', '7'], offset=2, length=3 
//        ^.........^ 
// the two strings are sharing the same char[]! 

En su pregunta SCJP, todo esto se reduce a:

  • La cadena "Fred" se toma de la tabla de cadenas.
  • "47" La cadena se toma de la tabla de cadenas.
  • "Fred47" La cadena se crea durante la llamada al método.// 1
  • La cadena "ed4" se crea durante la llamada al método, compartir la misma matriz de soporte como "Fred47" // 2
  • La cadena "ED4" se crea durante la llamada al método. // 3
  • El s.toString() no crea una nueva, simplemente devuelve this.

Un caso extremo interesante de todo esto: considerar lo que sucede si usted tiene un largo de cuerda, por ejemplo, una página web tomada de Internet, digamos que la longitud de la char[] es de dos megabytes. Si se toma el substring(0, 4) de esto, se obtiene una nueva cadena que se ve como si fuera sólo cuatro caracteres de longitud, pero todavía comparte esos dos megabytes de datos de respaldo. Esto no es tan común en el mundo real, ¡pero puede ser una gran pérdida de memoria! En el (raro) caso en que se encuentre con este problema, puede usar new String(hugeString.substring(0, 4)) para crear una Cadena con una nueva matriz de respaldo pequeña.

Por último, es posible forzar una cadena en la tabla de cadenas en tiempo de ejecución llamando intern() en él. La regla básica en este caso: no lo hagas. La regla extendida: no lo haga a menos que haya utilizado un generador de perfiles de memoria para asegurarse de que sea una optimización útil.

+0

Esto es incorrecto. Ninguna de las constantes de cadena proviene de un "grupo de cadenas", todas provienen del grupo constante de clases. Si la misma constante literal como "Fred47" se usa en varias clases, cada una de ellas tiene la misma constante en su conjunto constante. Vea el bytecode de la respuesta de Chip McCormick. –

2

Sobre la base de la salida javap, parece que durante la concatenación se crea un StringBuilder, no una cadena. Hay tres cadenas llamadas subcadena(), toUpperCase() y toString().

La última llamada no es redundante, ya que transforma el StringBuilder en una cadena.

>javap -c Test 
Compiled from "Test.java" 

public java.lang.String makinStrings(); 
Code: 
0: ldc  #5; //String Fred 
2: astore_1 
3: new  #6; //class java/lang/StringBuilder 
6: dup 
7: invokespecial #7; //Method java/lang/StringBuilder."<init>":()V 
10: aload_1 
11: invokevirtual #8; //Method java/lang/StringBuilder.append: (Ljava/lang/String;)Ljava/lang/StringBuilder; 
14: ldc  #9; //String 47 
16: invokevirtual #8; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 
19: invokevirtual #10; //Method java/lang/StringBuilder.toString:()Ljava/lang/String; 
22: astore_1 
23: aload_1 
24: iconst_2 
25: iconst_5 
26: invokevirtual #11; //Method java/lang/String.substring:(II)Ljava/lang/String; 
29: astore_1 
30: aload_1 
31: invokevirtual #12; //Method java/lang/String.toUpperCase:()Ljava/lang/String; 
34: astore_1 
35: aload_1 
36: invokevirtual #13; //Method java/lang/String.toString:()Ljava/lang/String; 
39: areturn 

}

+0

Entonces la respuesta correcta es: 3. ¿O estoy contando mal? –

Cuestiones relacionadas