คำตอบข้อหนึ่ง:
หนึ่งในคำถามสัมภาษณ์ Java ที่ได้รับความนิยมมากที่สุดคือ: อะไรคือวัตถุที่ไม่เปลี่ยนรูป (วัตถุที่ไม่เปลี่ยนรูป) ประโยชน์ของวัตถุที่ไม่เปลี่ยนรูปคืออะไร และควรใช้ภายใต้สถานการณ์ใด หรือเจาะจงกว่านั้น เหตุใดจึงควรตั้งค่าคลาส String ของ Java เป็น ประเภทที่ไม่เปลี่ยนรูป?
วัตถุที่ไม่เปลี่ยนรูปตามชื่อคือวัตถุที่ไม่สามารถเปลี่ยนแปลงได้หลังจากการสร้าง ตัวอย่างทั่วไปคือคลาส String ใน Java
คัดลอกรหัสรหัสดังต่อไปนี้:
สตริง s = "เอบีซี";
s.toLowerCase();
ดังที่กล่าวข้างต้น s.toLowerCase() จะไม่เปลี่ยนค่าของ "ABC" แต่สร้างคลาส String ใหม่ "abc" จากนั้นชี้อินสแตนซ์ใหม่ไปที่ตัวแปร s
วัตถุที่ไม่เปลี่ยนรูปมีข้อดีมากกว่าวัตถุที่ไม่เปลี่ยนรูปหลายประการ:
1) วัตถุที่ไม่เปลี่ยนรูปสามารถปรับปรุงประสิทธิภาพและความปลอดภัยของ String Pool หากคุณรู้ว่าวัตถุไม่เปลี่ยนรูป ดังนั้นเมื่อคุณต้องการคัดลอกเนื้อหาของวัตถุ คุณไม่จำเป็นต้องคัดลอกตัวมันเอง แต่เพียงที่อยู่ของวัตถุเท่านั้น การคัดลอกที่อยู่ (โดยปกติจะมีขนาดของตัวชี้) ต้องใช้หน่วยความจำน้อยมาก และมีประสิทธิภาพมาก โดยจะไม่ส่งผลกระทบต่อตัวแปรอื่นๆ ที่อ้างอิงถึง "ABC" นี้พร้อมกัน
2) อ็อบเจ็กต์ที่ไม่เปลี่ยนรูปนั้นปลอดภัยสำหรับมัลติเธรด เนื่องจากเมื่อหลายเธรดทำงานพร้อมกัน ค่าของอ็อบเจ็กต์ที่ไม่เปลี่ยนรูปมีแนวโน้มที่จะถูกเปลี่ยนแปลงโดยกระบวนการอื่น ซึ่งจะทำให้เกิดผลลัพธ์ที่คาดเดาไม่ได้ และการใช้อ็อบเจ็กต์ที่ไม่เปลี่ยนรูปคือสิ่งนี้ สถานการณ์สามารถหลีกเลี่ยงได้
แน่นอนว่ายังมีสาเหตุอื่นๆ อีก แต่เหตุผลหลักที่ทำให้ Java ตั้งค่า String ให้เป็นแบบไม่เปลี่ยนรูปก็คือประสิทธิภาพและความปลอดภัย
คำตอบที่สอง:
นี่เป็นคำถามเก่าที่ยังคงได้รับความนิยม การออกแบบสตริงให้ไม่เปลี่ยนรูปใน Java เป็นผลมาจากการพิจารณาปัจจัยต่างๆ เพื่อทำความเข้าใจปัญหานี้ คุณต้องรวมหน่วยความจำ การซิงโครไนซ์ และข้อมูลไว้ด้วย ผมจะสรุปเหตุผลต่างๆ
1. ความต้องการพูลคงที่ของสตริง
พูลคงที่สตริง (พูลสตริง, พูลฝึกงานสตริง, พูลการเก็บรักษาสตริง) คือพื้นที่เก็บข้อมูลพิเศษในหน่วยความจำฮีป Java เมื่อสร้างอ็อบเจ็กต์ String หากค่าสตริงมีอยู่แล้วในพูลคงที่ ค่านั้นจะไม่ถูกสร้างขึ้นใหม่ วัตถุ แต่เป็นการอ้างอิงถึงวัตถุที่มีอยู่
ดังที่แสดงในรหัสต่อไปนี้ จะมีการสร้างวัตถุสตริงจริงเดียวเท่านั้นในหน่วยความจำฮีป
คัดลอกรหัสรหัสดังต่อไปนี้:
สตริง s1 = "abcd";
สตริง s2 = "abcd";
แผนผังมีดังนี้:
หากอนุญาตให้เปลี่ยนออบเจ็กต์สตริง จะทำให้เกิดข้อผิดพลาดเชิงตรรกะต่างๆ ตัวอย่างเช่น การเปลี่ยนแปลงออบเจ็กต์หนึ่งจะส่งผลต่อออบเจ็กต์อิสระอีกรายการหนึ่ง แนวคิดของพูลคงที่นี้คือวิธีการปรับให้เหมาะสมที่สุด
โปรดคิดว่า: หากโค้ดเป็นดังนี้ s1 และ s2 จะยังคงชี้ไปที่วัตถุ String จริงอันเดียวกันหรือไม่
คัดลอกรหัสรหัสดังต่อไปนี้:
สตริง s1= "ab" + "cd";
สตริง s2= "abc" + "d";
บางทีคำถามนี้อาจขัดกับสัญชาตญาณสำหรับมือใหม่ แต่เมื่อพิจารณาว่าคอมไพเลอร์สมัยใหม่จะดำเนินการปรับให้เหมาะสมเป็นประจำ พวกเขาทั้งหมดจะชี้ไปที่ออบเจ็กต์เดียวกันในพูลคงที่ หรือคุณสามารถใช้เครื่องมือเช่น jd-gui เพื่อดูเอกสารคลาสที่คอมไพล์แล้ว
2. อนุญาตให้วัตถุ String แคช HashCode
รหัสแฮชของออบเจ็กต์ String ใน Java ถูกใช้บ่อย เช่น ในคอนเทนเนอร์ เช่น hashMap
ความไม่เปลี่ยนรูปของสตริงช่วยให้มั่นใจถึงความเป็นเอกลักษณ์ของรหัสแฮช ดังนั้นจึงสามารถแคชได้อย่างมั่นใจ นี่เป็นวิธีการเพิ่มประสิทธิภาพ ซึ่งหมายความว่าไม่จำเป็นต้องคำนวณรหัสแฮชใหม่ทุกครั้ง ในคำจำกัดความจะมีรหัสต่อไปนี้ ของคลาส String :
คัดลอกรหัสรหัสดังต่อไปนี้:
แฮช int ส่วนตัว;//ใช้เพื่อแคช HashCode
3. ความปลอดภัย
สตริงถูกใช้เป็นพารามิเตอร์โดยคลาส Java จำนวนมาก (ไลบรารี) เช่น URL ที่อยู่การเชื่อมต่อเครือข่าย พาธของไฟล์ และพารามิเตอร์สตริงที่กลไกการสะท้อนกลับต้องการ หากสตริงไม่ได้รับการแก้ไข จะทำให้เกิดความเสี่ยงด้านความปลอดภัยต่างๆ
หากมีรหัสดังต่อไปนี้:
คัดลอกรหัสรหัสดังต่อไปนี้:
เชื่อมต่อบูลีน (สตริง s) {
ถ้า (!isSecure) {
โยน SecurityException ใหม่ ();
-
// หากสตริงสามารถแก้ไขได้ที่อื่น ปัญหา/ข้อผิดพลาดที่ไม่คาดคิดต่างๆ จะเกิดขึ้นที่นี่
สาเหตุปัญหา;
-
โดยทั่วไปแล้ว สาเหตุที่ String ไม่สามารถเปลี่ยนรูปได้นั้นรวมถึงข้อควรพิจารณาในการออกแบบ ปัญหาการเพิ่มประสิทธิภาพ และความปลอดภัย ที่จริงแล้ว นี่คือคำตอบของ "ทำไม" หลายประการในการสัมภาษณ์ Java
คำตอบ 3: ประโยชน์ของความไม่เปลี่ยนรูปคลาส String
String เป็นคลาสที่ใช้บ่อยที่สุดในทุกภาษา เรารู้ว่าใน Java String นั้นไม่เปลี่ยนรูปและเป็นอันสุดท้าย Java ยังบันทึก String Pool ขณะรันไทม์ ซึ่งทำให้ String เป็นคลาสพิเศษ
ประโยชน์ของความไม่เปลี่ยนรูปคลาส String
1. การรวมสตริงจะทำได้เฉพาะเมื่อสตริงไม่เปลี่ยนรูปเท่านั้น การใช้งานพูลสตริงสามารถประหยัดพื้นที่ฮีปได้มากในขณะรันไทม์ เนื่องจากตัวแปรสตริงที่แตกต่างกันชี้ไปที่สตริงเดียวกันในพูล แต่ถ้าสตริงเป็นตัวแปร ดังนั้น String interning จะไม่สามารถทำได้ (หมายเหตุของผู้แปล: String interning หมายความว่ามีการบันทึกสตริงที่แตกต่างกันเพียงสตริงเดียวเท่านั้น นั่นคือ สตริงที่เหมือนกันหลายสตริงจะไม่ถูกบันทึก) เพราะในกรณีนี้ หากตัวแปร เปลี่ยนค่า ค่าของตัวแปรอื่นที่ชี้ไปที่ค่านี้ก็จะเปลี่ยนเช่นกัน
2. ถ้าสตริงมีตัวแปร จะทำให้เกิดปัญหาด้านความปลอดภัยร้ายแรง ตัวอย่างเช่น ชื่อผู้ใช้และรหัสผ่านของฐานข้อมูลจะถูกส่งผ่านเป็นสตริงเพื่อรับการเชื่อมต่อฐานข้อมูล หรือในการเขียนโปรแกรมซ็อกเก็ต ชื่อโฮสต์และพอร์ตจะถูกส่งผ่านเป็นสตริง เนื่องจากสตริงไม่เปลี่ยนรูป ค่าของมันจึงไม่สามารถเปลี่ยนแปลงได้ มิฉะนั้นแฮกเกอร์สามารถใช้ประโยชน์จากช่องโหว่และเปลี่ยนค่าของอ็อบเจ็กต์ที่สตริงชี้ไป ทำให้เกิดช่องโหว่ด้านความปลอดภัย
3. เนื่องจากสตริงไม่เปลี่ยนรูป จึงปลอดภัยแบบมัลติเธรด และอินสแตนซ์สตริงเดียวกันสามารถแชร์โดยหลายเธรดได้ ซึ่งช่วยลดความจำเป็นในการใช้การซิงโครไนซ์เนื่องจากปัญหาความปลอดภัยของเธรด ตัวสายเองนั้นปลอดภัยสำหรับเธรด
4. ตัวโหลดคลาสใช้สตริง และความไม่เปลี่ยนรูปให้ความปลอดภัยเพื่อให้สามารถโหลดคลาสที่ถูกต้อง ตัวอย่างเช่น หากคุณต้องการโหลดคลาส java.sql.Connection และค่านี้ถูกเปลี่ยนเป็น myhacked.Connection จะทำให้ฐานข้อมูลของคุณเสียหายโดยไม่ทราบสาเหตุ
5. เนื่องจากสตริงไม่เปลี่ยนรูป รหัสแฮชจึงถูกแคชไว้เมื่อถูกสร้างขึ้น และไม่จำเป็นต้องคำนวณใหม่ ซึ่งทำให้สตริงมีความเหมาะสมมากสำหรับเป็นคีย์ใน Maps และสามารถประมวลผลสตริงได้เร็วกว่าออบเจ็กต์คีย์อื่นๆ นี่คือสาเหตุที่คีย์ใน HashMap มักใช้สตริง
ข้างต้นคือบทสรุปของฉันเกี่ยวกับประโยชน์ของความไม่เปลี่ยนรูปของสตริง