CodeGym /จาวาบล็อก /สุ่ม /การขยับขยายและการจำกัดประเภทดั้งเดิม
John Squirrels
ระดับ
San Francisco

การขยับขยายและการจำกัดประเภทดั้งเดิม

เผยแพร่ในกลุ่ม
สวัสดี! เมื่อคุณก้าวหน้าผ่าน CodeGym คุณเคยพบกับประเภทดั้งเดิมหลายครั้ง นี่คือรายการสั้น ๆ ของสิ่งที่เรารู้เกี่ยวกับพวกเขา:
  1. ไม่ใช่วัตถุและเป็นตัวแทนของค่าที่เก็บไว้ในหน่วยความจำ
  2. มีหลายชนิด
    • จำนวนเต็ม: ไบต์ , สั้น , int , ยาว
    • ตัวเลขทศนิยม (เศษส่วน): ลอยและสองเท่า
    • ค่าตรรกะ: บูลีน
    • ค่าสัญลักษณ์ (สำหรับแสดงตัวอักษรและตัวเลข): ​​ถ่าน
  3. แต่ละประเภทมีช่วงค่าของตัวเอง:

ประเภทดึกดำบรรพ์ ขนาดในหน่วยความจำ ช่วงค่า
ไบต์ 8 บิต -128 ถึง 127
สั้น 16 บิต -32768 ถึง 32767
ถ่าน 16 บิต 0 ถึง 65536
นานาชาติ 32 บิต -2147483648 ถึง 2147483647
ยาว 64 บิต -9223372036854775808 ถึง 9223372036854775807
ลอย 32 บิต (2 ยกกำลัง -149) ถึง ((2 - (2 ยกกำลัง -23)) * 2 ยกกำลัง 127)
สองเท่า 64 บิต (-2 ยกกำลัง 63) ถึง ((2 ยกกำลัง 63) - 1)
บูลีน 8 (เมื่อใช้ในอาร์เรย์), 32 (หากไม่ได้ใช้ในอาร์เรย์) จริงหรือเท็จ
แต่นอกจากจะมีค่าต่างกันแล้ว ยังต่างกันตรงที่มีพื้นที่ว่างในหน่วยความจำด้วย int ใช้ เวลามากกว่าหนึ่งไบต์ และยาวใหญ่กว่าสั้น จำนวนหน่วยความจำที่ถูกครอบครองโดยวัตถุโบราณสามารถเปรียบเทียบได้กับตุ๊กตาทำรังของรัสเซีย: การขยับขยายและการจำกัดประเภทดั้งเดิมให้แคบลง - 2 ตุ๊กตาทำรังแต่ละตัวมีพื้นที่ว่างภายใน ยิ่งตุ๊กตาทำรังมีขนาดใหญ่เท่าใดก็ยิ่งมีพื้นที่มากขึ้นเท่านั้น ตุ๊กตาทำรังขนาดใหญ่ ( long ) จะรองรับint ที่เล็กกว่า ได้ ง่าย มันพอดีและคุณไม่จำเป็นต้องทำอะไรอีก ใน Java เมื่อทำงานกับ primitives สิ่งนี้เรียกว่าการแปลงโดยปริยาย หรือเรียกต่างกันเรียกว่าขยับขยาย

การขยับขยายใน Java

นี่คือตัวอย่างง่ายๆ ของการแปลงที่กว้างขึ้น:

public class Main {

