We know that in Java you can print strings and things like that, but when you print an object directly, you don’t print a value

But something weird like creating a student object and printing the student object results in the following:

The output is: Student@16d3586. Why is this output?

Looking at the Java API, we can see that when Java directly outputs an object of a class, it automatically calls that class’s toString method

This method is in the Object class, and all classes in Java inherit from object, so all classes have toString methods.

Through the source code we found that the implementation of this method is like this:

getClass().getName()+The '@'+Integer.toHexString(hashCode())
Copy the code

The output is:

Class name + @ + hash code value

We try to get a hash of an object and print the following

So what is the hashcode that hashcode() gets?

The hash code represents a value of type int assigned to this Object by the JVM virtual machine

The JVM uses the object’s Hashcode value to improve the efficiency of using HashMap and Hashtable hash access objects

How do you make it,? Then we enter the source code to specific analysis:

Download the source code at the following address:

Its seven 1:download.java.net/openjdk/jdk download address… .

After obtaining the source code, go to openJDK \ JDK \ SRC \share\classes\ Java \lang

You can see the source code for Object.java, open it, and look at the definition of **hashCode()** as follows:

public native int hashCode();

Native means that the method is a local method, and Java calls the implementation of that method from the local method library.

Since there are JNI method calls in the Object class, according to JNI rules, JNI headers should be generated

Execute the javah-jni java.lang.Object directive in this directory

A javA_lang_object.h header file will be generated

The javA_lang_object.h header contains the following information about the hashCode method:

/* * Class: java_lang_Object * Method: hashCode * Signature: ()I */
JNIEXPORT jint JNICALL Java_java_lang_Object_hashCode
 (JNIEnv *, jobject);
Copy the code

Then open the ** openJDK \ JDK \ SRC \share\native\ Java \lang\ ** directory

Looking at the object.c file, you can see that the hashCode() method is registered to handle the JVM_IHashCode method pointer:

#include #include#include#include "jni.h"
#include "jni_util.h"
#include "jvm.h"

#include "java_lang_Object.h"

static JNINativeMethod methods[] = {
   {"hashCode"."()I",                    (void *)&JVM_IHashCode}, // The method pointer to hashcode 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},
};

JNIEXPORT void JNICALL
Java_java_lang_Object_registerNatives(JNIEnv *env, jclass cls)
{
   (*env)->RegisterNatives(env, cls,
                           methods, sizeof(methods)/sizeof(methods[0]));
}

JNIEXPORT jclass JNICALL
Java_java_lang_Object_getClass(JNIEnv *env, jobject this)
{
   if (this == NULL) {
       JNU_ThrowNullPointerException(env, NULL);
       return 0;
   } else {
       return (*env)->GetObjectClass(env, this); }}Copy the code

The JVM_IHashCode method pointer is defined in openJDK \hotspot\ SRC \ Share \vm\prims\jvm.cpp,

As follows:

JVM_ENTRY(jint, JVM_IHashCode(JNIEnv* env, jobject handle))
 JVMWrapper("JVM_IHashCode");
 // as implemented in the classic virtual machine; return 0 if object is NULL
 return handle == NULL ? 0 : ObjectSynchronizer::FastHashCode (THREAD, JNIHandles::resolve_non_null(handle)) ;
JVM_END
Copy the code

As you can see, JVM_IHashCode method called ObjectSynchronizer: : FastHashCode method

* * * * ObjectSynchronizer: : fashHashCode () method

Openjdk/hotspot/SRC/share/vm/Runtime/synchronizer.cpp

