ในบทความนี้ผู้เขียนจะแนะนำคุณลักษณะที่สำคัญและน่าสนใจใน Java ซึ่งเป็นการชกมวยอัตโนมัติและการยกเลิกกล่องและตีความหลักการของการชกมวยอัตโนมัติและยกเลิกการกล่องจากซอร์สโค้ด ในเวลาเดียวกันคุณสมบัตินี้ยังทิ้งกับดัก หากนักพัฒนาไม่ใส่ใจพวกเขาจะตกอยู่ในกับดักนี้ได้อย่างง่ายดาย
การเป็นอิสระ
คำนิยาม
เมื่อเขียนโปรแกรม Java ผู้คนมักจะกำหนดวัตถุจำนวนเต็มในรูปแบบต่อไปนี้:
จำนวนเต็ม i = 100;
จากรหัสข้างต้นคุณสามารถรู้ได้ว่าฉันเป็นข้อมูลอ้างอิงของจำนวนเต็มประเภทและ 100 เป็นประเภทข้อมูลพื้นฐานใน Java (ประเภทข้อมูลดั้งเดิม) วิธีการผ่านประเภทข้อมูลพื้นฐานโดยตรงไปยังคลาส wrapper ที่สอดคล้องกันคือการชกมวยอัตโนมัติ
ใน JDK 1.5 มีการแนะนำมวยอัตโนมัติเป็นครั้งแรก ก่อน JDK 1.5 หากคุณต้องการกำหนดวัตถุจำนวนเต็มที่มีค่า 100 คุณต้องทำสิ่งนี้:
จำนวนเต็ม i = จำนวนเต็มใหม่ (100);
หลักการ
มาสร้างจุดพักที่รหัสด้านบน "จำนวนเต็ม i = 100;" และติดตามมัน
ต่อไปเราจะเห็นได้ว่าโปรแกรมจะข้ามไปที่วิธีการ (int i) ของคลาสจำนวนเต็ม
/** * ส่งคืนอินสแตนซ์ <tt> จำนวนเต็ม </tt> แทนค่า * <tt> int </tt> ที่ระบุ * หากไม่จำเป็นต้องใช้อินสแตนซ์ <tt> จำนวนเต็มใหม่ </tt> วิธีนี้ควรใช้วิธีนี้ * โดยทั่วไปควรใช้ในการตั้งค่ากับตัวสร้าง * {@link #integer (int)} เนื่องจากวิธีนี้น่าจะให้พื้นที่และประสิทธิภาพที่ดีขึ้นอย่างมีนัยสำคัญโดยการแคช * ค่าที่ร้องขอบ่อยครั้ง * * @param ฉันเป็นค่า <code> int </code> * @return a <tt> อินสแตนซ์ </tt> อินสแตนซ์แทน <tt> i </tt> * @Since 1.5 */ ค่าคงที่ค่าคงที่สาธารณะ (int i) {ถ้า (i> = -128 && i <= integercache.high) ส่งคืน integercache.cache [i + 128]; อื่นกลับใหม่จำนวนเต็มใหม่ (i); -กล่าวอีกนัยหนึ่งการบรรจุคือ JDK ที่ช่วยให้คุณโทรไปยัง Integer.ValueOf (100)
การแกะกล่องออก
คำนิยาม
จำนวนเต็มจำนวนเต็ม 100 = 100; int int100 = integer100;
จากรหัสด้านบนคุณจะเห็นว่า Integer100 เป็นข้อมูลอ้างอิงถึงพิมพ์จำนวนเต็มและ INT100 เป็นประเภทข้อมูลดั้งเดิมของประเภท int อย่างไรก็ตามเราสามารถกำหนดวัตถุประเภทจำนวนเต็มให้กับตัวแปรของชนิดข้อมูลดั้งเดิมที่สอดคล้องกัน นี่คือการยกเลิกการกล่อง
Unboxing เป็นสิ่งที่ตรงกันข้ามกับการบรรจุ การชกมวยเป็นตัวแปรที่กำหนดชนิดข้อมูลดั้งเดิมให้กับคลาสที่ห่อหุ้ม Unboxing หมายถึงการกำหนดตัวแปรของคลาส encapsulated ให้กับตัวแปรของชนิดข้อมูลดั้งเดิมที่สอดคล้องกัน ชื่อของการบรรจุและ Unboxing ก็ค่อนข้างเหมาะสมเช่นกัน
หลักการ
ฉันเชื่อว่าทุกคนคาดเดาได้ว่า JDK ทำอะไรให้เราในระหว่างกระบวนการ Unboxing มาพิสูจน์การคาดเดาของเราผ่านการทดลอง
ตั้งค่าเบรกพอยต์บนบรรทัดที่สองของรหัสด้านบนนั่นคือตั้งค่าเบรกพอยต์เป็น "int int100 = integer100;" และติดตามมัน
เราจะเห็นได้ว่าโปรแกรมข้ามไปยังวิธี intvalue () ของจำนวนเต็ม
/** * ส่งคืนค่าของ <code> จำนวนเต็มนี้ </code> เป็น * <code> int </code> */ public int intvalue () {ค่าคืน; -นั่นคือ JDK ช่วยให้เราทำการโทรไปยังวิธี intvalue () สำหรับการทดลองข้างต้นมันคือการเรียกใช้วิธี intvalue () ของ Integer100 และกำหนดค่าคืนค่าให้กับ Int100
ซึ่งขยายออกไป
การทดลองที่ 1
Integer Integer400 = 400; int int400 = 400; system.out.println (Integer400 == int400);
ในบรรทัดที่สามของรหัสด้านบน Integer400 และ INT400 เรียกใช้งาน == Run และทั้งสองนี้เป็นตัวแปรประเภทต่าง ๆ Integer400 Unboxing หรือ Int400 บรรจุอยู่หรือไม่? ผลลัพธ์ของการดำเนินการคืออะไร?
== การดำเนินการคือการพิจารณาว่าที่อยู่ของวัตถุสองชิ้นมีค่าเท่ากันหรือไม่หรือค่าของประเภทข้อมูลพื้นฐานสองชนิดนั้นเท่ากัน ดังนั้นจึงเป็นเรื่องง่ายที่จะอนุมานได้ว่าหากไม่มีการบ็อกซ์ Integer400 หมายความว่าค่าของสองประเภทพื้นฐานจะถูกเปรียบเทียบและผลลัพธ์การทำงานจะต้องเป็นจริงในเวลานี้ หาก Int400 บรรจุหมายความว่าที่อยู่ของวัตถุทั้งสองมีค่าเท่ากันและผลลัพธ์การรันจะต้องเป็นเท็จในเวลานี้ (สำหรับสาเหตุที่ผู้เขียนมอบหมายให้พวกเขาถึง 400 มันเกี่ยวข้องกับกับดักที่จะกล่าวถึงในภายหลัง)
ผลการทำงานที่แท้จริงของเราเป็นจริง ดังนั้นมันจึงเป็น Integer400 Unboxing ผลลัพธ์ของการติดตามรหัสพิสูจน์สิ่งนี้
การทดลองที่ 2
จำนวนเต็มจำนวนเต็ม 100 = 100; int int100 = 100; system.out.println (integer100.equals (int100));
ในบรรทัดที่สามของรหัสข้างต้นพารามิเตอร์ของวิธีการของ Integer100 เท่ากับ INT100 เรารู้ว่าพารามิเตอร์ของวิธี Equals เป็นวัตถุไม่ใช่ชนิดข้อมูลพื้นฐานดังนั้นที่นี่จะต้องบรรจุ Int100 ผลลัพธ์ของการติดตามรหัสพิสูจน์สิ่งนี้
ในความเป็นจริงหากประเภทพารามิเตอร์ในเมธอดคือประเภทข้อมูลดั้งเดิมและประเภทพารามิเตอร์ที่ส่งผ่านคือคลาสการห่อหุ้มมันจะไม่ถูกบ็อกซ์โดยอัตโนมัติ ดังนั้นหากประเภทพารามิเตอร์ในวิธีการคือประเภทการห่อหุ้มและประเภทพารามิเตอร์ที่ส่งผ่านเป็นชนิดข้อมูลดั้งเดิมมันจะถูกบรรจุโดยอัตโนมัติ
การทดลอง 3
จำนวนเต็มจำนวนเต็ม 100 = 100; int int100 = 100; long200 = 200l; system.out.println (integer100 + int100); system.out.println (long200 == (integer100 + int100));
ในการทดลองครั้งแรกเราได้เรียนรู้ว่าเมื่อชนิดข้อมูลพื้นฐานดำเนินการ == การดำเนินการด้วยคลาส encapsulation คลาส encapsulation จะไม่ถูกบ็อกซ์ เกิดอะไรขึ้นถ้า +, -, *, /? เราสามารถรู้ได้ในการทดลองนี้
หาก + การดำเนินการประเภทข้อมูลพื้นฐานจะถูกบรรจุแล้ว:
•ในบรรทัดที่ 4, Integer100+INT100 จะได้รับวัตถุ O ของจำนวนเต็มประเภทและค่า 200 และดำเนินการวิธี TOSTRING () ของวัตถุและเอาต์พุตนี้ "200";
•ในบรรทัดที่ 5 Integer100+INT100 จะได้รับวัตถุ O ของจำนวนเต็มประเภทและค่าของ 200 การดำเนินการ == เปรียบเทียบวัตถุนี้กับวัตถุ Long200 เห็นได้ชัดว่าเท็จจะถูกส่งออก;
•ในบรรทัดที่ 6 Integer100+INT100 จะได้รับวัตถุ O ของจำนวนเต็มชนิดและค่า 200. วิธี Equals ของ Long เปรียบเทียบ Long200 กับ O เนื่องจากทั้งคู่เป็นคลาสที่ห่อหุ้มด้วยประเภทที่แตกต่างกันดังนั้นเอาต์พุตเป็นเท็จ
ถ้า + การดำเนินการคลาส encapsulation จะไม่ถูกบ็อกซ์แล้ว:
•ในบรรทัดที่ 4, Integer100+INT100 จะได้รับประเภทข้อมูลพื้นฐาน B ของประเภท int และ value 200 จากนั้น Box B เพื่อรับ O, ดำเนินการวิธีการ TOSTRING () ของวัตถุนี้และเอาต์พุต "200";
•ในบรรทัดที่ 5 Integer100+INT100 จะได้รับประเภทข้อมูลพื้นฐาน B1 ของประเภท int และ value 200. การดำเนินการ == unpoxes long200 เพื่อรับ B2 เห็นได้ชัดว่า b1 == b2 และส่งออกจริง;
•ในบรรทัดที่ 6, Integer100+INT100 จะได้รับประเภทข้อมูลพื้นฐาน B ของประเภท int และ value 200. Long's Method Method Boxs B แต่ผลการชกมวยในวัตถุ O ของจำนวนเต็มประเภทเนื่องจาก O และ Long200 เป็นวัตถุประเภทต่าง ๆ ดังนั้นเอาต์พุตเป็นเท็จ
ผลลัพธ์ของการทำงานของโปรแกรมคือ:
200
จริง
เท็จ
ดังนั้นการเก็งกำไรที่สองจึงถูกต้องนั่นคือคลาส encapsulation จะไม่ถูกบ็อกซ์ระหว่างการดำเนินการ +
กับดัก
กับดัก 1
จำนวนเต็มจำนวนเต็ม 100 = null;
int int100 = integer100;
รหัสสองบรรทัดนี้ถูกกฎหมายอย่างสมบูรณ์และสามารถรวบรวมได้อย่างสมบูรณ์ แต่เมื่อรันข้อยกเว้นตัวชี้โมฆะจะถูกโยนลงไป ในหมู่พวกเขา Integer100 เป็นวัตถุของจำนวนเต็มประเภทซึ่งแน่นอนว่าสามารถชี้ไปที่ Null แต่ในบรรทัดที่สอง Integer100 จะไม่ถูกบ็อกซ์นั่นคือเมธอด intvalue () จะถูกดำเนินการบนวัตถุที่เป็นโมฆะและแน่นอนว่าข้อยกเว้นตัวชี้โมฆะจะถูกโยนทิ้ง ดังนั้นเมื่อยกเลิกการกล่องคุณต้องให้ความสนใจเป็นพิเศษว่าวัตถุคลาสที่ห่อหุ้มเป็นโมฆะหรือไม่
กับดัก 2
จำนวนเต็ม i1 = 100;
จำนวนเต็ม i2 = 100;
จำนวนเต็ม i3 = 300;
จำนวนเต็ม i4 = 300;
System.out.println (i1 == i2);
System.out.println (i3 == i4);
เนื่องจาก i1, i2, i3 และ i4 เป็นจำนวนเต็มทั้งหมดเราคิดว่าผลลัพธ์การทำงานควรเป็นเท็จ อย่างไรก็ตามผลการทำงานจริงคือ "system.out.println (i1 == i2);" ซึ่งเป็นจริง แต่ "system.out.println (i3 == i4);" ซึ่งเป็นเท็จ ซึ่งหมายความว่าการอ้างอิงของสองประเภทจำนวนเต็ม i1 และ i2 ชี้ไปที่วัตถุเดียวกันในขณะที่ i3 และ i4 ชี้ไปที่วัตถุต่าง ๆ ทำไม พวกเขาทั้งหมดไม่ได้เรียกว่าเมธอดจำนวนเต็ม Valueof (int i) หรือไม่?
ลองดูที่จำนวนเต็ม Valueof (int i) อีกครั้ง
/** * ส่งคืนอินสแตนซ์ <tt> จำนวนเต็ม </tt> แทนค่า * <tt> int </tt> ที่ระบุ * หากไม่จำเป็นต้องใช้อินสแตนซ์ <tt> จำนวนเต็มใหม่ </tt> วิธีนี้ควรใช้วิธีนี้ * โดยทั่วไปควรใช้ในการตั้งค่ากับตัวสร้าง * {@link #integer (int)} เนื่องจากวิธีนี้น่าจะให้พื้นที่และประสิทธิภาพที่ดีขึ้นอย่างมีนัยสำคัญโดยการแคช * ค่าที่ร้องขอบ่อยครั้ง * * @param ฉันเป็นค่า <code> int </code> * @return a <tt> อินสแตนซ์ </tt> อินสแตนซ์แทน <tt> i </tt> * @Since 1.5 */ ค่าคงที่ค่าคงที่สาธารณะ (int i) {ถ้า (i> = -128 && i <= integercache.high) ส่งคืน integercache.cache [i + 128]; อื่นกลับใหม่จำนวนเต็มใหม่ (i); -เราจะเห็นได้ว่าเมื่อฉัน> =-128 และ i <= integercache.high, integercache.cache [i + 128] ถูกส่งกลับโดยตรง ในหมู่พวกเขา Integercache เป็นระดับคงที่ภายในของจำนวนเต็มและรหัสดั้งเดิมของมันมีดังนี้:
InteegerCache คลาสส่วนตัวแบบคงที่ {คงที่ระดับสูง int สูง; แคชจำนวนเต็มสุดท้ายคงที่ []; คงที่ {สุดท้าย int low = -128; // ค่าสูงอาจถูกกำหนดค่าโดยคุณสมบัติ int h = 127; ถ้า (IntegerCachehighPropValue! = null) {// ใช้ long.decode ที่นี่เพื่อหลีกเลี่ยงวิธีการเรียกใช้ที่ // ต้องการแคช autoboxing ของจำนวนเต็มเพื่อเริ่มต้น int i = long.decode (integercachehighpropvalue) i = math.max (i, 127); // ขนาดอาร์เรย์สูงสุดคือจำนวนเต็ม max_value h = math.min (i, integer.max_value - -low); } สูง = h; แคช = จำนวนเต็มใหม่ [(สูง - ต่ำ) + 1]; int j = ต่ำ; สำหรับ (int k = 0; k <cache.length; k ++) แคช [k] = จำนวนเต็มใหม่ (j ++); } Private IntegerCache () {}}เราสามารถเห็นได้อย่างชัดเจนว่า IntegerCache มีแคชตัวแปรสมาชิกคงที่ซึ่งเป็นอาร์เรย์ที่มี 256 องค์ประกอบ แคชยังเริ่มต้นใน IntegerCache นั่นคือองค์ประกอบ I-TH เป็นวัตถุจำนวนเต็มที่มีค่า I-128 -128 ถึง 127 เป็นวัตถุจำนวนเต็มที่ใช้กันมากที่สุดและวิธีการนี้ยังช่วยปรับปรุงประสิทธิภาพอย่างมาก มันก็เป็นเพราะสิ่งนี้ว่า "integeri1 = 100; จำนวนเต็ม i2 = 100;", i1 และ i2 ได้รับวัตถุเดียวกัน
เมื่อเปรียบเทียบการทดลองครั้งที่สองในส่วนขยายเราได้เรียนรู้ว่าเมื่อคลาส encapsulation ทำงาน == กับประเภทพื้นฐานคลาส encapsulation จะ unbox และผลการยกเลิกการบ็อกซ์จะถูกเปรียบเทียบกับประเภทพื้นฐาน; ในขณะที่เมื่อคลาสการห่อหุ้มทั้งสองทำงาน == กับวัตถุอื่น ๆ พวกเขาเปรียบเทียบที่อยู่ของวัตถุทั้งสองนั่นคือเพื่อตรวจสอบว่าการอ้างอิงทั้งสองชี้ไปที่วัตถุเดียวกันหรือไม่
บทความข้างต้นกล่าวถึงการบรรจุแบบอัตโนมัติ Java และ Unboxing และกับดักเป็นเนื้อหาทั้งหมดที่ฉันแบ่งปันกับคุณ ฉันหวังว่ามันจะให้ข้อมูลอ้างอิงและฉันหวังว่าคุณจะสนับสนุน wulin.com มากขึ้น