Intern() comportándose de manera diferente en Java 6 y Java 7
string java-7 (9)
class Test {
public static void main(String...args) {
String s1 = "Good";
s1 = s1 + "morning";
System.out.println(s1.intern());
String s2 = "Goodmorning";
if (s1 == s2) {
System.out.println("both are equal");
}
}
}
Este código produce diferentes salidas en Java 6 y Java 7. En Java 6, la condición s1==s2
devuelve false
y en Java 7 el s1==s2
devuelve true
. ¿Por qué?
¿Por qué este programa produce resultados diferentes en Java 6 y Java 7?
Cuando esté comparando entre dos String, no use ==
y use eqauls()
porque está comparando objetos y no referencias:
string1.equals(string2);
El código de resultado dependientes en tiempo de ejecución:
class Test {
public static void main(String... args) {
String s1 = "Good";
s1 = s1 + "morning";
System.out.println(s1 == s1.intern()); // Prints true for jdk7, false - for jdk6.
}
}
Si escribes así:
class Test {
public static void main(String... args) {
String s = "GoodMorning";
String s1 = "Good";
s1 = s1 + "morning";
System.out.println(s1 == s1.intern()); // Prints false for both jdk7 and jdk6.
}
}
la razón es ''ldc #N'' (Cargar cadena de agrupación constante) y String.intern () ambos usarán StringTable en el punto de acceso JVM. Para más detalles, escribí un artículo en inglés sobre el pool: http://aprilsoft.cn/blog/post/307.html
En jdk6: String s1="Good";
crea un objeto String "Bueno" en conjunto constante.
s1=s1+"morning";
crea otro objeto String "morning" en el grupo constante, pero esta vez en realidad JVM do: s1=new StringBuffer().append(s1).append("morning").toString();
.
Ahora, cuando el new
operador crea un objeto en el montón, la referencia en s1
es del conjunto no constante del montón y la String s2="Goodmorning";
crea un objeto String "Goodmorning" en un grupo constante cuya referencia se almacena en s2
.
Por lo tanto, if(s1==s2)
condición es falsa.
¿Pero que pasa en jdk7?
Hay principalmente 4 formas de comparar la cadena:
- "== operador": simplemente compara la variable de referencia del objeto de cadena. Por lo tanto, podría darte resultados inesperados dependiendo de cómo hayas creado la cadena, es decir, usar el constructor de la clase String o simplemente usar comillas dobles, ya que ambos obtienen memoria de manera diferente (en el montón y en el grupo, respectivamente).
- "método igual a (objeto)": este es el método de la clase de objeto y está OVERLOADED por la clase de cadena. Se compara la cadena entera y es sensato al caso.
- "Método equalsIgnoreCase (String)": este es el método de la clase de cadena y compara toda la cadena y NO ES SENSIBLE AL CASO.
- "método de comparación (Cadena)": compare ambos caracteres cadena por carácter y devuelva su diferencia si el valor devuelto es 0, esto significa que las cadenas son iguales.
Necesitas usar s1.equals(s2)
. Usar ==
con objetos String
compara las referencias de objetos en sí mismas.
Edición: cuando ejecuto el segundo fragmento de código, no se imprimen "ambos son iguales".
Edit2: aclaró que las referencias se comparan cuando usas ''==''.
PRIMER CASO:
En el primer código cortado, en realidad estás agregando tres cadenas en el conjunto de cadenas. 1. s1 = "Bueno"
2. s1 = "Goodmorning" (después de concatenar) 3. s2 = "Goodmorining"
Mientras se hace if (s1 == s2), los objetos son iguales pero de referencia, por lo tanto, es falso.
SEGUNDO CASO:
En este caso, está usando s1.intern (), lo que implica que si la agrupación ya contiene una cadena igual a este objeto String según lo determinado por el método equals (Object), se devuelve la cadena de la agrupación. De lo contrario, este objeto String se agrega a la agrupación y se devuelve una referencia a este objeto String.
- s1 = "Bueno"
- s1 = "Goodmorning" (después de concatenar)
- Para String s2 = "Goodmorning", la nueva String no se agrega a la agrupación y obtiene una referencia de una existente para s2. Por lo tanto, si (s1 == s2) devuelve true.
Parece que JDK7 procesa a los internos de una manera diferente como antes.
Lo probé con la compilación 1.7.0-b147 y obtuve "ambos son iguales", pero al ejecutarlo (el mismo bytecode) con 1,6.0_24 no recibo el mensaje.
También depende de dónde se encuentre la línea String b2 =...
en el código fuente. El siguiente código tampoco da salida al mensaje:
class Test {
public static void main(String... args) {
String s1 = "Good";
s1 = s1 + "morning";
String s2 = "Goodmorning";
System.out.println(s1.intern()); //just changed here s1.intern() and the if condition runs true
if(s1 == s2) {
System.out.println("both are equal");
} //now it works.
}
}
Parece que el intern
después de no encontrar la Cadena en su conjunto de cadenas, inserta la instancia real s1 en el conjunto. La JVM está utilizando ese grupo cuando se crea s2, por lo que obtiene la misma referencia que s1. En el otro lado, si se crea s2 primero, esa referencia se almacena en el conjunto.
Esto puede ser el resultado de mover las cadenas internas de la generación permanente del montón de Java.
Encontrado aquí: RFE importantes abordadas en JDK 7
En JDK 7, las cadenas internas ya no se asignan en la generación permanente del montón de Java, sino que se asignan en la parte principal del montón de Java (conocidas como generaciones jóvenes y viejas), junto con los otros objetos creados por la aplicación . Este cambio dará como resultado que más datos residan en el montón principal de Java y menos datos en la generación permanente, por lo que es posible que sea necesario ajustar los tamaños del montón. La mayoría de las aplicaciones solo verán diferencias relativamente pequeñas en el uso del montón debido a este cambio, pero las aplicaciones más grandes que cargan muchas clases o hacen un uso intensivo del método String.intern () verán diferencias más significativas.
No estoy seguro de si se trata de un error y de qué versión ... El JLS 3.10.5 indica
El resultado de internar explícitamente una cadena computada es la misma cadena que cualquier cadena literal preexistente con el mismo contenido.
así que la pregunta es cómo se interpreta el preexistente, el tiempo de compilación o el tiempo de ejecución: ¿es "Goodmorning" preexistente o no?
Prefiero la forma en que se implementó antes de las 7 ...
Vamos a omitir detalles innecesarios del ejemplo:
class Test {
public static void main(String... args) {
String s1 = "Good";
s1 = s1 + "morning";
System.out.println(s1 == s1.intern()); // Prints true for jdk7, false - for jdk6.
}
}
Consideremos a String#intern
como una caja negra. Sobre la base de unos pocos casos de prueba ejecutados, llego a la conclusión de que la implementación es la siguiente:
Java 6:
si la agrupación contiene un objeto igual a this
, devuelva la referencia a ese objeto, de lo contrario, cree una nueva cadena (igual a this
), colóquela en la agrupación y devuelva la referencia a la instancia creada.
Java 7:
si la agrupación contiene un objeto igual a this
, entonces devuelva la referencia a ese objeto, de lo contrario, ponga this
en la agrupación y devuelva this
Ni Java 6 ni Java 7 rompen el contrato del método .
Parece que el nuevo comportamiento del método interno fue el resultado de la corrección de este error: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6962931 .
==
compara las referencias. El método interno asegura que las cadenas con el mismo valor tengan la misma referencia.
El javadoc para el método String.intern explica:
pasante público de cuerda ()
Devuelve una representación canónica para el objeto cadena.
Un grupo de cadenas, inicialmente vacío, se mantiene en privado por la clase Cadena.
Cuando se invoca el método interno, si la agrupación ya contiene una cadena igual a este objeto de Cadena según lo determina el método igual (Objeto), se devuelve la cadena de la agrupación. De lo contrario, este objeto String se agrega a la agrupación y se devuelve una referencia a este objeto String.
De ello se deduce que para cualquiera de las dos cadenas s y t, s.intern () == t.intern () es verdadera si y solo si s.equals (t) es verdadera.
Todas las cadenas literales y las expresiones constantes con valores de cadena están internadas. Los literales de cadena se definen en §3.10.5 de la Especificación del lenguaje Java
Devuelve: una cadena que tiene el mismo contenido que esta cadena, pero que se garantiza que sea de un conjunto de cadenas únicas.
Así que sin internarse, el compilador mira las constantes en el código java y construye su grupo constante a partir de eso. La clase String mantiene un grupo diferente, y interning verifica la cadena que se pasa contra el grupo y se asegura de que la referencia sea única (de modo que == funcione).