如何获取到Java对象的地址

最近在整理final相关的测试用例,所以对final进行了重新思考,final 可以修饰类、方法、实例、参数!
final修饰实例时,表示该实例不可以被修改,但是具体是指实例的内存地址不可以被修改? 还是实例里的值不可以被修改?还是实例的内存地址和值都不可以被修改?
其实是蛮简单的一个问题,当final修饰到对象实例时, 表示什么意义,你有真正思考过吗?

所以我写了一个测试类:

import com.alibaba.fastjson.JSONObject;

/**
 * final 修饰对象 
 * 
 * @author zhoufy
 * @date 2019年1月24日 下午6:35:26
 */
public class FinalTest {
    
    public static void main(String[] args){
        FinalTest t = new FinalTest();
        t.test();
    }
    private void test(){
        final Inner inner = new Inner();
        print(inner);
        inner.setId(1);
        inner.setName("xiao ming");
        print(inner);
        inner.setId(2);
        inner.setName("xiao hong");
        print(inner);
    }
    
    private void print(Inner inner){
        System.out.println(inner.hashCode()+" : "+System.identityHashCode(inner)+" : "+JSONObject.toJSONString(inner));
    }
    
    class Inner{
        private int id;
        private String name;
        public int getId() {
            return id;
        }
        public void setId(int id) {
            this.id = id;
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
    }
}


输出:

366712642 : 366712642 : {“id”:0}
366712642 : 366712642 : {“id”:1,“name”:“xiao ming”}
366712642 : 366712642 : {“id”:2,“name”:“xiao hong”}

走到这里就已经证明了一点:final修饰的对象里的值是可以改变的,那地址呢? 而且还引起了我思考的一个问题haseCode()方法跟System.identityHashCode() 能代表对象的内存地址吗?

先看一下haseCode()的源码:

/**
 * Returns a hash code value for the object. This method is
 * supported for the benefit of hash tables such as those provided by
 * {@link java.util.HashMap}.
 * 

 * The general contract of {@code hashCode} is:  *

     *
  • Whenever it is invoked on the same object more than once during  *     an execution of a Java application, the {@code hashCode} method  *     must consistently return the same integer, provided no information  *     used in {@code equals} comparisons on the object is modified.  *     This integer need not remain consistent from one execution of an  *     application to another execution of the same application.  *
  • If two objects are equal according to the {@code equals(Object)}  *     method, then calling the {@code hashCode} method on each of  *     the two objects must produce the same integer result.  *
  • It is not required that if two objects are unequal  *     according to the {@link java.lang.Object#equals(java.lang.Object)}  *     method, then calling the {@code hashCode} method on each of the  *     two objects must produce distinct integer results.  However, the  *     programmer should be aware that producing distinct integer results  *     for unequal objects may improve the performance of hash tables.  *
 *

 * As much as is reasonably practical, the hashCode method defined by  * class {@code Object} does return distinct integers for distinct  * objects. (This is typically implemented by converting the internal  * address of the object into an integer, but this implementation  * technique is not required by the  * Java™ programming language.)  *  * @return  a hash code value for this object.  * @see     java.lang.Object#equals(java.lang.Object)  * @see     java.lang.System#identityHashCode   */   public native int hashCode();


这其中有几句话要细细斟酌:

  • Returns a hash code value for the object. 返回对象的hash值
  • If two objects are equal according to the equals(Object) method, then calling the hashCode() method on each of the two objects must produce the same value. 如果两个对象相等(根据equals()方法判断),则hashCode()方法返回的值相同
  • It is not required that if two objects are unequal according to the equals(java.lang.Object) method, then calling the hashCode method on each of the two objects must produce distinct integer results. However, developers should be aware that producing distinct integer results for unequal objects improves the performance of hash tables. 当equals()方法返回的不相等,则没有要求hashCode()方法必须返回不同的int值,但是开发人员应该为不相等的对象生成不同的整数结果,这样可以提高hash表的性能


当看到这已经知道了hashCode()方法返回值不能代表对象的内存地址!!!

顺便再看一下identityHashCode()方法的源码:

