Object.toString()如何获取“内存地址”以及如何模仿它

时间:2016-04-02 17:10:55

标签: java memory tostring

toString的{​​{1}}方法是唯一的,因为它似乎是Java中唯一可以查看内存地址的地方。 Object如何做到这一点?

我想知道,以便我可以在我自己的课程中模仿它的实现。我无法使用Object因为我正在扩展一个已经覆盖super.toString()的类。

更新:我的问题的前提是询问内存地址,但答案表明这个前提是不正确的,所以我实际上问的是: toString如何返回它的作用,我怎么能模仿呢?

5 个答案:

答案 0 :(得分:13)

它不是内存地址,而是hashCode()。另请参阅Object.toString(),其中(部分)

  

toString的{​​{1}}方法返回一个字符串,该字符串由对象为实例的类的名称,符号字符Object和无符号十六进制组成表示对象的哈希码。换句话说,此方法返回一个等于值的字符串:

@

Object.hashCode()(这通常是通过将对象的内部地址转换为整数来实现的,但Java™编程语言不需要这种实现技术。) so它不是必需作为内存地址,如果使用它只对getClass().getName() + '@' + Integer.toHexString(hashCode()) 的内部(本机)实现可见。

答案 1 :(得分:10)

  

Object的toString方法是唯一的,因为它似乎是Java中唯一可以查看内存地址的地方。 Object如何做到这一点?

它没有获取地址,在HotSpot JVM中,它获得一个随机生成的31位哈希码,存储在对象的标题中。这必须存储因为;

  • 即使移动了对象并且有一个新地址,哈希码也不会改变。
  • 地址不够随意。地址的低8位始终为0.在每个GC之后,要创建的第一个对象始终是相同的。
  • 地址可以是64位。

请在家里尝试,不适合工作!!。

您可以使用Unsafe

获取/设置hashCode()
static final Unsafe UNSAFE;

static {
    try {
        Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
        theUnsafe.setAccessible(true);
        UNSAFE = (Unsafe) theUnsafe.get(null);
    } catch (Exception e) {
        throw new AssertionError(e);
    }
}

public static void setIdentityHashCode(Object o, int code) {
    UNSAFE.putInt(o, 1l, code & 0x7FFF_FFF);
}

public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
    Double d = 1.0;
    Double d2 = 1.0;
    setIdentityHashCode(d, 1);
    setIdentityHashCode(d2, 1);
    System.out.println("d: "+d+" System.identityHashCode(d): "+System.identityHashCode(d));
    System.out.println("d2: "+d2+" System.identityHashCode(d2): "+System.identityHashCode(d2));
    System.out.println("d == d2: " + (d == d2));
}

打印

d: 1.0 System.identityHashCode(d): 1
d2: 1.0 System.identityHashCode(d2): 1
d == d2: false

如果您知道内存的翻译方式,您可以从参考值中获取地址。在最简单的情况下,(您有64位引用),引用是未翻译的,地址是存储在引用中的值。

如果在具有-XX:-UseCompressedOops

的64位JVM上运行此操作
// This only works if a GC doesn't move the object while attempting to access it.
static final Unsafe UNSAFE;

static {
    try {
        Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
        theUnsafe.setAccessible(true);
        UNSAFE = (Unsafe) theUnsafe.get(null);
    } catch (Exception e) {
        throw new AssertionError(e);
    }
}

// run with: -ea -XX:-UseCompressedOops
public static void main(String[] args) {
    Object i = 0x12345678;
    System.out.printf("indentityHashCode = %08x%n", System.identityHashCode(i));

    Object[] obj = { i };
    assert Unsafe.ARRAY_OBJECT_INDEX_SCALE == 8; // 8 bytes per reference.
    long address = UNSAFE.getLong(obj, (long) Unsafe.ARRAY_OBJECT_BASE_OFFSET);
    System.out.printf("%x%n", address);
    for (int j=0;j<24;j++)
        System.out.printf("%02x ", UNSAFE.getByte(address + j) & 0xFF);
    System.out.println();
    // now some really scary sh!t
    UNSAFE.putLong(i, 8L, UNSAFE.getLong(0L, 8L));
    System.out.printf("`i` is now a %s and is %x%n", i.getClass(), i);
}

打印

indentityHashCode = 5a07e868
7fbf41cb8560
01 68 e8 07 5a 00 00 00 48 33 3f b9 b9 7f 00 00 78 56 34 12 00 00 00 00 
   ^^hashCode^          ^class address  ^       ^int value^
`i` is now a class java.lang.Long and is 12345678

答案 2 :(得分:9)

没有。它得到的是哈希码,而不是内存地址,哈希码与内存地址没有必要的连接。 (它可能在一些实现中。)

在基本实现中,它是System.identityHashCode,尽管它仍与内存地址没有关系。

答案 3 :(得分:0)

如果您在toString()班级中找到此Object的定义。你会发现这个。

getClass().getName() + '@' + Integer.toHexString(hashCode()) //returns classname@hashCode.

它得到的是hash-code,而不是内存地址(根据Louis Wasserman),并且哈希码与内存地址没有必要的连接。

根据您的需要,您应该为项目中声明的每个类覆盖此toString()

答案 4 :(得分:0)

这不是获取内存地址的漏洞,因为您只需在对象的标头中随机生成31位哈希码。它没有提供任何实际的方式来获取真实的内存地址。