Como você obtém a “referência de objeto” de um objeto em java quando toString () e hashCode () foram substituídos?

106

Gostaria de imprimir a "referência de objeto" de um objeto em Java para fins de depuração. Ou seja, certificar-se de que o objeto é o mesmo (ou diferente) dependendo da situação.

O problema é que a classe em questão herda de outra classe, que sobrescreveu toString () e hashCode (), o que normalmente me forneceria o id.

Situação de exemplo: Rodando um aplicativo multi-threaded, onde eu (durante o desenvolvimento) quero verificar se todos os threads usam a mesma instância de um objeto de recurso ou não.

Nicolai
fonte
1
dependendo se você pode fazer isso ... == é o caminho a seguir ... mas eu não tenho ideia de como o código em questão está estruturado. Novamente, o hashCode provavelmente é adequado para o que você está fazendo, mas pode falhar, dependendo de como a biblioteca é implementada.
TofuBeer
É realmente uma boa pergunta.
Zhang Xiang

Respostas:

108

O que exatamente você está planejando fazer com ele (o que você quer fazer faz diferença no que você precisa ligar).

hashCode, conforme definido no JavaDocs, diz:

Por mais que seja razoavelmente prático, o método hashCode definido pela classe Object retorna inteiros distintos para objetos distintos. (Isso geralmente é implementado convertendo o endereço interno do objeto em um número inteiro, mas essa técnica de implementação não é exigida pela linguagem de programação Java ™).

Portanto, se você está usando hashCode()para descobrir se é um objeto único na memória, não é uma boa maneira de fazê-lo.

System.identityHashCode faz o seguinte:

Retorna o mesmo código hash para o objeto fornecido que seria retornado pelo método padrão hashCode (), independentemente de a classe do objeto fornecido substituir hashCode (). O código hash para a referência nula é zero.

O que, para o que você está fazendo, parece o que você quer ... mas o que você deseja fazer pode não ser seguro, dependendo de como a biblioteca é implementada.

TofuBeer
fonte
6
Não estou agindo com base no valor do código. As pr. minha edição de pergunta, eu só uso para fins de depuração de uma determinada situação. É por isso que acho que minha resposta é razoável, mas dou um +1 por uma resposta perspicaz.
Nicolai
1
as chances são de que ele sempre fará o que você deseja - mas pode falhar em algumas VMs.
TofuBeer
Ele vai quebrar (ou seja, identityHashCode não será necessariamente exclusivo) em qualquer VM razoável. IdentityHashCode não é um ID
Tom Hawtin - tackline
Como já foi mencionado, não há garantia de que o hashcode seja baseado no endereço. Eu vi vários objetos com o mesmo ID ocorrerem no IBM VM dentro do WAS.
Robin
"Isso normalmente é implementado convertendo o endereço interno do objeto em um número inteiro" não uma garantia, mas a implementação padrão da Sun. Coisas como s = "Hello" e t = "Hello" provavelmente resultariam em s e t tendo o mesmo identityHashCode, pois eles realmente são o mesmo objeto.
TofuBeer
50

Foi assim que resolvi:

Integer.toHexString(System.identityHashCode(object));
Nicolai
fonte
5
Na verdade, isso não está correto, pois vários objetos podem retornar o mesmo identityHashCode.
Robin
2
Não é verdade que dois objetos (referências) com o mesmo hash de identidade são o mesmo objeto? isso é o que o OP deseja
basszero
3
Não, não é verdade. É muito provável, mas não garantido, pois a especificação NÃO define o algoritmo.
Robin
8

O dobro de igual ==sempre verificará com base na identidade do objeto, independentemente da implementação de hashCode ou igual dos objetos. Claro - certifique-se de que as referências de objeto que você está comparando estejam volatile(em uma JVM 1.5+).

Se você realmente deve ter o resultado de Object toString original (embora não seja a melhor solução para seu caso de uso de exemplo), a biblioteca Commons Lang tem um método ObjectUtils.identityToString (Object) que fará o que você quiser. Do JavaDoc:

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

Obtém o toString que seria produzido por Object se uma classe não substituísse o próprio toString. null retornará null.

 ObjectUtils.identityToString(null)         = null
 ObjectUtils.identityToString("")           = "java.lang.String@1e23"
 ObjectUtils.identityToString(Boolean.TRUE) = "java.lang.Boolean@7fa"
Noahlz
fonte
1
Se você estiver usando Java 7, deve considerar o uso de java.util.Objects
noahlz
5

Você não pode fazer o que deseja com segurança, pois o hashCode () padrão pode não retornar o endereço e, como foi mencionado, vários objetos com o mesmo hashCode são possíveis. A única maneira de realizar o que você deseja é realmente sobrescrever o método hashCode () para os objetos em questão e garantir que todos eles forneçam valores únicos. Se isso é viável em sua situação, é outra questão.

Para registro, experimentei vários objetos com o mesmo hashcode padrão em uma IBM VM em execução em um servidor WAS. Tivemos um defeito em que os objetos colocados em um cache remoto eram sobrescritos por causa disso. Isso foi uma revelação para mim naquele ponto, já que assumi que o hashcode padrão era o endereço de memória do objeto também.

Robin
fonte
2

Adicione um id único a todas as suas instâncias, ou seja,

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

Estenda de AbstractSomething e consulte esta propriedade. Estará seguro dentro de uma única VM (assumindo que nenhum jogo com classloaders para contornar a estática).

Michael Myers
fonte
Eu provavelmente usaria AtomicInteger neste cenário - ele tem uma taxa de transferência mais alta porque a sincronização não é necessária e usa operações de memória atômica nativas fornecidas porsun.misc.Unsafe
RAnders00
1

podemos simplesmente copiar o código de tostring da classe de objeto para obter a referência de string

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
fonte