Object is the parent class of all classes, that is, all classes in Java are inherited directly or indirectly from the Object class. For example, if you create a classA, although it is not explicitly stated, it is the default of extendsObject.
The following three points "..." indicate that several uncertain number of parameters can be accepted. The old way of writing is Objectargs[], but it is recommended to use... in the new version of Java. For example
publicvoidgetSomething(String...strings)(){}
object is the parent class of all classes in Java. That is to say, all classes, whether they are created by themselves or the classes in the system, are inherited from the object class, that is, all classes can replace the object class in any occasion. According to the principle of Rich's replacement, subclasses can replace their parent class in any occasion, but the parent class may not necessarily replace their subclass. What is often said in Java is actually this truth! The object class embodies the four major characteristics of polymorphism, inheritance, encapsulation, and abstraction in the OOP idea!
The object class is the base class of all classes, not a data type. You can query the jdk document to understand this, all classes are inherited from Object.
Object...objects This parameter definition is a polymorphic manifestation in the case of uncertain method parameters. That is, this method can pass multiple parameters, and the number of these parameters is uncertain. In this way, you need to do some corresponding processing in the method body. Because Object is a base class, use parameter form such as Object...objects, allowing all objects inherited from Object as parameters. This method should be used relatively rarely in practice.
The form of Object[]obj is a parameter form composed of an Object array. This means that the parameters of this method are fixed and are an Object array. As for the elements stored in this array, they can be objects inherited from all classes of Object.
It is recommended that you read these basic things a few more times "Thinkinjava"
Java's Object is the parent class of all other classes. From the perspective of inheritance, it is the top-level root, so it is also the only class without a parent class. It contains some commonly used methods for objects, such as getClass, hashCode, equals, clone, toString, notify, wait and other commonly used methods. Therefore, after other classes inherit Object, they can implement these methods without repeated implementation. Most of these methods are native methods, and the following detailed analysis is made.
The main code is as follows:
public class Object {private static native void registerNatives();static {registerNatives();}public final native Class<?> getClass();public native int hashCode();public Boolean equals(Object obj) {return (this == obj);}protected native Object clone() throws CloneNotSupportedException;public String toString() {return getClass().getName() + "@" + Integer.toHexString(hashCode());}public final native void notify();public final native void notifyAll();public final native void wait(long timeout) throws InterruptedException;public final void wait(long timeout, int nanos) throws InterruptedException {if (timeout < 0) {throw new IllegalArgumentException("timeout value is negative");}if (nanos < 0 || nanos > 999999) {throw new IllegalArgumentException("nanos second timeout value out of range");}if (nanos > 0) {timeout++;}wait(timeout);}public final void wait() throws InterruptedException {wait(0);}protected void finalize() throws Throwable {}}registerNatives method
Since the registerNatives method is modified by a static block, the method will be executed when the Object class is loaded. The corresponding local method is Java_java_lang_Object_registerNatives, as follows,
JNIEXPORT void JNICALLJava_java_lang_Object_registerNatives(JNIEnv *env, jclass cls){(*env)->RegisterNatives(env, cls,methods, sizeof(methods)/sizeof(methods[0]));}You can see that it indirectly calls the method of the JNINativeInterface_ structure, which can be simply regarded as this: what it does is probably correspond to the method name of the Java layer with the local function, so that the execution engine can call C/C++ functions based on these corresponding relationship tables when executing bytecode. As shown below, register these methods. When the execution engine executes the hashCode method, it can find the JVM's JVM_IHashCode function through the relationship table. ()I can also know that the types on the Java layer should be converted to int type. This mapping can actually be regarded as mapping a string to a function pointer.
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},}; getClass method
The getClass method is also a local method, and the corresponding local method is Java_java_lang_Object_getClass, as follows:
JNIEXPORT jclass JNICALLJava_java_lang_Object_getClass(JNIEnv *env, jobject this){ if (this == NULL) { JNU_ThrowNullPointerException(env, NULL); return 0; } else { return (*env)->GetObjectClass(env, this); }}So here we mainly look at the GetObjectClass function. The corresponding class in the Java layer in the C++ layer is klassOop, so the metadata and method information about the class can be obtained through it.
JNI_ENTRY(jclass, jni_GetObjectClass(JNIEnv *env, job obj)) JNIWrapper("GetObjectClass"); DTRACE_PROBE2(hotspot_jni, GetObjectClass__entry, env, obj); klassOop k = JNIHandles::resolve_non_null(obj)->klass(); jclass ret = (jclass) JNIHandles::make_local(env, Klass::cast(k)->java_mirror()); DTRACE_PROBE1(hotspot_jni, GetObjectClass__return, ret); return ret;JNI_ENDhashCode method
From the previous registerNatives method registers several local methods, we can see that the function corresponding to the hashCode method is JVM_IHashCode, that is,
JVM_ENTRY(jint, JVM_IHashCode(JNIEnv* env, job 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_ENDThe logic generated for hashcode is determined by the get_next_hash function of synchronizer.cpp. The implementation is relatively complex. There are different generation strategies based on different values of hashcode, and finally a hash mask is used to process it.
static inline intptr_t get_next_hash(Thread * Self, oop obj) {intptr_t value = 0 ;if (hashCode == 0) {value = os::random() ;} else if (hashCode == 1) {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 {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;} equals method
This is a non-local method, and the judgment logic is very simple, and it is directly == comparison.
clone method
From the local method table, we know that the local function corresponding to the clone method is JVM_Clone. The clone method mainly implements the cloning function of the object and generates the same new object based on the object (the attributes of the object of our common class will clone the value if it is a primitive type, but if it is an object, the address of the object will be cloned). To implement cloning of Java classes, you need to implement the Cloneable interface. If (!klass->is_cloneable()) will verify whether the interface is implemented. Then determine whether the memory space is allocated in two situations. The new object is new_obj, and then copy and C++ layer data structure are set for new_obj. Finally, it will be converted to the job type to the Java layer Object type.
JVM_ENTRY(jobject, JVM_Clone(JNIEnv* env, job handle)) JVMWrapper("JVM_Clone");Handle obj(THREAD, JNIHandles::resolve_non_null(handle));const KlassHandle klass (THREAD, obj->klass());JvmtiVMObjectAllocEventCollector oam;if (!klass->is_cloneable()) {ResourceMark rm(THREAD);THROW_MSG_0(vmSymbols::java_lang_CloneNotSupportedException(), klass->external_name());}const int size = obj->size();oop new_obj = NULL;if (obj->is_javaArray()) {const int length = ((arrayOop)obj())->length();new_obj = CollectedHeap::array_allocate(klass, size, length, CHECK_NULL);} else {new_obj = CollectedHeap::obj_allocate(klass, size, CHECK_NULL);}Copy::conjoint_jlongs_atomic((jlong*)obj(), (jlong*)new_obj, (size_t)align_object_size(size) / HeapWordsPerlong);new_obj->init_mark();BarrierSet* bs = Universe::heap()->barrier_set();assert(bs->has_write_region_opt(), "Barrier set does not have write_region");bs->write_region(MemRegion((HeapWord*)new_obj, size));if (klass->has_finalizer()) {assert(obj->is_instance(), "should be instanceOop");new_obj = instanceKlass::register_finalizer(instanceOop(new_obj), CHECK_NULL);}return JNIHandles::make_local(env, oop(new_obj));JVM_ENDtoString method
The logic is to get the class name plus @ plus hexadecimal hashCode.
notify method
This method is used to wake up the thread, and the final modification instructions cannot be rewritten. The corresponding local method is JVM_MonitorNotify. ObjectSynchronizer::notify will eventually call ObjectMonitor::notify(TRAPS). This process is that ObjectSynchronizer will try to get the freeObjectMonitor object in the current thread, and try to get it from the global if it fails.
JVM_ENTRY(void, JVM_MonitorNotify(JNIEnv* env, job handle)) JVMWrapper("JVM_MonitorNotify"); Handle obj(THREAD, JNIHandles::resolve_non_null(handle)); assert(obj->is_instance() || obj->is_array(), "JVM_MonitorNotify must apply to an object"); ObjectSynchronizer::notify(obj, CHECK); JVM_ENDThe ObjectMonitor object contains a _WaitSet queue object, which holds all threads in the wait state and is represented by the ObjectWaiter object. What notify needs to do is to first obtain the _WaitSet queue lock, then remove the first ObjectWaiter object in the _WaitSet queue, and then process the object according to different strategies, such as adding it to the _EntryList queue. Then release the _WaitSet queue lock. It does not release the corresponding lock of synchronized, so the lock can only be released until the synchronized synchronization block is over.
void ObjectMonitor::notify(TRAPS) {CHECK_OWNER();if (_WaitSet == NULL) {TEVENT (Empty-Notify) ;return ;}DTRACE_MONITOR_PROBE(notify, this, object(), THREAD);int Policy = Knob_MoveNotifyee ;Thread::SpinAcquire (&_WaitSetLock, "WaitSet - notify") ;ObjectWaiter * iterator = DequeueWaiter() ;if (iterator != NULL) {TEVENT (Notify1 - Transfer) ;guarantee (iterator->TState == ObjectWaiter::TS_WAIT, "invariant") ;guarantee (iterator->_notified == 0, "invariant") ;if (Policy != 4) {iterator->TState = ObjectWaiter::TS_ENTER ;}iterator->_notified = 1 ;ObjectWaiter * List = _EntryList ;if (List != NULL) {assert (List->_prev == NULL, "invariant") ;assert (List->TState == ObjectWaiter::TS_ENTER, "invariant") ;assert (List != iterator, "invariant") ;}if (Policy == 0) {// prepend to EntryListif (List == NULL) {iterator->_next = iterator->_prev = iterator ;} else {List->_prev = iterator ;iterator->_next = List ;iterator->_prev = NULL ;_EntryList = iterator ;}} else if (Policy == 1) {// append to EntryListif (List == NULL) {iterator->_next = iterator->_prev = NULL ;_EntryList = iterator ;} else {// CONSIDER: finding the tail currently requires a linear-time walk of// the EntryList. We can make tail access constant-time by converting to// a CDLL instead of using our current DLL.ObjectWaiter * Tail ;for (Tail = List ; Tail->_next != NULL ; Tail = Tail->_next) ;assert (Tail != NULL && Tail->_next == NULL, "invariant") ;Tail->_next = iterator ;iterator->_prev = Tail ;iterator->_next = NULL ;}} else if (Policy == 2) {// prepend to cxq// prepend to cxqif (List == NULL) {iterator->_next = iterator->_prev = NULL ;_EntryList = iterator ;} else {iterator->TState = ObjectWaiter::TS_CXQ ;for (;;) {ObjectWaiter * Front = _cxq ;iterator->_next = Front ;if (Atomic::cmpxchg_ptr (iterator, &_cxq, Front) == Front) {break ;}}}} else if (Policy == 3) {// append to cxqiterator->TState = ObjectWaiter::TS_CXQ ;for (;;) {ObjectWaiter * Tail ;Tail = _cxq ;if (Tail == NULL) {iterator->_next = NULL ;if (Atomic::cmpxchg_ptr (iterator, &_cxq, NULL) == NULL) {break ;}} else {while (Tail->_next != NULL) Tail = Tail->_next ;Tail->_next = iterator ;iterator->_prev = Tail ;iterator->_next = NULL ;break ;}}} else {ParkEvent * ev = iterator->_event ;iterator->TState = ObjectWaiter::TS_RUN ;OrderAccess::fence() ;ev->unpark() ;}if (Policy < 4) {iterator->wait_reenter_begin(this);}// _WaitSetLock protects the wait queue, not the EntryList. We could// move the add-to-EntryList operation, above, outside the critical section// protected by _WaitSetLock. In practice that's not useful. With the// exception of wait() timeouts and interrupts the monitor owner// is the only thread that grabs _WaitSetLock. There's almost no content// on _WaitSetLock so it's not profitable to reduce the length of the// critical section.}Thread::SpinRelease (&_WaitSetLock) ;if (iterator != NULL && ObjectMonitor::_sync_Notifications != NULL) {ObjectMonitor::_sync_Notifications->inc() ;}}notifyAll method
Similar to the notify method, it is just that when fetching the _WaitSet queue, it is not the first one but all.
Wait method
The wait method makes the thread wait. Its corresponding local method is JVM_MonitorWait, which indirectly calls ObjectSynchronizer::wait, which corresponds to notify. It is also the wait method corresponding to calling the ObjectMonitor object. This method is long and will not be posted here. It is probably to create an ObjectWaiter object, then obtain the _WaitSet queue lock and add the ObjectWaiter object to the queue, and then release the queue lock. In addition, it will release the corresponding lock of synchronized, so the lock does not wait until the synchronized synchronization block is over.
JVM_ENTRY(void, JVM_MonitorWait(JNIEnv* env, job handle, jlong ms)) JVMWrapper("JVM_MonitorWait"); Handle obj(THREAD, JNIHandles::resolve_non_null(handle)); assert(obj->is_instance() || obj->is_array(), "JVM_MonitorWait must apply to an object"); JavaThreadInObjectWaitState jtiows(thread, ms != 0); if (JvmtiExport::should_post_monitor_wait()) { JvmtiExport::post_monitor_wait((JavaThread *)THREAD, (oop)obj(), ms); } ObjectSynchronizer::wait(obj, ms, CHECK);JVM_ENDFinalize method
This method is used to be called when the object is recycled. This is supported by the JVM. The finalize method of Object does nothing by default. If the subclass needs to perform some logical processing when the object is recycled, the finalize method can be overridden.
Summarize
The above is all the content of this article about Java's example analysis of Object from the perspective of JDK source code. I hope it will be helpful to everyone. Interested friends can continue to refer to other related topics on this site. If there are any shortcomings, please leave a message to point it out. Thank you friends for your support for this site!