Operador == y Strings


El operador de igualdad, ==, admite operandos numéricos, booleanos, e igualdad de referencias a objetos.

A tiempo de ejecución, el resultado de == es true si los valores de los operandos son ambos null o ambos se refieren al mismo objeto o mismo arreglo; de otra manera, el resultado es false.

Mientras == puede ser usado para comparar referencias de tipo String, ese test de igualdad determina si los dos operandos se refieren al mismo objeto  String. El resultado es false si los operandos apuntan a distintos objetos Strings, aún si éstos contienen la misma secuencia de caracteres. El contendido de dos strings s  y t pueden ser verificados por igualdad invocando s.equals(t).
Así fue visto en clases y corresponde a la especificación del lenguaje Java.

Un detalle relevante y significativo, no visto en clases, aparece cuando los operandos son instancias de String literales. Una vez creado toda instancia de String es constante en Java (otra opción es crear instancias de StringBuffer los cuales son mutables, se pueden cambiar). Strings de literales corresponden a aquellos compuestos por cero o más caracteres encerrados entre comillas (Ver sección 3.10.5 de la especificación del lenguaje).

La clase String mantiene una colección inicialmente vacía de strings. Cuando definimos un string literal en el programa, a tiempo de compilación éste es agregado a esta colección si la misma secuencia, determinada por equals(object), no existe aún. Luego en el programa se ubica la referencia a tal string. Esta operación es conocida como internar el string. Además cualquier string, no necesariamente puede ser internado invocando su método intern().

Con esto para el siguiente programa se tiene:

    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 en otro archivo (y directorio)

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

produce la salida:

    true true true true false true

Este ejemplo ilustra 6 puntos:

    * Strings literales dentro de la misma clase en el paquete representan referencias al mismo objeto string.
    * Strings literales dentro de diferentes clases en el mismo paquete representan referencias al mismo objeto string.
    * Strings literales dentro de diferentes clases en diferentes paquete también representan referencias al mismo objeto string.
    * Strings obtenidos a partir de expresiones constantes son computados en tiempo de compilación y entonces son tratados como si fueran literales.
    * Strings calculados en tiempo de ejecución son creados independientemente y consecuentemente distintos.
    * El resultado de internar explícitamente un string calculado en tiempo de ejecución genera el mismo string que cualquier pre-existente string literal con el mismo contenido.

Para probar el ejemplo haga:
1.- Cree directorio testPackage.
2.- Cree directorio other.
3.- Cree el archivo Test.java dentro de testPackage y Other.java dentro de other.
4.- Estando en directorio padre de testPackage y other, compile con
   $ javac testPackage/Test.java
5.- Finalmente corra el programa con:
   $ java testPackage.Test
Puede bajar los archivos desde aquí.