// hashCode() generation :
//
// Possibilities:
// * MD5Digest of {obj,stwRandom}
// * CRC32 of {obj,stwRandom} or any linear-feedback shift register function.
// * A DES- or AES-style SBox[] mechanism
// * One of the Phi-based schemes, such as:
// 2654435761 = 2^32 * Phi (golden ratio)
// HashCodeValue = ((uintptr_t(obj) >> 3) * 2654435761) ^ GVars.stwRandom ;
// * A variation of Marsaglia's shift-xor RNG scheme.
// * (obj ^ stwRandom) is appealing, but can result
// in undesirable regularity in the hashCode values of adjacent objects
// (objects allocated back-to-back, in particular). This could potentially
// result in hashtable collisions and reduced hashtable efficiency.
// There are simple ways to "diffuse" the middle address bits over the
// generated hashCode values:
//

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 = 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 = 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;
}
/ / ObjectSynchronizer: : FastHashCode method, this method will eventually return hashcode we look forward to already a long time
intptr_t ObjectSynchronizer::FastHashCode (Thread * Self, oop obj) {
 if (UseBiasedLocking) {
   // NOTE: many places throughout the JVM do not expect a safepoint
   // to be taken here, in particular most operations on perm gen
   // objects. However, we only ever bias Java instances and all of
   // the call sites of identity_hash that might revoke biases have
   // been checked to make sure they can handle a safepoint. The
   // added check of the bias pattern is to avoid useless calls to
   // thread-local storage.
   if (obj->mark()->has_bias_pattern()) {
     // Box and unbox the raw reference just in case we cause a STW safepoint.
     Handle hobj (Self, obj) ;
     // Relaxing assertion for bug 6320749.
     assert(Universe::verify_in_progress() || ! SafepointSynchronize::is_at_safepoint(),"biases should not be seen by VM thread here");
     BiasedLocking::revoke_and_rebias(hobj, false, JavaThread::current());
     obj = hobj() ;
     assert(! obj->mark()->has_bias_pattern(),"biases should be revoked by now"); }}// hashCode() is a heap mutator ...
 // Relaxing assertion for bug 6320749.
 assert(Universe::verify_in_progress() || ! SafepointSynchronize::is_at_safepoint(),"invariant");assert (Universe::verify_in_progress() ||
         Self->is_Java_thread() , "invariant");assert(Universe::verify_in_progress() || ((JavaThread *)Self)->thread_state() ! = _thread_blocked,"invariant"); ObjectMonitor* monitor = NULL; markOop temp, test; intptr_t hash; markOop mark = ReadStableMark (obj);// object should remain ineligible for biased locking
 assert(! mark->has_bias_pattern(),"invariant");if (mark->is_neutral()) {
   hash = mark->hash();              // this is a normal header
   if (hash) {                       // if it has hash, just return it
     return hash;
   }
   hash = get_next_hash(Self, obj);  // allocate a new hash code
   temp = mark->copy_set_hash(hash); // merge the hash code into header
   // use (machine word version) atomic operation to install the hash
   test = (markOop) Atomic::cmpxchg_ptr(temp, obj->mark_addr(), mark);
   if (test == mark) {
     return hash;
   }
   // If atomic operation failed, we must inflate the header
   // into heavy weight monitor. We could add more code here
   // for fast path, but it does not worth the complexity.
 } else if (mark->has_monitor()) {
   monitor = mark->monitor();
   temp = monitor->header();
   assert (temp->is_neutral(), "invariant"); hash = temp->hash();if (hash) {
     return hash;
   }
   // Skip to the following code to reduce code size
 } else if (Self->is_lock_owned((address)mark->locker())) {
   temp = mark->displaced_mark_helper(); // this is a lightweight monitor owned
   assert (temp->is_neutral(), "invariant"); hash = temp->hash();// by current thread, check if the displaced
   if (hash) {                       // header contains hash code
     return hash;
   }
   // WARNING:
   // The displaced header is strictly immutable.
   // It can NOT be changed in ANY cases. So we have
   // to inflate the header into heavyweight monitor
   // even the current thread owns the lock. The reason
   // is the BasicLock (stack slot) will be asynchronously
   // read by other threads during the inflate() function.
   // Any change to stack may not propagate to other threads
   // correctly.
 }

 // Inflate the monitor to set hash code
 monitor = ObjectSynchronizer::inflate(Self, obj);
 // Load displaced header and check it has hash code
 mark = monitor->header();
 assert (mark->is_neutral(), "invariant"); hash = mark->hash();if (hash == 0) {
   hash = get_next_hash(Self, obj);
   temp = mark->copy_set_hash(hash); // merge hash code into header
   assert (temp->is_neutral(), "invariant"); test = (markOop) Atomic::cmpxchg_ptr(temp, monitor, mark);if(test ! = mark) {// The only update to the header in the monitor (outside GC)
     // is install the hash code. If someone add new usage of
     // displaced header, please update this code
     hash = test->hash();
     assert (test->is_neutral(), "invariant");assert(hash ! =0."Trivial unexpected object/monitor header usage."); }}return hash;
}
Copy the code

Through such complicated steps we finally get the hash code value.

A lot of times we don’t want to print the hash value of an object in real development

I want to get specific properties of the object. What do I do?

We know that the toString method is automatically called when an object is printed directly

This method inherits from the Object class, so in order to change the content

Obviously we need to rewrite the toString method as follows:

@Override
public String toString(a) {
   return "Student{" +
           "id=" + id +
           ", name='" + name + '\' ' +
           ", age=" + age +
           '} ';
}
Copy the code

When we overwrite, we find that instead of printing hash values, we print the values of the properties that we want to get


Reprinted from: Immediate Employment Assistance

The original link

Previous good articles recommended: juejin.cn/post/695277…