   public static void main(String[] args) {
      
       int bigNumber = 10000000;

       byte littleNumber = 16;

       bigNumber = littleNumber;
       System.out.println(bigNumber);
   }
}
ที่นี่เรากำหนดค่าไบต์ให้กับตัวแปรint การกำหนดสำเร็จโดยไม่มีปัญหา: ค่าที่จัดเก็บไว้ในหนึ่งไบต์ใช้หน่วยความจำน้อยกว่าที่intสามารถรองรับได้ ตุ๊กตาทำรังตัวเล็ก (ค่าไบต์) ใส่ในตุ๊กตาทำรังตัวใหญ่ ( ตัวแปรint ) ได้อย่างง่ายดาย เป็นเรื่องที่แตกต่างออกไปหากคุณพยายามทำสิ่งที่ตรงกันข้าม เช่น ใส่ค่าขนาดใหญ่ลงในตัวแปรที่มีช่วงไม่สามารถรองรับประเภทข้อมูลขนาดใหญ่ได้ สำหรับตุ๊กตาทำรังจริง ๆ จำนวนนั้นไม่พอดี ด้วย Java สามารถทำได้ แต่มีความแตกต่าง ลองใส่intลงใน ตัวแปร แบบสั้น :

public static void main(String[] args) {

   int bigNumber = 10000000;
  
   short littleNumber = 1000;

   littleNumber = bigNumber;// Error!
   System.out.println(bigNumber);
}
ข้อผิดพลาด! คอมไพเลอร์เข้าใจว่าคุณกำลังพยายามทำสิ่งผิดปกติโดยการผลักตุ๊กตาทำรังขนาดใหญ่ ( int ) เข้าไปในตุ๊กตาตัวเล็ก ( short ) ในกรณีนี้ ข้อผิดพลาดในการคอมไพล์คือคำเตือนจากคอมไพเลอร์: "เฮ้ คุณแน่ใจจริงๆ เหรอว่าต้องการทำสิ่งนี้" หากคุณแน่ใจ ให้คุณบอกคอมไพเลอร์ว่า: "ทุกอย่างเรียบร้อยดี ฉันรู้ว่าฉันกำลังทำอะไรอยู่!" กระบวนการนี้เรียกว่าการแปลงประเภทที่ชัดเจนหรือการทำให้แคบลง

การทำให้แคบลงใน Java

หากต้องการแปลงค่าให้แคบลง คุณต้องระบุประเภทที่คุณต้องการแปลงมูลค่าให้ชัดเจน กล่าวอีกนัยหนึ่ง คุณต้องตอบคำถามของคอมไพเลอร์: "เอาล่ะ ตุ๊กตาทำรังตัวเล็กๆ ตัวไหนที่คุณต้องการใส่ตุ๊กตาทำรังตัวใหญ่นี้ลงไป" ในกรณีของเรา ดูเหมือนว่า:

public static void main(String[] args) {

   int bigNumber = 10000000;

   short littleNumber = 1000;

   littleNumber = (short) bigNumber;
   System.out.println(littleNumber);
}
เราระบุอย่างชัดเจนว่าเราต้องการใส่intลงใน ตัวแปร สั้น ๆและเราจะรับผิดชอบ เมื่อเห็นว่ามีการระบุประเภทที่แคบลงอย่างชัดเจน คอมไพเลอร์จึงทำการแปลง ผลลัพธ์คืออะไร? เอาต์พุตคอนโซล: -27008 นั่นเป็นสิ่งที่ไม่คาดคิดเล็กน้อย ทำไมเราถึงได้รับสิ่งนั้น? ในความเป็นจริงมันง่ายมาก เดิมค่าคือ 10000000 มันถูกเก็บไว้ใน ตัวแปร intซึ่งใช้พื้นที่ 32 บิต นี่คือการแสดงเลขฐานสองของมัน:
การขยับขยายและการจำกัดประเภทดั้งเดิมให้แคบลง - 3
เราเขียนค่านี้เป็น ตัวแปร สั้นๆเก็บได้เพียง 16 บิตเท่านั้น! ดังนั้นหมายเลขของเรา 16 บิตแรกเท่านั้นที่จะถูกย้ายไปที่นั่น ส่วนที่เหลือจะถูกทิ้ง เป็นผลให้ตัวแปรสั้นได้รับค่าต่อไปนี้
การขยับขยายและการจำกัดประเภทดั้งเดิมให้แคบลง - 4
ซึ่งอยู่ในรูปทศนิยมเท่ากับ -27008 นั่นคือเหตุผลที่คอมไพเลอร์ขอให้คุณ "ยืนยัน" โดยระบุการแปลงที่แคบลงอย่างชัดเจนเป็นประเภทเฉพาะ ประการแรก นี่แสดงว่าคุณกำลังรับผิดชอบต่อผลลัพธ์ที่ได้ และอย่างที่สอง มันบอกคอมไพเลอร์ว่าจะจัดสรรพื้นที่ว่างเท่าใดเมื่อเกิดการแปลง ท้ายที่สุด ในตัวอย่างที่แล้ว ถ้าเรากำหนดค่า int ให้กับตัวแปร byte แทนที่จะเป็นshortเราจะเหลือแค่ 8 บิต ไม่ใช่ 16 และผลลัพธ์ที่ได้ก็จะแตกต่างออกไป ประเภทเศษส่วน ( floatและdouble ) มีกระบวนการของตัวเองในการจำกัดการแปลง หากคุณพยายามแปลงจำนวนแฟกชันเป็นประเภทจำนวนเต็ม ส่วนที่เป็นเศษส่วนจะถูกละทิ้ง