   /**
    * Returns the same hash code for the given object as
    * would be returned by the default method hashCode(),
    * whether or not the given object's class overrides
    * hashCode().
    * The hash code for the null reference is zero.
    *
    * @param x object for which the hashCode is to be calculated
    * @return  the hashCode
    * @since   JDK1.1
    */
   public static native int identityHashCode(Object x);


从方法注释已经看出来,该方法默认返回的跟hashCode()方法同样的值

如果查看jdk的源码:
java.lang.System.java类对应的本地c语言的实现——System.c,在 openjdk-8u\jdk\src\share\native\java\lang 目录下

JNIEXPORT jint JNICALL
Java_java_lang_System_identityHashCode(JNIEnv *env, jobject this, jobject x)
{
    return JVM_IHashCode(env, x);
}


java.lang.Object.java类对应的本地c语言的实现——Object.c,在 openjdk-8u\jdk\src\share\native\java\lang 目录下

static JNINativeMethod methods[] = {
    {"hashCode",    "()I",                    (void *)&JVM_IHashCode},
    {"wait",        "(J)V",                   (void *)&JVM_MonitorWait},
    {"notify",      "()V",                    (void *)&JVM_MonitorNotify},
    {"notifyAll",   "()V",                    (void *)&JVM_MonitorNotifyAll},
    {"clone",       "()Ljava/lang/Object;",   (void *)&JVM_Clone},
};


看jdk源说明hashCode()和identityHashCode() 调用的是同一个方法:JVM_IHashCode()
所以两个方法返回的值相等就对了!

那如何获取到Java对象的地址呢? 几经查询资料,原来还是有办法的,要通过sun.misc.Unsafe类来实现:

package com.zhoufy.sun.unsafe.e3;

public class A {
    private long a;        // not initialized value
    
    public A() {
        this.a = 1;        // initialization
    }
    
    public long a() { return this.a; }
}
package com.zhoufy.sun.unsafe.e3;

import java.lang.reflect.Field;
import sun.misc.Unsafe;

/**
 * @author zhoufy
 * @date 2019年2月2日 上午10:29:15
 */
@SuppressWarnings("restriction")        //取消显示的警告集
public class ObjectsAddressDemo {

    static final Unsafe unsafe = getUnsafe();
    static final boolean is64bit = true; // auto detect if possible.

    public static void main(String... args) {
        A a = new A();
        
        //GC前
        System.out.println("-----------GC前------------");
        print(a);
        
        System.gc();
        
        //GC后
        System.out.println("-----------GC后------------");
        print(a);
    }

    private static void print(A a) {
        // hashcode
        System.out.println("Hashcode :       " + a.hashCode());
        System.out.println("Hashcode :       " + System.identityHashCode(a));
        System.out.println("Hashcode (HEX) : " + Integer.toHexString(a.hashCode()));// Integer.toHexString(int)是将一个整型转成一个十六进制数

        // toString
        System.out.println("toString :       " + String.valueOf(a));

        //通过sun.misc.Unsafe;
        printAddresses("Address", a);
    }

    @SuppressWarnings("deprecation")
    public static void printAddresses(String label, Object... objects) {
        System.out.print(label + ":         0x");
        long last = 0;
        int offset = unsafe.arrayBaseOffset(objects.getClass());
        int scale = unsafe.arrayIndexScale(objects.getClass());
        switch (scale) {
        case 4:
            long factor = is64bit ? 8 : 1;
            final long i1 = (unsafe.getInt(objects, offset) & 0xFFFFFFFFL) * factor;
            System.out.print(Long.toHexString(i1));
            last = i1;
            for (int i = 1; i < objects.length; i++) {
                final long i2 = (unsafe.getInt(objects, offset + i * 4) & 0xFFFFFFFFL) * factor;
                if (i2 > last)
                    System.out.print(", +" + Long.toHexString(i2 - last));
                else
                    System.out.print(", -" + Long.toHexString(last - i2));
                last = i2;
            }
            break;
        case 8:
            throw new AssertionError("Not supported");
        }
        System.out.println();
    }

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


输出:

