สวัสดี! คุณค่อนข้างคุ้นเคยกับประเภทดั้งเดิมอยู่แล้ว และเคยทำงานกับมันมาบ้างแล้ว
![การห่อ การแกะ และการบรรจุ - 1]()
พื้นฐานในการเขียนโปรแกรมและโดยเฉพาะ Java มีข้อดีหลายประการ: ใช้หน่วยความจำน้อย จึงเพิ่มประสิทธิภาพของโปรแกรม และแบ่งออกเป็นช่วงของค่าอย่างชัดเจน อย่างไรก็ตาม ในกระบวนการเรียนรู้ Java เราได้ทำซ้ำมากกว่าหนึ่งครั้ง เหมือนมนต์ “
ใน Java ทุกอย่างเป็นวัตถุ ” แต่คำดั้งเดิมเป็นการหักล้างคำเหล่านี้โดยตรง พวกเขาไม่ใช่วัตถุ ดังนั้นหลักการ “ทุกสิ่งเป็นวัตถุ” จึงผิดใช่ไหม? ไม่เชิง. ใน Java primitive type ทุกประเภทมีพี่น้องฝาแฝด ซึ่งเป็น
คลาส wrapper (
Wrapper
) กระดาษห่อคืออะไร?
wrapper เป็นคลาสพิเศษที่เก็บค่าของดั้งเดิมไว้ภายในตัวมันเอง แต่เนื่องจากนี่คือคลาส จึงสามารถสร้างอินสแตนซ์ของตัวเองได้ พวกเขาจะเก็บค่าดั้งเดิมที่จำเป็นไว้ข้างในในขณะที่เป็นวัตถุจริง ชื่อของคลาส wrapper นั้นคล้ายคลึงกับชื่อของคลาสพื้นฐานที่เกี่ยวข้องมาก หรือเหมือนกันโดยสิ้นเชิง ดังนั้นจึงง่ายต่อการจดจำ
คลาส Wrapper สำหรับประเภทข้อมูลดั้งเดิม |
ประเภทข้อมูลดั้งเดิม |
คลาส Wrapper |
ภายใน |
จำนวนเต็ม |
สั้น |
สั้น |
ยาว |
ยาว |
ไบต์ |
ไบต์ |
ลอย |
ลอย |
สองเท่า |
สองเท่า |
ถ่าน |
อักขระ |
บูลีน |
บูลีน |
อ็อบเจ็กต์คลาส Wrapper ถูกสร้างขึ้นเหมือนกับที่อื่น:
public static void main(String[] args) {
Integer i = new Integer(682);
Double d = new Double(2.33);
Boolean b = new Boolean(false);
}
คลาส Wrapper ช่วยให้คุณสามารถลดข้อเสียของประเภทดั้งเดิมได้ สิ่งที่ชัดเจนที่สุดคือ
แบบดั้งเดิมไม่มีวิธีการ ตัวอย่างเช่น ไม่มีเมธอด
toString()
ดังนั้น คุณจึงไม่สามารถแปลงตัวเลข
int
เป็นสตริงได้ แต่ด้วยคลาส wrapper
Integer
มันเป็นเรื่องง่าย
public static void main(String[] args) {
Integer i = new Integer(432);
String s = i.toString();
}
จะมีปัญหากับการแปลงแบบย้อนกลับด้วย สมมติว่าเรามีสตริงที่เรารู้ว่ามีตัวเลขอยู่ด้วย อย่างไรก็ตาม ในกรณีของประเภทดั้งเดิม
int
เราจะไม่สามารถรับหมายเลขนี้จากสตริงและเปลี่ยนให้เป็นตัวเลขได้ แต่ต้องขอบคุณคลาส wrapper ที่ทำให้เรามีโอกาสนี้แล้ว
public static void main(String[] args) {
String s = "1166628";
Integer i = Integer.parseInt(s);
System.out.println(i);
}
เอาท์พุต: 1166628 เราดึงตัวเลขจากสตริงและกำหนดให้กับตัวแปรอ้างอิงได้
Integer i
สำเร็จ โดยวิธีการเกี่ยวกับลิงค์ คุณรู้อยู่แล้วว่าพารามิเตอร์ถูกส่งผ่านไปยังวิธีการต่างๆ หลายวิธี: ค่าพื้นฐานจะถูกส่งผ่านด้วยค่า และวัตถุจะถูกส่งผ่านโดยการอ้างอิง คุณสามารถใช้ความรู้นี้เมื่อสร้างวิธีการของคุณ: หากวิธีการของคุณใช้งานได้ เช่น ใช้จำนวนเศษส่วน แต่คุณต้องการตรรกะในการส่งผ่านการอ้างอิง คุณสามารถส่งพารามิเตอร์ไปยังวิธีการ
Double/Float
แทน
double/float
ได้ นอกจากนี้ นอกเหนือจากวิธีการแล้ว คลาส wrapper ยังมีฟิลด์คงที่ซึ่งสะดวกต่อการใช้งานมาก ตัวอย่างเช่น ลองนึกภาพว่าคุณกำลังเผชิญกับงาน:
พิมพ์จำนวนสูงสุดที่เป็นไปได้ไปยังคอนโซล int
จากนั้นพิมพ์จำนวนขั้นต่ำที่เป็นไปได้ งานนี้ดูเหมือนจะเป็นงานเบื้องต้น แต่คุณคงไม่สามารถทำได้หากไม่มี Google และคลาส wrapper ช่วยให้คุณสามารถแก้ไข “ปัญหาในชีวิตประจำวัน” ต่อไปนี้ได้อย่างง่ายดาย:
public class Main {
public static void main(String[] args) {
System.out.println(Integer.MAX_VALUE);
System.out.println(Integer.MIN_VALUE);
}
}
สาขาดังกล่าวช่วยให้คุณไม่เสียสมาธิจากงานที่จริงจังกว่านี้ ไม่ต้องพูดถึงความจริงที่ว่าในกระบวนการพิมพ์หมายเลข
2147483647 (นี่คือ MAX_VALUE พอดี) จึงไม่น่าแปลกใจที่จะพิมพ์ผิด :) นอกจากนี้ในการบรรยายครั้งหนึ่งก่อนหน้านี้เราได้ดึงความสนใจไปที่ความจริงที่ว่า
วัตถุของคลาส wrapper ไม่เปลี่ยนรูป (Immutable) .
public static void main(String[] args) {
Integer a = new Integer(0);
Integer b = new Integer(0);
b = a;
a = 1;
System.out.println(b);
}
เอาท์พุต: 0 วัตถุที่การอ้างอิงเดิมชี้ไป
а
ไม่ได้เปลี่ยนสถานะ ไม่เช่นนั้นค่า
b
ก็จะเปลี่ยนไปเช่นกัน เช่นเดียวกับ
String
แทนที่จะเปลี่ยนสถานะของออบเจ็กต์ wrapper ออบเจ็กต์ใหม่ทั้งหมดจะถูกสร้างขึ้นในหน่วยความจำ เหตุใดผู้สร้าง Java จึงตัดสินใจเก็บประเภทดั้งเดิมไว้ในภาษาในที่สุด เนื่องจากทุกสิ่งควรเป็นวัตถุ และเรามีคลาส wrapper อยู่แล้วที่สามารถใช้เพื่อแสดงทุกสิ่งที่ primitive แสดงออก ทำไมไม่ปล่อยมันไว้ในภาษาและลบ primitive ออกไปล่ะ? คำตอบนั้นง่าย - ประสิทธิภาพ ประเภทดั้งเดิมเรียกว่าดั้งเดิมเนื่องจากไม่มีคุณลักษณะ "หนัก" มากมายของวัตถุ ใช่ วัตถุมีวิธีการที่สะดวกมากมาย แต่คุณไม่จำเป็นต้องใช้มันเสมอไป บางครั้งคุณแค่ต้องการตัวเลข 33 หรือ 2.62 หรือค่าของ
true
/
false
ในสถานการณ์ที่ประโยชน์ของอ็อบเจ็กต์ไม่เกี่ยวข้องและไม่จำเป็นสำหรับการทำงานของโปรแกรม primitive จะทำงานได้ดีขึ้นมาก
บรรจุอัตโนมัติ/เปิดออกอัตโนมัติ
หนึ่งในคุณสมบัติของ primitive และคลาส wrapper ใน Java คือ autoboxing/autounboxing
![การห่อ การแกะ และการบรรจุ - 2]()
มาทำความเข้าใจแนวคิดนี้กันดีกว่า ดังที่คุณและฉันได้เรียนรู้มาก่อนหน้านี้แล้ว Java เป็นภาษาเชิงวัตถุ ซึ่งหมายความว่าโปรแกรมทั้งหมดที่เขียนด้วย Java ประกอบด้วยวัตถุ ดั้งเดิมไม่ใช่วัตถุ อย่างไรก็ตาม ตัวแปรคลาส wrapper สามารถกำหนดค่าเป็นประเภทดั้งเดิมได้ กระบวนการนี้เรียกว่า
ออโต้บ็อกซ์ ในทำนองเดียวกัน ตัวแปรประเภทดั้งเดิมสามารถกำหนดให้กับอ็อบเจ็กต์ของคลาส wrapper ได้
กระบวนการนี้เรียกว่า autounboxing ตัวอย่างเช่น:
public class Main {
public static void main(String[] args) {
int x = 7;
Integer y = 111;
x = y;
y = x * 123;
}
}
ในบรรทัดที่ 5 เรากำหนดค่าดั้งเดิม x ของ y ซึ่งเป็นอ็อบเจ็กต์ของคลาส
Integer
wrapper อย่างที่คุณเห็นไม่จำเป็นต้องดำเนินการใด ๆ เพิ่มเติมสำหรับสิ่งนี้:
คอมไพเลอร์รู้สิ่งนั้นint
และInteger
อันที่จริงเป็นสิ่งเดียวกัน นี่คือการเปิดออกอัตโนมัติ สิ่งเดียวกันนี้เกิดขึ้นกับ autoboxing ในบรรทัดที่ 6: object y สามารถกำหนดค่าของ primitives ได้อย่างง่ายดาย (x*123) นี่คือตัวอย่างของการบรรจุอัตโนมัติ นี่คือสาเหตุที่เพิ่มคำว่า "อัตโนมัติ":
เพื่อกำหนดการอ้างอิงดั้งเดิมให้กับอ็อบเจ็กต์ของคลาส wrapper (และในทางกลับกัน) คุณไม่จำเป็นต้องทำอะไรเลย ทุกอย่างจะเกิดขึ้นโดยอัตโนมัติ สะดวกใช่ไหม? :) ความสะดวกสบายที่ยอดเยี่ยมอีกประการหนึ่งของการบรรจุอัตโนมัติ/การเปิดบรรจุภัณฑ์อัตโนมัตินั้นแสดงออกมาในการทำงานของวิธีการต่างๆ ความจริงก็คือ
พารามิเตอร์ของวิธีการยังขึ้นอยู่กับการบรรจุอัตโนมัติและการเปิดบรรจุภัณฑ์อัตโนมัติด้วย และตัวอย่างเช่น ถ้าหนึ่งในนั้นรับวัตถุสองชิ้นเป็นอินพุต
Integer
เราก็สามารถส่งผ่านวัตถุดั้งเดิมตรงนั้นได้อย่างง่ายดาย
int
!
public class Main {
public static void main(String[] args) {
printNumber(7);
}
public static void printNumber(Integer i) {
System.out.println("You entered a number" + i);
}
}
ผลลัพธ์: คุณป้อนหมายเลข 7 มันได้ผลในทางกลับกัน:
public class Main {
public static void main(String[] args) {
printNumber(new Integer(632));
}
public static void printNumber(int i) {
System.out.println("You entered a number" + i);
}
}
จุดสำคัญที่ต้องจำ:
การทำ autoboxing และ unboxing ใช้ไม่ได้กับ arrays !
public class Main {
public static void main(String[] args) {
int[] i = {1,2,3,4,5};
printArray(i);
}
public static void printArray(Integer[] arr) {
System.out.println(Arrays.toString(arr));
}
}
การพยายามส่งผ่านอาร์เรย์ของดั้งเดิมไปยังวิธีการที่รับอาร์เรย์ของอ็อบเจ็กต์เป็นอินพุตจะทำให้เกิดข้อผิดพลาดในการคอมไพล์ สุดท้ายนี้ เราจะมาเปรียบเทียบ primitive และ wrappers
primitives กันอีกครั้งโดยย่อ:
- มีความได้เปรียบด้านประสิทธิภาพ
กระดาษห่อ:
- พวกเขาอนุญาตให้คุณไม่ละเมิดหลักการ "ทุกสิ่งเป็นวัตถุ" เพื่อให้ตัวเลขสัญลักษณ์และค่าบูลีนจริง/เท็จไม่หลุดออกจากแนวคิดนี้
- ขยายความสามารถในการทำงานกับค่าเหล่านี้โดยจัดเตรียมวิธีการและสาขาที่สะดวก
- จำเป็นเมื่อวิธีการบางอย่างสามารถทำงานได้กับวัตถุโดยเฉพาะ
GO TO FULL VERSION