public static void main(String[] args) {

   double d = 2.7;

   long x = (int) d;
   System.out.println(x);
}
เอาต์พุตคอนโซล: 2

ถ่าน

คุณรู้อยู่แล้วว่าcharใช้เพื่อแสดงอักขระแต่ละตัว

public static void main(String[] args) {

   char c = '!';
   char z = 'z';
   char i = '8';
  
}
แต่ประเภทข้อมูลนี้มีคุณลักษณะหลายอย่างที่สำคัญที่ต้องทำความเข้าใจ มาดูตารางช่วงค่ากันอีกครั้ง:
ประเภทดึกดำบรรพ์ ขนาดในหน่วยความจำ ช่วงค่า
ไบต์ 8 บิต -128 ถึง 127
สั้น 16 บิต -32768 ถึง 32767
ถ่าน 16 บิต 0 ถึง 65536
นานาชาติ 32 บิต -2147483648 ถึง 2147483647
ยาว 64 บิต -9223372036854775808 ถึง 9223372036854775807
ลอย 32 บิต (2 ยกกำลัง -149) ถึง ((2 - (2 ยกกำลัง -23)) * 2 ยกกำลัง 127)
สองเท่า 64 บิต (-2 ยกกำลัง 63) ถึง ((2 ยกกำลัง 63) - 1)
บูลีน 8 (เมื่อใช้ในอาร์เรย์), 32 (หากไม่ได้ใช้ในอาร์เรย์) จริงหรือเท็จ
ช่วง 0 ถึง 65536 ถูกระบุสำหรับประเภทถ่าน แต่นั่นหมายความว่าอย่างไร? ท้ายที่สุดแล้วcharไม่เพียงแค่แทนตัวเลขเท่านั้น แต่ยังรวมถึงตัวอักษร เครื่องหมายวรรคตอนด้วย... ประเด็นก็คือ ค่า char ใน Java นั้น ถูกจัดเก็บในรูปแบบ Unicode เราพบ Unicode ในบทเรียนก่อนหน้านี้แล้ว คุณคงจำได้ว่า Unicode เป็นมาตรฐานการเข้ารหัสอักขระที่มีสัญลักษณ์ของภาษาเขียนเกือบทั้งหมดในโลก กล่าวอีกนัยหนึ่งคือรายการรหัสพิเศษที่แสดงถึงอักขระเกือบทุกตัวในทุกภาษา ตาราง Unicode ทั้งหมดมีขนาดใหญ่มากและแน่นอนว่าไม่จำเป็นต้องเรียนรู้ด้วยใจจริง นี่คือส่วนเล็ก ๆ ของมัน: การขยับขยายและการจำกัดประเภทดั้งเดิมให้แคบลง - 5 สิ่งสำคัญคือต้องเข้าใจวิธีจัดเก็บตัวอักษร และจำไว้ว่าถ้าคุณรู้รหัสสำหรับอักขระใดอักขระหนึ่ง คุณสามารถสร้างอักขระนั้นในโปรแกรมของคุณได้เสมอ มาลองสุ่มตัวเลขกัน:

