วัตถุเป็นคลาสหลักของคลาสทั้งหมดนั่นคือคลาสทั้งหมดใน Java ได้รับการสืบทอดโดยตรงหรือโดยอ้อมจากคลาสวัตถุ ตัวอย่างเช่นหากคุณสร้าง Classa แม้ว่าจะไม่ได้ระบุไว้อย่างชัดเจน แต่ก็เป็นค่าเริ่มต้นของ ExtendSobject
สามจุดต่อไปนี้ "... " ระบุว่าสามารถยอมรับพารามิเตอร์จำนวนหนึ่งที่ไม่แน่นอนได้ วิธีการเขียนแบบเก่าคือ ObjectArgs [] แต่ขอแนะนำให้ใช้ ... ใน Java เวอร์ชันใหม่ ตัวอย่างเช่น
publicvoidgetSomething(String...strings)(){}
Object เป็นคลาสหลักของคลาสทั้งหมดใน Java กล่าวคือคลาสทั้งหมดไม่ว่าจะถูกสร้างขึ้นด้วยตัวเองหรือคลาสในระบบนั้นได้รับการสืบทอดมาจากคลาสวัตถุนั่นคือคลาสทั้งหมดสามารถแทนที่คลาสวัตถุในทุกโอกาส ตามหลักการของการแทนที่ของ Rich, subclasses สามารถแทนที่คลาสหลักของพวกเขาในทุกโอกาส แต่คลาสหลักอาจไม่จำเป็นต้องแทนที่ subclass ของพวกเขา สิ่งที่มักพูดในชวาเป็นความจริงนี้จริง ๆ ! คลาสวัตถุรวบรวมคุณสมบัติสำคัญสี่ประการของ polymorphism, การสืบทอด, การห่อหุ้มและสิ่งที่เป็นนามธรรมในแนวคิด OOP!
คลาสวัตถุเป็นคลาสพื้นฐานของคลาสทั้งหมดไม่ใช่ชนิดข้อมูล คุณสามารถสอบถามเอกสาร JDK เพื่อทำความเข้าใจสิ่งนี้คลาสทั้งหมดได้รับการสืบทอดมาจากวัตถุ
วัตถุ ... วัตถุคำจำกัดความพารามิเตอร์นี้เป็นการรวมตัวกันแบบ polymorphic ในกรณีของพารามิเตอร์วิธีการที่ไม่แน่นอน นั่นคือวิธีนี้สามารถผ่านพารามิเตอร์หลายตัวและจำนวนพารามิเตอร์เหล่านี้ไม่แน่นอน ด้วยวิธีนี้คุณต้องทำการประมวลผลที่สอดคล้องกันในร่างกายวิธีการ เนื่องจากวัตถุเป็นคลาสพื้นฐานให้ใช้รูปแบบพารามิเตอร์เช่นวัตถุ ... วัตถุที่อนุญาตให้วัตถุทั้งหมดที่สืบทอดมาจากวัตถุเป็นพารามิเตอร์ วิธีนี้ควรใช้ค่อนข้างน้อยในทางปฏิบัติ
รูปแบบของวัตถุ [] OBJ เป็นรูปแบบพารามิเตอร์ที่ประกอบด้วยอาร์เรย์วัตถุ ซึ่งหมายความว่าพารามิเตอร์ของวิธีนี้ได้รับการแก้ไขและเป็นอาร์เรย์วัตถุ สำหรับองค์ประกอบที่เก็บไว้ในอาร์เรย์นี้พวกเขาสามารถเป็นวัตถุที่สืบทอดมาจากทุกคลาสของวัตถุ
ขอแนะนำให้คุณอ่านสิ่งพื้นฐานเหล่านี้อีกสองสามครั้ง "ThinkInjava"
วัตถุของ Java เป็นคลาสหลักของคลาสอื่น ๆ ทั้งหมด จากมุมมองของการสืบทอดมันเป็นรากระดับบนสุดดังนั้นจึงเป็นคลาสเดียวที่ไม่มีคลาสหลัก มันมีวิธีการที่ใช้กันทั่วไปสำหรับวัตถุเช่น getclass, hashcode, เท่ากับ, โคลน, toString, แจ้งเตือน, รอและวิธีการอื่น ๆ ที่ใช้กันทั่วไป ดังนั้นหลังจากคลาสอื่น ๆ สืบทอดวัตถุพวกเขาสามารถใช้วิธีการเหล่านี้โดยไม่ต้องใช้ซ้ำ วิธีการเหล่านี้ส่วนใหญ่เป็นวิธีการดั้งเดิมและมีการวิเคราะห์อย่างละเอียดต่อไปนี้
รหัสหลักมีดังนี้:
วัตถุระดับสาธารณะ {private native native void registernative (); Static {registernative ();} ชั้นเรียนพื้นเมืองสุดท้าย <?> getclass (); hashcode int ของประชาชน (); บูลีนสาธารณะเท่ากับ (Object obj) {return (this == obj);} + "@" + integer.tohexstring (hashCode ());} โมฆะพื้นเมืองสุดท้ายสาธารณะ Notify (); โมฆะพื้นเมืองสุดท้ายสาธารณะ NotifyAll (); void native void สุดท้ายของประชาชนรอ (หมดเวลานาน) พ่น InterruptedException; void สุดท้ายรอ (เวลานาน ลบ ");} if (nanos <0 || nanos> 999999) {โยน unlegalargumentException ใหม่ (" Nanos ค่าหมดเวลาครั้งที่สองออกจากช่วง ");} ถ้า (nanos> 0) {หมดเวลา ++; -วิธีการลงทะเบียน
เนื่องจากเมธอด registernatives ได้รับการแก้ไขโดยบล็อกแบบคงที่วิธีการจะถูกดำเนินการเมื่อโหลดคลาสวัตถุ วิธีการในท้องถิ่นที่สอดคล้องกันคือ Java_java_lang_object_registernatives ดังนี้
Jniexport เป็นโมฆะ jnicalljava_java_lang_object_registernatives (jnienv *env, jclass cls) {( *env)-> registernative (env, cls, วิธีการขนาด (วิธีการ)/sizeof (วิธี [0]);คุณจะเห็นว่ามันเรียกวิธีการทางอ้อมของโครงสร้าง JninativeInterface_ ซึ่งสามารถถือได้ว่าเป็นสิ่งนี้: สิ่งที่อาจสอดคล้องกับชื่อวิธีของเลเยอร์ Java กับฟังก์ชั่นท้องถิ่นเพื่อให้เครื่องมือดำเนินการสามารถเรียกฟังก์ชัน C/C ++ ตามตาราง ดังที่แสดงด้านล่างลงทะเบียนวิธีการเหล่านี้ เมื่อเอ็นจิ้นการดำเนินการดำเนินการวิธีการ HashCode มันสามารถค้นหาฟังก์ชั่น JVM_ihashCode ของ JVM ผ่านตารางความสัมพันธ์ () ฉันยังสามารถรู้ได้ว่าประเภทของเลเยอร์ Java ควรถูกแปลงเป็นประเภท int การแมปนี้สามารถถือได้ว่าเป็นการแมปสตริงกับตัวชี้ฟังก์ชั่น
jninativeMethod แบบคงที่ [] = {{"hashCode", "() i", (void *) & jvm_ihashcode}, {"รอ", "(j) v", (void *) & jvm_monitorwait}, {"notify", "() "() v", (void *) & jvm_monitornotifyall}, {"clone", "() ljava/lang/object;", (void *) & jvm_clone},}; วิธี getClass
วิธี getClass ยังเป็นวิธีการในท้องถิ่นและวิธีการในท้องถิ่นที่สอดคล้องกันคือ Java_java_lang_object_getclass ดังต่อไปนี้:
jniexport jclass jnicalljava_java_lang_object_getclass (jnienv *env, Jobject this) {ถ้า (นี่ == null) {jnu_thrownullpointerexception (env, null); กลับ 0; } else {return (*env)-> getObjectClass (env, this); -ดังนั้นที่นี่เราส่วนใหญ่ดูฟังก์ชั่น getObjectClass คลาสที่สอดคล้องกันในเลเยอร์ Java ในเลเยอร์ C ++ คือ Klassoop ดังนั้นข้อมูลเมตาและวิธีการเกี่ยวกับคลาสสามารถรับได้ผ่าน
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_endวิธี hashcode
จากวิธีการลงทะเบียนก่อนหน้านี้ลงทะเบียนวิธีการในท้องถิ่นหลายวิธีเราจะเห็นได้ว่าฟังก์ชั่นที่สอดคล้องกับวิธีการ HashCode คือ JVM_IHASHCODE นั่นคือนั่นคือ
JVM_ENTRY (JINT, JVM_IHASHCODE (JNIENV* Env, Handing Job)) JVMWRAPPER ("JVM_IHASHCODE"); // ตามที่ใช้ในเครื่องเสมือนคลาสสิก return 0 ถ้าวัตถุเป็น null return handle == null? 0: ObjectSynchronizer :: FastHashCode (เธรด, jnihandles :: resolve_non_null (มือจับ)); jvm_endตรรกะที่สร้างขึ้นสำหรับ HashCode นั้นถูกกำหนดโดยฟังก์ชัน get_next_hash ของ synchronizer.cpp การดำเนินการค่อนข้างซับซ้อน มีกลยุทธ์การสร้างที่แตกต่างกันตามค่า hashcode ที่แตกต่างกันและในที่สุดก็มีการใช้หน้ากากแฮชเพื่อประมวลผล
intlin inline intptr_t get_next_hash (เธรด * ตัวเอง, oop obj) {intptr_t value = 0; ถ้า (hashcode == 0) {value = os :: random ();} อื่นถ้า (hashcode == 1) {intptr_t addrbits = intptr_t gvars.stwrandom;} อื่นถ้า (hashcode == 2) {value = 1; // สำหรับการทดสอบความไว} lese ถ้า (hashcode == 3) {value = ++ gvars.hcequence; << 11); Self-> _ hashstatex = self-> _ hashstatey; self-> _ hashstate = self-> _ hashstatez; self-> _ hashstatez = self-> _ hashstatew; unsigned v = self-> _ hashstatew; ; self-> _ hashstatew = v; value = v;} value & = markoopdesc :: hash_mask; ถ้า (ค่า == 0) ค่า = 0xbad; assert (ค่า! = markoopdesc :: no_hash, "invariant"); วิธีการเท่ากับ
นี่เป็นวิธีที่ไม่ใช่ท้องถิ่นและตรรกะการตัดสินนั้นง่ายมากและเป็นการเปรียบเทียบโดยตรง ==
วิธีการโคลน
จากตารางวิธีการท้องถิ่นเรารู้ว่าฟังก์ชั่นท้องถิ่นที่สอดคล้องกับวิธีการโคลนคือ jvm_clone วิธีการโคลนส่วนใหญ่ใช้ฟังก์ชันการโคลนของวัตถุและสร้างวัตถุใหม่เดียวกันตามวัตถุ (แอตทริบิวต์ของวัตถุของคลาสทั่วไปของเราจะโคลนค่าถ้ามันเป็นประเภทดั้งเดิม แต่ถ้าเป็นวัตถุที่อยู่ของวัตถุจะถูกโคลน) ในการใช้การโคลนนิ่งคลาส Java คุณต้องใช้อินเตอร์เฟส cloneable if (! klass-> is_cloneable ()) จะตรวจสอบว่าอินเทอร์เฟซถูกนำไปใช้หรือไม่ จากนั้นตรวจสอบว่าพื้นที่หน่วยความจำถูกจัดสรรในสองสถานการณ์หรือไม่ วัตถุใหม่คือ new_obj จากนั้นคัดลอกและโครงสร้างข้อมูลเลเยอร์ C ++ จะถูกตั้งค่าสำหรับ new_obj ในที่สุดมันจะถูกแปลงเป็นประเภทงานเป็นประเภทวัตถุเลเยอร์ Java
JVM_ENTRY (Jobject, jvm_clone (jnienv* env, handle งาน)) jvmwrapper ("jvm_clone"); จัดการ obj (เธรด, jnihandles :: resolve_non_null (จับ); (! klass-> is_cloneable ()) {ResourceMark rm (เธรด); throw_msg_0 (vmsymbols :: java_lang_clonenotsupportedException (), klass-> external_name ()); ความยาว int = ((arrayoop) obj ())-> ความยาว (); new_obj = collectedheap :: array_allocate (klass, ขนาด, ความยาว, check_null);} else {new_obj = collectedheap :: obj_allocate (klass, ขนาด, check_null); (jlong*) new_obj, (size_t) align_object_size (ขนาด) / heapwordsperlong); new_obj-> init_mark (); barrierset* bs = universe :: heap ()-> barrier_set () write_region "); bs-> write_region (memregion ((heapword*) new_obj, size)); ถ้า (klass-> has_finalizer ()) {assert (obj-> is_instance ()," ควรเป็นอินสแตนซ์ "); new_obj = อินสแตนซ์ jnihandles :: make_local (env, oop (new_obj)); jvm_endวิธีการ ToString
ตรรกะคือการรับชื่อคลาส Plus @ plus hashcode hexadecimal
วิธีการแจ้งเตือน
วิธีนี้ใช้เพื่อปลุกเธรดและไม่สามารถเขียนคำแนะนำการแก้ไขขั้นสุดท้ายได้ วิธีการในท้องถิ่นที่สอดคล้องกันคือ JVM_Monitornotify ObjectSynchronizer :: Notify จะเรียก ObjectMonitor :: Notify (กับดัก) ในที่สุด กระบวนการนี้คือ ObjectSynchronizer จะพยายามรับวัตถุ FreeObjectMonitor ในเธรดปัจจุบันและพยายามที่จะรับจากทั่วโลกหากมันล้มเหลว
JVM_ENTRY (เป็นโมฆะ, JVM_MONITORNOTIFY (JNIENV* Env, Handing Job)) JVMWRAPPER ("JVM_MONITORNOTIFY"); จัดการ obj (เธรด, jnihandles :: resolve_non_null (จับ)); ยืนยัน (obj-> is_instance () || obj-> is_array (), "jvm_monitornotify ต้องใช้กับวัตถุ"); ObjectSynchronizer :: Notify (OBJ, ตรวจสอบ); jvm_endObjectMonitor Object มีวัตถุคิว _waitset ซึ่งถือเธรดทั้งหมดในสถานะการรอและแสดงโดยวัตถุ ObjectWaiter สิ่งที่ต้องแจ้งให้ทราบคือการได้รับ _waitset คิวล็อคก่อนจากนั้นลบวัตถุ ObjectWaiter แรกในคิว _waitset แล้วประมวลผลวัตถุตามกลยุทธ์ที่แตกต่างกันเช่นเพิ่มลงในคิว _entrylist จากนั้นปล่อยล็อคคิว _waitset มันไม่ได้ปล่อยล็อคที่สอดคล้องกันของการซิงโครไนซ์ดังนั้นล็อคสามารถปล่อยออกมาได้จนกว่าบล็อกซิงโครไนซ์ซิงโครไนซ์จะจบลง
เป็นโมฆะ ObjectMonitor :: Notify (กับดัก) {check_owner (); ถ้า (_waitset == null) {tevent (ว่างเปล่า); return;} dtrace_monitor_probe (แจ้งเตือนนี้, วัตถุ (), thread); iterator = dequeuewaiter (); if (iterator! = null) {tevent (แจ้งเตือน 1-การถ่ายโอน); รับประกัน (iterator-> tstate == ObjectWaiter :: ts_wait, "invariant"); รับประกัน (iterator-> _ notified == 0 ObjectWaiter :: TS_ENTER;} Iterator-> _ Notified = 1; ObjectWaiter * list = _ENTRIST; ถ้า (รายการ! = null) {assert (รายการ-> _ prev == null, "invariant"); ;} if (policy == 0) {// prepend to enterlistif (list == null) {iterator-> _ next = iterator-> _ prev = iterator;} else {list-> _ prev = iterator; iterator-> _ next = list; ผนวกเข้ากับ enterlistif (list == null) {iterator-> _ next = iterator-> _ prev = null; _EntryList = iterator;} อื่น {// พิจารณา: การค้นหาหางในปัจจุบันต้องใช้เวลาเดินเชิงเส้นของ // รายการ เราสามารถทำให้การเข้าถึงหางคงที่เวลาโดยแปลงเป็น // a cdll แทนที่จะใช้ dll.objectwaiter * tail; สำหรับ (tail = list; tail-> _ ถัดไป! = null; tail = tail-> _ ถัดไป); assert (tail! = null && tail-> _ next == null ; iterator-> _ next = null;}} else ถ้า (นโยบาย == 2) {// prepend to cxq // prepend to cxqif (list == null) {iterator-> _ next = iterator-> _ prev = null; _entrylist = iterator; {ObjectWaiter * front = _cxq; iterator-> _ next = front; if (atomic :: cmpxchg_ptr (iterator, & _cxq, front) == ด้านหน้า) {break;}}}}}}}}}}}} {ObjectWaiter * tail; tail = _cxq; if (tail == null) {iterator-> _ next = null; ถ้า (อะตอม :: cmpxchg_ptr (iterator, & _cxq, null) == null) {break;}}} ; iterator-> _ prev = tail; iterator-> _ next = null; break;}}} else {parkevent * ev = iterator-> _ เหตุการณ์; iterator-> tstate = objectWaiter :: ts_run; orderaccess :: fence (); ev-> unpark () {iterator-> wait_reenter_begin (นี่);} // _waitsetlock ปกป้องคิวรอไม่ใช่รายการเข้าร่วม เราสามารถ // ย้ายการดำเนินการเพิ่มเติมไปยังด้านบนด้านบนนอกส่วนที่สำคัญ // ได้รับการปกป้องโดย _waitsetlock ในทางปฏิบัติที่ไม่มีประโยชน์ ด้วย // ข้อยกเว้นของการรอ () หมดเวลาและขัดจังหวะเจ้าของจอภาพ // เป็นเธรดเดียวที่คว้า _waitsetlock แทบจะไม่มีเนื้อหา // บน _waitsetlock ดังนั้นจึงไม่ทำกำไรได้เพื่อลดความยาวของ // ส่วนสำคัญ} Thread :: spinrelease (& _waitsetlock); ถ้า (iterator! = null && objectmonitor :: _ sync_notifications! = null)วิธีการแจ้งเตือน
คล้ายกับวิธีการแจ้งเตือนมันเป็นเพียงว่าเมื่อดึงคิว _waitset มันไม่ใช่วิธีแรก แต่ทั้งหมด
วิธีรอ
วิธีการรอทำให้เธรดรอ วิธีการในท้องถิ่นที่สอดคล้องกันคือ JVM_Monitorwait ซึ่งเรียก ObjectSynchronizer ทางอ้อม :: รอซึ่งสอดคล้องกับการแจ้งเตือน นอกจากนี้ยังเป็นวิธีการรอที่สอดคล้องกับการเรียกวัตถุ ObjectMonitor วิธีนี้ยาวและจะไม่ถูกโพสต์ที่นี่ มันอาจจะสร้างวัตถุ ObjectWaiter จากนั้นรับ _waitset คิวล็อคและเพิ่มวัตถุ ObjectWaiter ลงในคิวจากนั้นปล่อยคิวล็อค นอกจากนี้มันจะปล่อยล็อคที่สอดคล้องกันของการซิงโครไนซ์ดังนั้นการล็อคจะไม่รอจนกว่าบล็อกซิงโครไนซ์ซิงโครไนซ์จะสิ้นสุดลง
JVM_ENTRY (เป็นโมฆะ, JVM_Monitorwait (JNienv* Env, Handle Job, Jlong MS)) JVMWrapper ("JVM_Monitorwait"); จัดการ obj (เธรด, jnihandles :: resolve_non_null (จับ)); ยืนยัน (obj-> is_instance () || obj-> is_array (), "jvm_monitorwait ต้องใช้กับวัตถุ"); javathreadinobjectwaitstate jtiows (เธรด, ms! = 0); if (jvmtiexport :: ควร _post_monitor_wait ()) {jvmtiexport :: post_monitor_wait ((javathread *) เธรด (oop) obj (), ms); } ObjectSynchronizer :: Wait (obj, MS, ตรวจสอบ); jvm_endวิธีการขั้นสุดท้าย
วิธีนี้ใช้เรียกเมื่อวัตถุถูกรีไซเคิล สิ่งนี้ได้รับการสนับสนุนโดย JVM วิธีสุดท้ายของวัตถุไม่ได้ทำอะไรโดยค่าเริ่มต้น หากคลาสย่อยจำเป็นต้องทำการประมวลผลแบบลอจิคัลเมื่อมีการรีไซเคิลวัตถุวิธีการสุดท้ายสามารถแทนที่ได้
สรุป
ข้างต้นเป็นเนื้อหาทั้งหมดของบทความนี้เกี่ยวกับการวิเคราะห์ตัวอย่างของ Java จากมุมมองของซอร์สโค้ด JDK ฉันหวังว่ามันจะเป็นประโยชน์กับทุกคน เพื่อนที่สนใจสามารถอ้างถึงหัวข้ออื่น ๆ ที่เกี่ยวข้องในเว็บไซต์นี้ต่อไป หากมีข้อบกพร่องใด ๆ โปรดฝากข้อความไว้เพื่อชี้ให้เห็น ขอบคุณเพื่อนที่ให้การสนับสนุนเว็บไซต์นี้!