คำนำ
บทความนี้ส่วนใหญ่แนะนำเนื้อหาที่เกี่ยวข้องเกี่ยวกับจำนวนเต็มใน Java และแบ่งปันสำหรับการอ้างอิงและการเรียนรู้ของคุณ ฉันจะไม่พูดด้านล่างมากนักลองมาดูการแนะนำรายละเอียดด้วยกัน
ปรสิตจริง
ไม่กี่วันที่ผ่านมาฉันเห็นบทความที่ใช้ร่วมกันโดยช่วงเวลาของฉัน "กลไกการส่งพารามิเตอร์ของฟังก์ชั่น Java - คุณเข้าใจจริงหรือไม่?
ทริกเกอร์บางตัวได้รับการศึกษาในจำนวนเต็มของ Java มาก่อนดังนั้นฉันจึงเขียนบทความนี้โดยหวังว่ามันจะเป็นประโยชน์กับคุณ
แลกเปลี่ยน
ก่อนอื่นให้ดูตัวอย่าง
โปรดใช้ Java เพื่อทำฟังก์ชั่นการแลกเปลี่ยนและการแลกเปลี่ยนค่าจำนวนเต็มสองประเภท
การทดสอบโมฆะแบบคงที่สาธารณะ () โยนข้อยกเว้น {จำนวนเต็ม A = 1, b = 2; swap (a, b); System.out.println ("a =" + a + ", b =" + b);} การแลกเปลี่ยนโมฆะคง อันดับแรก
หากคุณไม่เข้าใจว่าวัตถุ Java ถูกจัดสรรในหน่วยความจำอย่างไรและวิธีการผ่านพารามิเตอร์คุณสามารถเขียนรหัสต่อไปนี้ได้อย่างไร
โมฆะคงที่สาธารณะ swapone (จำนวนเต็ม A, จำนวนเต็ม b) โยนข้อยกเว้น {จำนวนเต็ม atempvalue = a; a = b; b = atempvalue;} ผลลัพธ์ของการรันแสดงให้เห็นว่าค่า A และ B ไม่ได้แลกเปลี่ยน
ลองมาดูกันว่าวัตถุ Java ถูกจัดสรรในหน่วยความจำอย่างไรเมื่อโปรแกรมข้างต้นทำงาน:
การกำหนดที่อยู่วัตถุ
จากนี้เราจะเห็นได้ว่าตารางตัวแปรท้องถิ่นของทั้งสองวิธีเก็บข้อมูลอ้างอิงไปยังที่อยู่ข้อมูลจริงของวัตถุ A และ B
ฟังก์ชั่นการแลกเปลี่ยนที่นำมาใช้ข้างต้นการแลกเปลี่ยนการอ้างอิงเฉพาะตัวแปรท้องถิ่น A และตัวแปรท้องถิ่น B ในฟังก์ชัน SWAP และไม่แลกเปลี่ยนข้อมูลจริงใน JVM HEAP
ดังนั้นข้อมูลที่อ้างอิงโดย A และ B ในฟังก์ชั่นหลักจึงไม่ได้แลกเปลี่ยนดังนั้น A และ B ของตัวแปรท้องถิ่นในฟังก์ชั่นหลักจะไม่เปลี่ยนแปลง
ดังนั้นคุณจะทำงานอย่างไรเมื่อแลกเปลี่ยนข้อมูลในฟังก์ชั่นหลัก
ครั้งที่สอง
ตามการปฏิบัติข้างต้นเราจะพิจารณาการแลกเปลี่ยนค่าข้อมูลของ A และ B บนกอง JVM ได้หรือไม่?
มาเรียนรู้สั้น ๆ เกี่ยวกับวัตถุจำนวนเต็ม มันมีค่าประเภท int ระดับวัตถุเท่านั้นเพื่อแสดงค่าของวัตถุ
ดังนั้นเราจึงใช้การสะท้อนกลับเพื่อแก้ไขค่ารหัสมีดังนี้:
โมฆะคงที่สาธารณะ Swaptwo (จำนวนเต็ม A1, จำนวนเต็ม B1) โยนข้อยกเว้น {Field ValueField = Integer.class.getDeclaredField ("ค่า"); ValueField.SetAccessible (จริง); int tempavalue = valuefield.getInt (a1); ValueField.SetInt (A1, B1.IntValue ()); ValueField.setint (B1, Tempavalue);}ผลการดำเนินงานสอดคล้องกับความคาดหวัง
เซอร์ไพรส์
หลังจากทำงานโปรแกรมข้างต้นจะเกิดอะไรขึ้นถ้าฉันประกาศ Integer c = 1, d = 2;
โปรแกรมตัวอย่างมีดังนี้:
โมฆะคงที่สาธารณะ Swaptwo (จำนวนเต็ม A1, จำนวนเต็ม B1) โยนข้อยกเว้น {Field ValueField = Integer.class.getDeclaredField ("ค่า"); ValueField.SetAccessible (จริง); int tempavalue = valuefield.getInt (a1); ValueField.SetInt (A1, B1.IntValue ()); ValueField.setint (b1, tempavalue);} โมฆะคงที่สาธารณะ testthree () โยนข้อยกเว้น {จำนวนเต็ม a = 1, b = 2; Swaptwo (A, B); System.out.println ("a =" + a + "; b =" + b); จำนวนเต็ม c = 1, d = 2; System.out.println ("c =" + c + "; d =" + d);}ผลลัพธ์ผลลัพธ์มีดังนี้:
a = 2; B = 1C = 2; d = 1
ประหลาดใจหรือไม่! อุบัติเหตุหรือไม่! กระตุ้นหรือไม่!
ในเชิงลึก
เกิดอะไรขึ้นกันแน่? มาดูรหัสที่ถอดรหัสกัน:
ผู้เขียนใช้เครื่องมือ IDE เพื่อถอดรหัสไฟล์. class นี้โดยตรง
โมฆะคงที่สาธารณะ testThree () โยนข้อยกเว้น {จำนวนเต็ม a = จำนวนเต็ม ValueOf (1); จำนวนเต็ม b = จำนวนเต็ม valueof (2); Swaptwo (A, B); System.out.println ("a =" + a + "; b =" + b); จำนวนเต็ม c = integer.valueof (1); จำนวนเต็ม d = integer.valueof (2); System.out.println ("c =" + c + "; d =" + d);} ในกระบวนการของ Java ชกมวยประเภทดั้งเดิมโดยอัตโนมัติไปยังประเภทจำนวนเต็มใช้วิธีการ Integer.valueOf(int)
จะต้องเป็นวิธีที่วิธีนี้ห่อหุ้มการดำเนินงานบางอย่างภายในซึ่งทำให้เรามีผลกระทบทั่วโลกหลังจากแก้ไข Integer.value
ทั้งหมดนี้เกี่ยวข้องกับรหัสในส่วนนี้ของรหัสนี้จะติดกาวในครั้งเดียว (PS: ผู้เขียนที่ไม่ได้ลากเป็น coder ที่ดี):
ชั้นเรียนสาธารณะจำนวนเต็ม { / ** * @Since 1.5 * / ค่าจำนวนเต็มคงที่สาธารณะ (int i) {ถ้า (i> = integercache.low && i <= integercache.high) ส่งคืน integercache.cache [i + (-integercache.low)]; ส่งคืนจำนวนเต็มใหม่ (i); } คลาสคงที่คลาสคงที่ integercache {คงที่ int int low = -128; int สุดท้ายคงที่สูง; แคชจำนวนเต็มสุดท้ายคงที่ []; {// ค่าสูงอาจถูกกำหนดค่าโดยคุณสมบัติ int h = 127; String IntegercachehighPropValue = sun.misc.vm.gm.getSavedProperty ("java.lang.integer.integercache.high"); if (IntegerCachehighPropValue! = null) {ลอง {int i = parseInt (IntegerCachehighPropValue); i = math.max (i, 127); // ขนาดอาร์เรย์สูงสุดคือจำนวนเต็ม max_value h = math.min (i, integer.max_value -(-low) -1); } catch (numberFormatexception nfe) {// ถ้าคุณสมบัติไม่สามารถแยกวิเคราะห์เป็น int ได้ให้ละเว้น }} สูง = h; แคช = จำนวนเต็มใหม่ [(สูง - ต่ำ) + 1]; int j = ต่ำ; สำหรับ (int k = 0; k <cache.length; k ++) แคช [k] = จำนวนเต็มใหม่ (j ++); // ช่วง [-128, 127] ต้องฝึกงาน (JLS7 5.1.7) ยืนยัน IntegerCache.high> = 127; } Private IntegerCache () {}} ดังที่แสดงไว้ด้านบนจำนวนเต็มมี integercache คลาสคงที่อยู่ภายในซึ่งคงที่แบบคงที่เริ่มต้นอาร์เรย์จำนวนเต็มที่มี Integer.IntegerCache.low ถึง java.lang.Integer.IntegerCache.high
ช่วงค่าของ java.lang.Integer.IntegerCache.high อยู่ระหว่าง [127~Integer.MAX_VALUE - (-low) -1]
วัตถุทั้งหมดที่ส่งคืนโดยฟังก์ชั่น Integer.valueOf(int) ในช่วงเวลานี้เป็นออฟเซ็ตที่คำนวณตามค่า int และได้รับจาก array Integer.IntegerCache.cache วัตถุเหมือนกันและไม่มีการสร้างวัตถุใหม่
ดังนั้นเมื่อเราแก้ไขค่าของ Integer.valueOf(1) ค่าคืนทั้งหมดของ Integer.IntegerCache.cache[ 1 - IntegerCache.low ] จะเปลี่ยนไป
ฉันเชื่อว่าไอคิวของคุณควรเข้าใจ หากคุณไม่เข้าใจโปรดโทร 10086 ในส่วนความคิดเห็น
ตกลงแล้วชิ้นส่วนที่ไม่ได้อยู่ใน [IntegerCache.low~IntegerCache.high) ?
เห็นได้ชัดว่าพวกเขาโชคดีไม่ได้ถูกแคชโดย Integercache และทุกครั้งที่พวกเขามาถึงพวกเขาจะจัดสรรแผ่นดิน (ภายใน) (การกระจาย) บน JVM
ภวังค์
ถ้าฉันเปลี่ยนพารามิเตอร์ที่แปลงเป็นพิมพ์เป็น int?
public static void testone () โยนข้อยกเว้น {int a = 1, b = 2; Swapone (a, b); System.out.println ("a =" + a + ", b =" + b);} void swapone คงที่ (int a, int b) {// ส่วนที่ต้องใช้} ด้วยทักษะปัจจุบันของผู้เขียนไม่มีวิธีแก้ปัญหา ผู้เชี่ยวชาญสามารถฝากข้อความไว้ในบัญชีอย่างเป็นทางการขอบคุณมาก!
จนถึงตอนนี้ส่วนการแลกเปลี่ยนเสร็จสิ้นแล้ว
1 + 1
ก่อนอื่นมาดูรหัส:
public static void testone () {int one = 1; int สอง = หนึ่ง + หนึ่ง; System.out.printf ("สอง =%d", สอง);} ผลลัพธ์คืออะไร?
หากคุณบอกว่า 2 อย่างแน่นอนคุณได้เรียนรู้อย่างไร้สาระกรุณาโทร 95169 โดยตรง
ฉันสามารถบอกคุณได้ว่ามันอาจเป็นค่าใด ๆ ในช่วงเวลา [Integer.MIN_VALUE~Integer.MAX_VALUE]
ประหลาดใจหรือไม่! อุบัติเหตุหรือไม่! กระตุ้นหรือไม่!
มาหารหัสย่างทีละตัว
ผู้เขียนใช้เครื่องมือ IDE เพื่อถอดรหัสไฟล์. class นี้โดยตรง
public static void testone () {int one = 1; int สอง = หนึ่ง + หนึ่ง; System.out.printf ("สอง =%d", สอง);} ตัวแปรสองที่นี่ไม่ได้เรียกใน teger.valueOf(int) ซึ่งแตกต่างจากที่ฉันจินตนาการ ฉันสงสัยว่านี่เป็นหม้อของ IDE
ดังนั้นตรวจสอบ bytecode ที่รวบรวมอย่างเด็ดขาด ต่อไปนี้เป็นไบต์บางส่วนของข้อความที่ตัดตอนมา:
ldc "two =%d" iconst_1anewarray java/lang/objectdupiconst_0iload 2invokestatic java/lang/integer.valueof (i) ljava/lang/integer; aastoreinvokevirtual java/io/printstream.printf (ljava/lang/string; [ljava/lang/object;) ljava/io/printstream;
จะเห็นได้ว่ามันเป็นหม้อของ IDE ไม่เพียง แต่เป็น Integer.valueOf(int) ที่เรียกว่าครั้งเดียวเท่านั้น แต่ยังมีการสร้างอาร์เรย์ของวัตถุด้วย
รหัส Java ที่สมบูรณ์ควรมีลักษณะเช่นนี้:
public static void testone () {int one = 1; int สอง = หนึ่ง + หนึ่ง; Object [] params = {integer.valueof (สอง)}; System.out.printf ("สอง =%d", params);} ดังนั้นเพียงปรับเปลี่ยนค่าของ Integer.IntegerCache.cache[2+128] ก่อนที่จะเรียกวิธีการดังนั้นเพิ่มรหัสบางส่วนลงในส่วนการเริ่มต้นแบบคงที่ของคลาส
คลาสสาธารณะ OnePlusOne {Static {ลอง {class <?> cacheclazz = class.forName ("java.lang.integer $ IntegerCache"); Field Cachefield = cacheclazz.getDeclaredField ("แคช"); cachefield.setAccessible (จริง); จำนวนเต็ม [] cache = (จำนวนเต็ม []) cachefield.get (null); // เปลี่ยนที่นี่เป็น 1 + 1 = 3 แคช [2 + 128] = จำนวนเต็มใหม่ (3); } catch (exception e) {e.printstacktrace (); }} โมฆะคงที่สาธารณะ testone () {int one = 1; int สอง = หนึ่ง + หนึ่ง; System.out.printf ("สอง =%d", สอง); -สอง == 2?
หลังจากแก้ไขค่าของ Integer.IntegerCache.cache[2 + 128] ตัวแปรสองเท่ากับ 2 หรือไม่?
โมฆะคงที่สาธารณะ testtwo () {int one = 1; int สอง = หนึ่ง + หนึ่ง; System.out.println (สอง == 2); System.out.println (Integer.valueof (สอง) == 2);}เอาต์พุตรหัสข้างต้นมีดังนี้
Truefalse
เนื่องจากสอง == 2 ไม่เกี่ยวข้องกับการแปลงการชกมวยจำนวนเต็มหรือการเปรียบเทียบประเภทดั้งเดิม 2 ประเภทดั้งเดิมจะเท่ากับ 2 เสมอ
รูปแบบที่แท้จริงของ Integer.valueOf(two)==2 คือ Integer.valueOf(two).intValue == 2 นั่นคือ 3 == 2 ดังนั้นจึงเป็นเท็จ
ที่นี่เราจะเห็นได้ว่าหากคุณเปรียบเทียบค่า NULL กับตัวแปรจำนวนเต็มที่มีตัวแปร int ที่มีเครื่องหมายเท่ากันสองเท่าจะมีการโยน nullpointException
เอาต์พุตแบบไหนถ้าวิธีการที่นี่ถูกแทนที่ด้วย System.out.println("Two=" + two) ? คุณสามารถลองได้
postscript
xcache
| ใจดี | มีแคช | ค่าต่ำสุด | ค่าสูงสุด |
|---|---|---|---|
| บูลีน | ไม่มี | - | - |
| ไบต์ | Bytecache | -128 | 127 (คงที่) |
| สั้น | ชอร์ทแคช | -128 | 127 (คงที่) |
| อักขระ | ตัวละคร | 0 | 127 (คงที่) |
| จำนวนเต็ม | Integercache | -128 | java.lang.integer.integercache.high |
| ยาว | longcache | -128 | 127 (คงที่) |
| ลอย | ไม่มี | - | - |
| สองเท่า | ไม่มี | - | - |
java.lang.integer.integercache.high
หลังจากอ่านวิธีการรับสูง sun.misc.VM.getSavedProperty คลาส Integercache คุณอาจมีคำถามต่อไปนี้ เราจะไม่ล่าช้าและใช้วิธีการตอบคำถามหนึ่งข้อ
1. จะส่งค่านี้ไปยัง JVM ได้อย่างไร?
เช่นเดียวกับคุณสมบัติของระบบเมื่อ JVM เริ่มต้นมันจะถูกส่งผ่านโดยการตั้งค่า -Djava.lang.Integer.IntegerCache.high=xxx
2. ความแตกต่างระหว่างวิธีนี้กับ System.getProperty คืออะไร?
เพื่อแยกแยะพารามิเตอร์ที่ระบบ JVM ต้องการจากพารามิเตอร์ที่ผู้ใช้ใช้
เมื่อ java.lang.System.initializeSystemClass เริ่มต้นพารามิเตอร์เริ่มต้นจะถูกบันทึกไว้ในสองสถานที่:
2.1 พารามิเตอร์ระบบที่ได้รับจาก JVM ทั้งหมดจะถูกบันทึกไว้ใน sun.misc.vm.savedprops
เมื่อ JVM เริ่มต้นจะเรียก java.lang.System.initializeSystemClass วิธีการเริ่มต้นคุณสมบัติ
ในเวลาเดียวกันวิธีการ sun.misc.VM.saveAndRemoveProperties จะถูกเรียกให้ลบคุณสมบัติต่อไปนี้ออกจาก java.lang.System.props :
คุณสมบัติที่ระบุไว้ข้างต้นเป็นพารามิเตอร์ระบบทั้งหมดที่จำเป็นต้องตั้งค่าสำหรับการเริ่มต้น JVM ดังนั้นสำหรับการพิจารณาความปลอดภัยและการพิจารณาการแยกให้แยกออกจากระบบที่ผู้ใช้เข้าถึงได้
2.2 บันทึกพารามิเตอร์อื่น ๆ ยกเว้นพารามิเตอร์ต่อไปนี้ที่จำเป็นสำหรับการเริ่มต้น JVM ใน java.lang.system.props
PS: JDK 1.8.0_91 ใช้โดยผู้แต่ง
Integercache สำหรับ Java 9
ลองนึกภาพว่าการเล่นเกมซุกซนข้างต้นปรากฏในแพ็คเกจการพึ่งพาของบุคคลที่สามจะมีกลุ่มโปรแกรมเมอร์ที่จะบ้าไปแล้ว (โปรดอย่าลองเล่นเกมที่ไม่ดีผลที่ตามมานั้นร้ายแรงมาก)
โชคดีที่ Java 9 ได้ จำกัด สิ่งนี้ คุณสามารถเขียนไฟล์โมดูล -info.java ในโมดูลที่เกี่ยวข้องซึ่ง จำกัด การใช้การสะท้อนกลับไปยังสมาชิก ฯลฯ หลังจากการประกาศตามต้องการรหัสสามารถเข้าถึงฟิลด์วิธีการและข้อมูลอื่น ๆ ที่สามารถเข้าถึงได้โดยการสะท้อนกลับ เฉพาะเมื่อคลาสอยู่ในโมดูลเดียวกันหรือโมดูลได้เปิดแพ็คเกจสำหรับการเข้าถึงการสะท้อนกลับ สำหรับรายละเอียดโปรดดูบทความ:
แก้ไข Integercache ใน Java 9?
ขอบคุณ Lydia และ Asuka สำหรับคำแนะนำที่มีค่าและการทำงานอย่างหนักในการพิสูจน์อักษร
ในที่สุดฉันต้องการแบ่งปันปัญหาที่ฉันไม่ได้ใส่ใจกับค่าจำนวนเต็มใน Java:
ก่อนอื่นดูตัวอย่างโค้ด:
โมฆะคงที่สาธารณะหลัก (String [] args) {จำนวนเต็ม A1 = Integer.ValueOf (60); // Danielinbiti Integer B1 = 60; System.out.println ("1: ="+(a1 == b1)); จำนวนเต็ม A2 = 60; จำนวนเต็ม B2 = 60; System.out.println ("2: ="+(a2 == b2)); จำนวนเต็ม A3 = จำนวนเต็มใหม่ (60); จำนวนเต็ม B3 = 60; System.out.println ("3: ="+(a3 == b3)); จำนวนเต็ม A3 = จำนวนเต็มใหม่ (60); จำนวนเต็ม B3 = 60; System.out.println ("3: ="+(a3 == b3)); จำนวนเต็ม A4 = 129; จำนวนเต็ม B4 = 129; System.out.println ("4: ="+(a4 == b4)); - หากผลการเปรียบเทียบของรหัสนี้ไม่ได้ดำเนินการฉันไม่รู้ว่าคำตอบคืออะไรในใจของคุณ
หากต้องการทราบคำตอบนี้มันเกี่ยวข้องกับปัญหา Java Buffer และ HEAP
ใน Java ประเภทจำนวนเต็มเป็นบัฟเฟอร์สำหรับตัวเลขระหว่าง -128-127 ดังนั้นจึงสอดคล้องกับเครื่องหมายที่เท่ากัน แต่สำหรับตัวเลขที่ไม่ได้อยู่ในช่วงนี้พวกเขาใหม่ในกอง ดังนั้นพื้นที่ที่อยู่จึงแตกต่างกันดังนั้นจึงไม่เท่ากัน
Integer b3=60 นี่คือกระบวนการบรรจุนั่นคือ Integer b3=Integer.valueOf(60)
ดังนั้นในอนาคตเมื่อคุณพบจำนวนเต็มคุณต้องใช้ intValue() เพื่อเปรียบเทียบว่าค่าเท่ากันหรือไม่
ไม่มีบัฟเฟอร์สำหรับสองเท่า
คำตอบ
1: = จริง
2: = จริง
3: = เท็จ
4: = เท็จ
สรุป
ข้างต้นเป็นเนื้อหาทั้งหมดของบทความนี้ ฉันหวังว่าเนื้อหาของบทความนี้จะมีค่าอ้างอิงบางอย่างสำหรับการศึกษาหรือที่ทำงานของทุกคน หากคุณมีคำถามใด ๆ คุณสามารถฝากข้อความไว้เพื่อสื่อสาร ขอบคุณสำหรับการสนับสนุน Wulin.com