Wie erhält man die "Objektreferenz" eines Objekts in Java, wenn toString () und hashCode () überschrieben wurden?


106

Ich möchte die "Objektreferenz" eines Objekts in Java zu Debugging-Zwecken drucken. Das heißt, um sicherzustellen, dass das Objekt je nach Situation gleich (oder unterschiedlich) ist.

Das Problem ist, dass die betreffende Klasse von einer anderen Klasse erbt, die sowohl toString () als auch hashCode () überschrieben hat, was mir normalerweise die ID geben würde.

Beispielsituation: Ausführen einer Multithread-Anwendung, bei der ich (während der Entwicklung) überprüfen möchte, ob alle Threads dieselbe Instanz eines Ressourcenobjekts verwenden oder nicht.


1
je nachdem, ob du es überhaupt kannst ... == ist der richtige Weg ... aber ich habe keine Ahnung, wie der fragliche Code aufgebaut ist. Auch hier ist hashCode wahrscheinlich in Ordnung für das, was Sie tun, aber es kann je nach Implementierung der Bibliothek kaputt gehen.
TofuBeer

Das ist wirklich eine gute Frage.
Zhang Xiang

Antworten:


108

Was genau planen Sie damit zu tun (was Sie tun möchten, macht einen Unterschied zu dem, was Sie anrufen müssen).

hashCode, wie in den JavaDocs definiert, sagt:

Soweit dies vernünftigerweise praktikabel ist, gibt die von class Object definierte hashCode-Methode unterschiedliche Ganzzahlen für unterschiedliche Objekte zurück. (Dies wird normalerweise implementiert, indem die interne Adresse des Objekts in eine Ganzzahl konvertiert wird. Diese Implementierungstechnik wird jedoch von der Programmiersprache Java ™ nicht benötigt.)

Wenn Sie also hashCode()herausfinden möchten, ob es sich um ein eindeutiges Objekt im Speicher handelt, ist dies kein guter Weg, dies zu tun.

System.identityHashCode macht folgendes:

Gibt den gleichen Hashcode für das angegebene Objekt zurück, der von der Standardmethode hashCode () zurückgegeben würde, unabhängig davon, ob die Klasse des angegebenen Objekts hashCode () überschreibt oder nicht. Der Hash-Code für die Nullreferenz ist Null.

Was für das, was Sie tun, nach dem klingt, was Sie wollen ... aber was Sie tun möchten, ist möglicherweise nicht sicher, je nachdem, wie die Bibliothek implementiert ist.


6
Ich handle nicht nach dem Wert im Code. Als pr. Meine Frage bearbeiten, ich benutze es nur zum Debuggen einer bestimmten Situation. Deshalb halte ich meine Antwort für vernünftig, aber ich gebe Ihnen eine +1 für eine aufschlussreiche Antwort.
Nicolai

1
Die Chancen stehen gut, dass es immer das tut, was Sie wollen - aber es könnte auf einigen VMs kaputt gehen.
TofuBeer

Es bricht auf jeder vernünftigen VM (dh IdentityHashCode muss nicht unbedingt eindeutig sein). identityHashCode ist keine ID
Tom Hawtin - Tackline

Wie bereits erwähnt, gibt es keine Garantie dafür, dass der Hashcode auf der Adresse basiert. Ich habe mehrere Objekte mit derselben ID in der IBM VM innerhalb von WAS gesehen.
Robin

"Dies wird normalerweise implementiert, indem die interne Adresse des Objekts in eine Ganzzahl konvertiert wird", keine Garantie, sondern die Standardimplementierung von Sun. Dinge wie s = "Hallo" und t = "Hallo" würden wahrscheinlich dazu führen, dass s und t denselben identitätshashCode haben, da sie wirklich dasselbe Objekt sind.
TofuBeer

50

So habe ich es gelöst:

Integer.toHexString(System.identityHashCode(object));

5
Dies ist eigentlich nicht korrekt, da mehrere Objekte denselben identityHashCode zurückgeben können.
Robin

2
Stimmt es nicht, dass zwei Objekte (Referenzen) mit demselben Identitäts-Hash dasselbe Objekt sind? Das ist es, was OP will
Basszero

3
Nein, das stimmt nicht. Dies ist sehr wahrscheinlich, aber nicht garantiert, da die Spezifikation den Algorithmus NICHT definiert.
Robin

8

Double Equals ==wird immer basierend auf der Objektidentität überprüft, unabhängig von der Implementierung von hashCode oder equals durch die Objekte. Natürlich - stellen Sie sicher, dass die Objektreferenzen, die Sie vergleichen, sindvolatile (in einer JVM von 1,5+) sind.

Wenn Sie wirklich das ursprüngliche Object toString-Ergebnis haben müssen (obwohl dies nicht die beste Lösung für Ihren Beispielanwendungsfall ist), verfügt die Commons Lang-Bibliothek über eine Methode ObjectUtils.identityToString (Object) , die das tut, was Sie wollen. Aus dem JavaDoc:

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

Ruft den toString ab, der von Object erzeugt wird, wenn eine Klasse toString selbst nicht überschreibt. null gibt null zurück.

 ObjectUtils.identityToString(null)         = null
 ObjectUtils.identityToString("")           = "java.lang.String@1e23"
 ObjectUtils.identityToString(Boolean.TRUE) = "java.lang.Boolean@7fa"

1
Wenn Sie Java 7 verwenden, sollten Sie die Verwendung von java.util.Objects in
noahlz

5

Sie können nicht sicher tun, was Sie wollen, da der Standard-HashCode () möglicherweise nicht die Adresse zurückgibt und bereits erwähnt wurde, dass mehrere Objekte mit demselben HashCode möglich sind. Die einzige Möglichkeit, das zu erreichen, was Sie möchten, besteht darin, die hashCode () -Methode für die betreffenden Objekte tatsächlich zu überschreiben und sicherzustellen, dass alle eindeutige Werte bereitstellen. Ob dies in Ihrer Situation machbar ist, ist eine andere Frage.

Für den Datensatz habe ich mehrere Objekte mit demselben Standard-Hashcode in einer IBM VM erlebt, die auf einem WAS-Server ausgeführt wird. Wir hatten einen Fehler, bei dem Objekte, die in einen Remote-Cache gestellt wurden, aus diesem Grund überschrieben wurden. Das war zu diesem Zeitpunkt ein Augenöffner für mich, da ich davon ausging, dass der Standard-Hashcode auch die Speicheradresse des Objekts war.


2

Fügen Sie allen Ihren Instanzen eine eindeutige ID hinzu, z

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

Erweitern Sie von AbstractSomething und fragen Sie diese Eigenschaft ab. Wird in einem einzigen VM sicher sein (vorausgesetzt, es wird kein Spiel mit Klassenladern gespielt, um die Statik zu umgehen).


Ich würde wahrscheinlich AtomicInteger in diesem Szenario verwenden - es hat einen höheren Durchsatz, da keine Synchronisation erforderlich ist, und es verwendet native atomare Speicheroperationen, die vonsun.misc.Unsafe
RAnders00

1

Wir können einfach den Code von der Zeichenfolge der Objektklasse kopieren, um die Referenz der Zeichenfolge zu erhalten

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)));
  }
}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.