Java中如何打印对象内存地址?
Posted 程序员Forlan
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java中如何打印对象内存地址?相关的知识,希望对你有一定的参考价值。
先看一个简单的程序,一般我们打印对象,大部分是下面的情况,可能会重写下toString()方法,这个另说
Frolan frolan = new Frolan();
System.out.println(frolan);
// 输出结果
com.test.admin.entity.Frolan@2b80d80f
这个结果其实是调用了Object.toString打印出来的,就是类路径名+@+hashCode的16进制数
public String toString()
return getClass().getName() + "@" + Integer.toHexString(hashCode());
这里的hashCode()是一个native方法,就是我们要的地址值?
我们通过源码来分析一波
static inline intptr_t get_next_hash(Thread * Self, oop obj)
intptr_t value = 0 ;
if (hashCode == 0)
// This form uses an unguarded global Park-Miller RNG,
// so it's possible for two threads to race and generate the same RNG.
// On MP system we'll have lots of RW access to a global, so the
// mechanism induces lots of coherency traffic.
value = os::random() ;
else
if (hashCode == 1)
// This variation has the property of being stable (idempotent)
// between STW operations. This can be useful in some of the 1-0
// synchronization schemes.
intptr_t addrBits = cast_from_oop<intptr_t>(obj) >> 3 ;
value = addrBits ^ (addrBits >> 5) ^ GVars.stwRandom ;
else
if (hashCode == 2)
value = 1 ; // for sensitivity testing
else
if (hashCode == 3)
value = ++GVars.hcSequence ;
else
if (hashCode == 4)
value = cast_from_oop<intptr_t>(obj) ;
else
// Marsaglia's xor-shift scheme with thread-specific state
// This is probably the best overall implementation -- we'll
// likely make this the default in future releases.
unsigned t = Self->_hashStateX ;
t ^= (t << 11) ;
Self->_hashStateX = Self->_hashStateY ;
Self->_hashStateY = Self->_hashStateZ ;
Self->_hashStateZ = Self->_hashStateW ;
unsigned v = Self->_hashStateW ;
v = (v ^ (v >> 19)) ^ (t ^ (t >> 8)) ;
Self->_hashStateW = v ;
value = v ;
value &= markOopDesc::hash_mask;
if (value == 0) value = 0xBAD ;
assert (value != markOopDesc::no_hash, "invariant") ;
TEVENT (hashCode: GENERATE) ;
return value;
大概意思就是,会根据不同的hashCode返回不同的结果
hashCode=0,返回随机数
hashCode=1,将oop的地址做位运算、异或运算得到的结果
hashCode=2,固定值1
hashCode=3,返回递增序列当前值
hashCode=4,oop的地址
hashCode=其他值,简单理解为移位寄存器,线程安全
我们设置JVM启动参数来模拟一下
// -XX:hashCode=2
for (int i = 0; i < 3; i++)
Frolan frolan = new Frolan();
System.out.println(frolan.hashCode());
// 输出结果
1
1
1
// -XX:hashCode=3
for (int i = 0; i < 3; i++)
Frolan frolan = new Frolan();
System.out.println(frolan.hashCode());
// 输出结果
714
715
716
我们模拟了hashCode=2和3的情况,其它情况没那么好验证,感兴趣的话,后续大家一起交流下~
那么我们不设置启动参数,默认值是什么?
java -XX:+PrintFlagsFinal -version | grep hashCode
intx hashCode = 5 product
java version "1.8.0_101"
Java(TM) SE Runtime Environment (build 1.8.0_101-b13)
Java HotSpot(TM) 64-Bit Server VM (build 25.101-b13, mixed mode)
可以看到默认值是5,所以我们不设置的情况下,打印的并不是对象的内存地址
网上很多说,hashCode=4,这里就是对象的内存地址,这个是错的,这里拿到的只是oop的地址,System.identityHashCode()方法拿到的也是这个地址
如果想要获取真正的对象地址,可以使用Java 对象布局 ( JOL ) 工具
引入依赖
<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol-core</artifactId>
<version>0.9</version>
</dependency>
Frolan frolan = new Frolan();
System.out.println(VM.current().addressOf(frolan));
// 输出结果
31867940040
Java Object类
Object的toString方法
-
toString 方法返回的是字符串,直接打印对象的名字,就是调用对象的toString,也就是打印对象堆内存中的地址值
- 重写toString方法
/** * @Version: 1.8.0_201 Java SE 8 * @Description: toString 方法返回的是字符串,直接打印对象的名字,就是调用对象的toString * 也就是打印对象堆内存中的地址值 */ public class Student { private String name; private int age; public Student(String name, int age) { this.name = name; this.age = age; } public void methodStudentMassage() { System.out.println( "Student{" + "name=‘" + name + ‘‘‘ + ", age=" + age + ‘}‘ ); } /** * 重写toString方法 * @return 学生的信息 */ @Override public String toString() { return "Student{" + "name=‘" + name + ‘‘‘ + ", age=" + age + ‘}‘; } }
- 测试重写的toString方法
/** * @Version: 1.8.0_201 Java SE 8 */ public class DemoStudentToString { public static void main(String[] args) { Student student = new Student("Lee Hua", 21); // 不重写toString方法 student.methodStudentMassage(); // 重写了toString方法 System.out.println( student.toString() ); } }
输出:
Student{name=‘Lee Hua‘, age=21} Student{name=‘Lee Hua‘, age=21}
Object类的equals方法 -
quals方法:其他某个对象是否与此对象相等
-
调用成员方法quals并指定参数为另一个对象,则可判断这两个对象是否相同
public boolean equals(Object obj) { return (this == obj); }
this 表示:哪个对象调用了equals方法,那么this就是那个对象 -
创建一个Person类,用于测试
public class Person { private String name; private int age; public Person(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
- 测试
public class DemoPersonEquals { public static void main(String[] args) { Person person1 = new Person("一号", 20); Person person2 = new Person("二号", 21); // 对象person1 和 对象person2 进行比较 boolean p = person1.equals(person2); System.out.println(person1); System.out.println(person2); System.out.println(p); } }
- 输出:
Person@61bbe9ba Person@610455d6 false /** * Person类如果重写类Object的toString方法,则返回字符串,而不是地址值 * 举例: */ @Override public String toString() { return "Person{" + "name=‘" + name + ‘‘‘ + ", age=" + age + ‘}‘; }
- 在Person类里边覆盖重写equals方法
public class Person { /** * 多态,无法使用子类特有内容,所以可进行覆盖重写 * 覆盖重写equals方法,提高程序的效率 */ @Override public boolean equals(Object obj) { // 如果传递的参数obj是this本身,直接返回true if (obj == this) { return true; } // 如果传递参数是null,直接返回false if (obj == null) { return false; } // 防止类型转换报:ClassCastException if (obj instanceof Person) { // 向下转型,将 obj 转换为 Person 类型 Person person = (Person)obj; return this.name.equals(person.name) && this.age == person.age; } // 不是Person类,也不是null,也直接返回false else { return false; } } }
- 这里也可以用 Generate 直接生成与上等功能代码
public class Person { @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } Person person = (Person) o; return age == person.age && name.equals(person.name); } }
使用equals方法时,防止空指针异常
- 例子
import java.util.Objects; public class DemoObjectEquals { public static void main(String[] args) { String s1 = "abc"; String s2 = null; // 不会出现空指针异常 // 输出 false System.out.println( s1.equals(s2) ); // 会出现空指针异常NullPointerException // 报错 Exception in thread "main" java.lang.NullPointerException System.out.println( s2.equals(s1) ); // 空指针异常,可以使用java.util.Objects的equals方法,防止空指针异常 // 输出 false System.out.println( Objects.equals(s1, s2) ); } }
以上是关于Java中如何打印对象内存地址?的主要内容,如果未能解决你的问题,请参考以下文章