 -----------GC前------------
Hashcode :       1829164700
Hashcode :       1829164700
Hashcode (HEX) : 6d06d69c
toString :       com.zhoufy.sun.unsafe.e3.A@6d06d69c
Address:         0x76b4635b8
-----------GC后------------
Hashcode :       1829164700
Hashcode :       1829164700
Hashcode (HEX) : 6d06d69c
toString :       com.zhoufy.sun.unsafe.e3.A@6d06d69c
Address:         0x6c1a0a338


当然也可以通过jol工具包里的工具类实现获取对象的内存信息,使用jol工具包需要在maven里引入:


    org.openjdk.jol
    jol-core
    0.9


Java例子:

package com.zhoufy.sun.unsafe.e3;

import java.lang.reflect.Field;

import org.openjdk.jol.info.GraphLayout;
import org.openjdk.jol.vm.VM;

import sun.misc.Unsafe;

/** 
 * @author zhoufy
 * @date 2019年2月2日 上午10:29:15
 */
@SuppressWarnings("restriction")        //取消显示的警告集
public class ObjectsAddressDemo {

    static final Unsafe unsafe = getUnsafe();
    static final boolean is64bit = true; // auto detect if possible.

    public static void main(String... args) {
        A a = new A();
        
        //GC前
        System.out.println("-----------GC前------------");
        print(a);
        
        System.gc();
        
        //GC后
        System.out.println("-----------GC后------------");
        print(a);
    }

    private static void print(A a) {
        //通过sun.misc.Unsafe;
        printAddresses("Address", a);
        
        //通过jol工具包打印对象的地址
        System.out.println(GraphLayout.parseInstance(a).toPrintable());
        System.out.println("Current address: " + VM.current().addressOf(a));
    }

    @SuppressWarnings("deprecation")
    public static void printAddresses(String label, Object... objects) {
        System.out.print(label + ":         0x");
        long last = 0;
        int offset = unsafe.arrayBaseOffset(objects.getClass());
        int scale = unsafe.arrayIndexScale(objects.getClass());
        switch (scale) {
        case 4:
            long factor = is64bit ? 8 : 1;
            final long i1 = (unsafe.getInt(objects, offset) & 0xFFFFFFFFL) * factor;
            System.out.print(Long.toHexString(i1));
            last = i1;
            for (int i = 1; i < objects.length; i++) {
                final long i2 = (unsafe.getInt(objects, offset + i * 4) & 0xFFFFFFFFL) * factor;
                if (i2 > last)
                    System.out.print(", +" + Long.toHexString(i2 - last));
                else
                    System.out.print(", -" + Long.toHexString(last - i2));
                last = i2;
            }
            break;
        case 8:
            throw new AssertionError("Not supported");
        }
        System.out.println();
    }

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


输出:

-----------GC前------------
Address:         0x76b463440
com.zhoufy.sun.unsafe.e3.A@119d7047d object externals:
          ADDRESS       SIZE TYPE                       PATH                           VALUE
        76b463440         24 com.zhoufy.sun.unsafe.e3.A                                (object)


Current address: 31864534080
-----------GC后------------
Address:         0x6c1a0aab0
com.zhoufy.sun.unsafe.e3.A@119d7047d object externals:
          ADDRESS       SIZE TYPE                       PATH                           VALUE
        6c1a0aab0         24 com.zhoufy.sun.unsafe.e3.A                                (object)


Current address: 29018335920

从输出信息来看,printAddresses()方法和jol工具类打印的对象的内存地址是一致的,当然如果去深入研究jol工具类,它底层也是通过sun.misc.Unsafe实现的!

原文链接:https://blog.csdn.net/zhoufanyang_china/article/details/86750351

你可能感兴趣的:(java,开发语言)