public static void main(String[] args) {

   int x = 32816;

   char c = (char) x ;
   System.out.println(c);
}
เอาต์พุตคอนโซล: 耰 นี่คือรูปแบบที่ใช้เก็บchar s ใน Java แต่ละสัญลักษณ์สอดคล้องกับตัวเลข: รหัสตัวเลข 16 บิต (สองไบต์) ใน Unicode 32816 ตรงกับอักขระจีน 耰 สังเกตประเด็นต่อไปนี้ ในตัวอย่างนี้ เราใช้ตัวแปรint มันใช้หน่วยความจำ 32 บิตในขณะที่ถ่านใช้ 16 บิต ที่นี่เราเลือกintเพราะหมายเลขของเรา (32816) ไม่พอดีกับshort แม้ว่าขนาดของอักขระ (เช่นเดียวกับshort ) คือ 16 บิต แต่ไม่มีตัวเลขลบใน ช่วง อักขระดังนั้นส่วน "บวก" ของอักขระช่วงกว้างเป็นสองเท่า (65536 แทน 32767 สำหรับ ประเภท สั้น ) เราสามารถใช้intได้ตราบใดที่รหัสของเรายังคงต่ำกว่า 65536 แต่ถ้าคุณสร้าง ค่า intที่มากกว่า 65536 ก็จะใช้มากกว่า 16 บิต และจะส่งผลให้การแปลงแคบลง

char c = (char) x;
บิตพิเศษจะถูกยกเลิก (ตามที่กล่าวไว้ข้างต้น) และผลลัพธ์จะค่อนข้างคาดไม่ถึง

คุณสมบัติพิเศษของการบวกตัวอักษรและจำนวนเต็ม

ลองดูตัวอย่างที่ผิดปกติ:

public class Main {

   public static void main(String[] args) {

      char c = '1';

      int i = 1;

       System.out.println(i + c);
   }
}
เอาต์พุตคอนโซล: 50 O_О มีเหตุผลอย่างไร 1+1. 50 มาจากไหน! คุณรู้อยู่แล้วว่าcharค่าถูกเก็บไว้ในหน่วยความจำเป็นตัวเลขในช่วงตั้งแต่ 0 ถึง 65536 และตัวเลขเหล่านี้เป็นตัวแทน Unicode ของอักขระ การขยับขยายและการจำกัดประเภทดั้งเดิมให้แคบลง - 6 เมื่อเราเพิ่มอักขระและประเภทจำนวนเต็มอักขระ นั้น จะถูกแปลงเป็นตัวเลข Unicode ที่สอดคล้องกัน ในรหัสของเรา เมื่อเราเพิ่ม 1 และ '1' สัญลักษณ์ '1' จะถูกแปลงเป็นรหัสของมันเอง ซึ่งก็คือ 49 (คุณสามารถตรวจสอบได้ในตารางด้านบน) ดังนั้น ผลลัพธ์คือ 50 ลองยกตัวอย่างเพื่อนเก่าของเรา 耰 อีกครั้ง แล้วลองบวกเข้ากับตัวเลขดู

public static void main(String[] args) {

   char c = '耰';
   int x = 200;

   System.out.println(c + x);
}
เอาต์พุตคอนโซล: 33016 เราค้นพบแล้วว่า 耰 ตรงกับ 32816 และเมื่อเราบวกเลขนี้กับ 200 เราจะได้ผลลัพธ์: 33016 :) อย่างที่คุณเห็น อัลกอริทึมที่นี่ค่อนข้างง่าย แต่คุณไม่ควรลืมมัน .
ความคิดเห็น
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION