¿Cómo se obtiene la "referencia de objeto" de un objeto en java cuando se han anulado toString () y hashCode ()?

106

Me gustaría imprimir la "referencia de objeto" de un objeto en Java con fines de depuración. Es decir, para asegurarse de que el objeto sea el mismo (o diferente) según la situación.

El problema es que la clase en cuestión hereda de otra clase, que ha anulado tanto toString () como hashCode () que normalmente me daría la identificación.

Situación de ejemplo: Ejecutar una aplicación de subprocesos múltiples, donde yo (durante el desarrollo) quiero verificar si todos los subprocesos usan la misma instancia de un objeto de recurso o no.

Nicolai
fuente
1
dependiendo de si puedes hacerlo ... == es el camino a seguir ... pero no tengo idea de cómo está estructurado el código en cuestión. Nuevamente, hashCode probablemente esté bien para lo que está haciendo, pero podría romperse dependiendo de cómo se implemente la biblioteca.
TofuBeer
Realmente es una buena pregunta.
Zhang Xiang

Respuestas:

108

¿Qué planeas hacer exactamente con él (lo que quieres hacer marca la diferencia con lo que necesitarás llamar)?

hashCode, como se define en los JavaDocs, dice:

Por mucho que sea razonablemente práctico, el método hashCode definido por la clase Object devuelve enteros distintos para objetos distintos. (Esto generalmente se implementa convirtiendo la dirección interna del objeto en un número entero, pero el lenguaje de programación Java ™ no requiere esta técnica de implementación).

Entonces, si está utilizando hashCode()para averiguar si es un objeto único en la memoria, esa no es una buena manera de hacerlo.

System.identityHashCode hace lo siguiente:

Devuelve el mismo código hash para el objeto dado que devolvería el método predeterminado hashCode (), ya sea que la clase del objeto dado anule o no hashCode (). El código hash de la referencia nula es cero.

Lo cual, por lo que está haciendo, suena como lo que quiere ... pero lo que quiere hacer podría no ser seguro dependiendo de cómo se implemente la biblioteca.

Tofu cerveza
fuente
6
No estoy actuando sobre el valor del código. Como pr. edición de mi pregunta, solo la uso con fines de depuración de una situación determinada. Es por eso que creo que mi respuesta es razonable, pero le doy un +1 por una respuesta perspicaz.
Nicolai
1
lo más probable es que siempre haga lo que desee, pero podría romperse en algunas máquinas virtuales.
TofuBeer
Se romperá (es decir, identityHashCode no será necesariamente único) en cualquier VM razonable. identityHashCode no es una identificación
Tom Hawtin - tackline
Como se ha mencionado, no hay garantía de que el código hash se base en la dirección. He visto varios objetos con el mismo ID en la VM de IBM dentro de WAS.
Robin
"Esto normalmente se implementa convirtiendo la dirección interna del objeto en un número entero", no una garantía, sino la implementación predeterminada de Sun. Cosas como s = "Hola" y t = "Hola" probablemente darían como resultado que syt tuvieran el mismo identityHashCode ya que en realidad son el mismo objeto.
TofuBeer
50

Así es como lo resolví:

Integer.toHexString(System.identityHashCode(object));
Nicolai
fuente
5
En realidad, esto no es correcto, ya que varios objetos pueden devolver el mismo identityHashCode.
Robin
2
¿No es cierto que dos objetos (referencias) con el mismo hash de identidad son el mismo objeto? eso es lo que quiere OP
basszero
3
No, no es cierto. Es muy probable, pero no está garantizado, ya que la especificación NO define el algoritmo.
Robin
8

Los dobles iguales ==siempre comprobarán en función de la identidad del objeto, independientemente de la implementación de los objetos de hashCode o iguales. Por supuesto, asegúrese de que las referencias de objeto que está comparando estén volatile(en una JVM 1.5+).

Si realmente debe tener el resultado original de Object toString (aunque no es la mejor solución para su caso de uso de ejemplo), la biblioteca Commons Lang tiene un método ObjectUtils.identityToString (Object) que hará lo que desee. Desde JavaDoc:

public static java.lang.String identityToString(java.lang.Object object)

Obtiene el toString que produciría Object si una clase no anulara el propio toString. nulo devolverá nulo.

 ObjectUtils.identityToString(null)         = null
 ObjectUtils.identityToString("")           = "java.lang.String@1e23"
 ObjectUtils.identityToString(Boolean.TRUE) = "java.lang.Boolean@7fa"
noahlz
fuente
1
Si está usando Java 7, entonces debería considerar usar java.util.Objects
noahlz
5

No puede hacer lo que quiere con seguridad ya que el hashCode () predeterminado puede no devolver la dirección, y se ha mencionado que son posibles varios objetos con el mismo hashCode. La única forma de lograr lo que desea es anular el método hashCode () para los objetos en cuestión y garantizar que todos proporcionen valores únicos. Si esto es factible en su situación es otra cuestión.

Para que conste, he experimentado varios objetos con el mismo código hash predeterminado en una máquina virtual IBM que se ejecuta en un servidor WAS. Tuvimos un defecto en el que los objetos que se colocaban en una caché remota se sobrescribían debido a esto. Eso fue una revelación para mí en ese momento, ya que asumí que el código hash predeterminado también era la dirección de memoria de los objetos.

Robin
fuente
2

Agregue una identificación única a todas sus instancias, es decir

public interface Idable {
  int id();
}

public class IdGenerator {
  private static int id = 0;
  public static synchronized int generate() { return id++; }
}

public abstract class AbstractSomething implements Idable {
  private int id;
  public AbstractSomething () {
    this.id = IdGenerator.generate();
  }
  public int id() { return id; }
}

Amplíe desde AbstractSomething y consulte esta propiedad. Estará seguro dentro de una sola máquina virtual (suponiendo que no se juegue con cargadores de clases para sortear las estáticas).

Michael Myers
fuente
Probablemente usaría AtomicInteger en este escenario: tiene un mayor rendimiento porque no se requiere sincronización y usa operaciones de memoria atómica nativa proporcionadas porsun.misc.Unsafe
RAnders00
1

simplemente podemos copiar el código de una cadena de clase de objeto para obtener la referencia de cadena

class Test
{
  public static void main(String args[])
  {
    String a="nikhil";     // it stores in String constant pool
    String s=new String("nikhil");    //with new stores in heap
    System.out.println(Integer.toHexString(System.identityHashCode(a)));
    System.out.println(Integer.toHexString(System.identityHashCode(s)));
  }
}
Nikhil Jaitak
fuente