สวัสดี! เมื่อคุณก้าวหน้าผ่าน CodeGym คุณเคยพบกับประเภทดั้งเดิมหลายครั้ง นี่คือรายการสั้น ๆ ของสิ่งที่เรารู้เกี่ยวกับพวกเขา:
- ไม่ใช่วัตถุและเป็นตัวแทนของค่าที่เก็บไว้ในหน่วยความจำ
- มีหลายชนิด
- จำนวนเต็ม: ไบต์ , สั้น , int , ยาว
- ตัวเลขทศนิยม (เศษส่วน): ลอยและสองเท่า
- ค่าตรรกะ: บูลีน
- ค่าสัญลักษณ์ (สำหรับแสดงตัวอักษรและตัวเลข): ถ่าน
-
แต่ละประเภทมีช่วงค่าของตัวเอง:
ประเภทดึกดำบรรพ์ |
ขนาดในหน่วยความจำ |
ช่วงค่า |
ไบต์ |
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 บิต นี่คือการแสดงเลขฐานสองของมัน:
เราเขียนค่านี้เป็น ตัวแปร
สั้นๆเก็บได้เพียง 16 บิตเท่านั้น! ดังนั้นหมายเลขของเรา 16 บิตแรกเท่านั้นที่จะถูกย้ายไปที่นั่น ส่วนที่เหลือจะถูกทิ้ง เป็นผลให้ตัวแปรสั้นได้รับค่าต่อไปนี้
ซึ่งอยู่ในรูปทศนิยมเท่ากับ -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 :) อย่างที่คุณเห็น อัลกอริทึมที่นี่ค่อนข้างง่าย แต่คุณไม่ควรลืมมัน .
GO TO FULL VERSION