toString
的{{1}}方法是唯一的,因为它似乎是Java中唯一可以查看内存地址的地方。 Object
如何做到这一点?
我想知道,以便我可以在我自己的课程中模仿它的实现。我无法使用Object
因为我正在扩展一个已经覆盖super.toString()
的类。
更新:我的问题的前提是询问内存地址,但答案表明这个前提是不正确的,所以我实际上问的是: toString
如何返回它的作用,我怎么能模仿呢?
答案 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位哈希码,存储在对象的标题中。这必须存储因为;
请在家里尝试,不适合工作!!。
您可以使用Unsafe
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
// 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位哈希码。它没有提供任何实际的方式来获取真实的内存地址。