กรณีที่สร้างออบเจ็กต์ที่ไม่จำเป็น
การใช้ new String()
สตริง a, b, c ทั้งหมดจะมีค่าเป็นสตริง “hi” แต่เนื่องจากที่อยู่ที่สตริงทั้งสามนี้อ้างอิงนั้นแตกต่างกัน จึงเกิดการสิ้นเปลืองเนื่องจากการจัดสรรหน่วยความจำที่แตกต่างกันสำหรับข้อมูลเดียวกัน
<span class="image-inline ck-widget" contenteditable="false"><img src="https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2Fa53d14aa-6abf-40f0-8441-435647d172fa%2FUntitled.png?table=block&id=f168859c-367c-4924-a4c6-5e04788fab67&spaceId=b453bd85-cb15-44b5-bf2e-580aeda8074e&width=2000&userId=80352c12-65a4-4562-9a36-2179ed0dfffb&cache=v2" alt="Untitled" style="aspect-ratio:2000/1146;" width="2000" height="1146"></span>
ดังนั้น เมื่อประกาศสตริง ควรประกาศด้วยลิเทอรัลแทนที่จะใช้คีย์เวิร์ด new
โค้ดข้างต้นใช้เพียงอินสแตนซ์เดียว ยิ่งไปกว่านั้น หากใช้แบบนี้ จะรับประกันได้ว่าโค้ดทั้งหมดที่ใช้ลิเทอรัลสตริง “hi” ภายใน JVM เดียวกันจะนำอินสแตนซ์เดียวกันมาใช้ซ้ำ ซึ่งเป็นเพราะคุณลักษณะของ Java Constant Pool
การใช้ new Boolean()
โค้ดข้างต้นสร้างอินสแตนซ์ Boolean ผ่านทางคอนสตรัคเตอร์ที่รับสตริงเป็นพารามิเตอร์ Boolean มีค่าเป็น true หรือ false เท่านั้น การสร้างอินสแตนซ์ทุกครั้งนั้นสิ้นเปลืองหน่วยความจำ ดังนั้นจึงควรใช้เมธอด Boolean.valueOf() ซึ่งเป็นเมธอดแบบคงที่
การใช้ String.matches()
หากต้นทุนการสร้างสูง ควรแคชและนำกลับมาใช้ซ้ำ แต่เราไม่สามารถทราบต้นทุนของออบเจ็กต์ที่สร้างได้เสมอไป ตัวอย่างเช่น หากต้องการเขียนเมธอดเพื่อตรวจสอบว่าสตริงที่กำหนดเป็นเลขโรมันที่ถูกต้องหรือไม่ วิธีที่ง่ายที่สุดคือการใช้ regular expression ดังนี้
แต่ String.matches() เป็นเมธอดที่มีปัญหาในแง่ประสิทธิภาพ อินสแตนซ์ Pattern สำหรับ regular expression ที่เมธอดนี้สร้างขึ้นภายในนั้น จะถูกใช้เพียงครั้งเดียวและทิ้งไปทันทีหลังจากนั้นจึงกลายเป็นเป้าหมายของการเก็บขยะ แต่เมื่อความถี่ในการใช้ regular expression นั้นเพิ่มขึ้น ต้นทุนในการสร้างและทิ้งอินสแตนซ์ Pattern ที่เหมือนกันก็จะเพิ่มขึ้นด้วย ดังนั้นจึงควรแคชอินสแตนซ์ Pattern ไว้ล่วงหน้าและนำกลับมาใช้ซ้ำทุกครั้งที่เมธอด isRomanNumeral() ถูกเรียกใช้
**หมายเหตุ**
ในตัวอย่างทั้งหมดข้างต้น เมื่อแคชออบเจ็กต์ที่ไม่จำเป็นนั้น เราได้สร้างออบเจ็กต์ที่ไม่เปลี่ยนแปลงทั้งหมด เนื่องจากเพื่อความปลอดภัยในการนำกลับมาใช้ซ้ำ แต่บางกรณีตรงข้ามกับสัญชาตญาณในการนำออบเจ็กต์ที่ไม่เปลี่ยนแปลงมาใช้ซ้ำ
อะแดปเตอร์ (มุมมอง) เป็นออบเจ็กต์ที่มอบหมายงานจริงให้กับออบเจ็กต์หลังส่วน และทำหน้าที่เป็นอินเทอร์เฟซที่สอง อะแดปเตอร์จำเป็นต้องจัดการกับออบเจ็กต์หลังส่วนเท่านั้น ดังนั้นจึงควรสร้างอะแดปเตอร์เพียงหนึ่งตัวต่อออบเจ็กต์หลังส่วนหนึ่งตัว
ตัวอย่างเช่น เมธอด keySet() ของอินเทอร์เฟซ Map จะส่งคืนมุมมอง Set ที่ประกอบด้วยคีย์ทั้งหมดภายในออบเจ็กต์ Map ผู้ใช้สามารถคิดได้ว่าอินสแตนซ์ Set ใหม่จะถูกสร้างขึ้นทุกครั้งที่เรียกใช้เมธอด keySet() แต่ในความเป็นจริงแล้ว การใช้งาน JDK จะส่งคืนอินสแตนซ์ Set ที่เปลี่ยนแปลงได้เดียวกันทุกครั้ง
เหตุผลก็คือแม้ว่าอินสแตนซ์ Set ที่ส่งคืนจะเปลี่ยนแปลงได้ แต่ฟังก์ชันที่ดำเนินการทั้งหมดก็เหมือนกัน และอินสแตนซ์ Set ทั้งหมดเป็นตัวแทนของอินสแตนซ์ Map ดังนั้น แม้ว่า keySet() จะสร้างออบเจ็กต์มุมมองหลายรายการ ก็ไม่มีปัญหาหรือประโยชน์อะไร
ดังนั้น หากแก้ไขอินสแตนซ์ names1 อินสแตนซ์ names2 ก็จะได้รับผลกระทบเช่นกัน
อย่างไรก็ตาม ในฐานะบุคคล ผมคิดว่าการส่งคืนค่าของเมธอด keySet() ควรใช้การคัดลอกแบบป้องกันเพื่อส่งคืนออบเจ็กต์ใหม่ทุกครั้ง หากอินสแตนซ์ Set ที่ได้รับจากเมธอด keySet() กำลังถูกใช้ในที่อื่น และมีโค้ดที่เปลี่ยนแปลงสถานะของอินสแตนซ์นี้ เราจะไม่สามารถมั่นใจได้ในค่าของอินสแตนซ์ Set และอินสแตนซ์ Map ที่กำลังใช้งานอยู่
นอกจากนี้ ในสภาพแวดล้อมที่ไม่ได้ใช้ keySet() มากเกินไป การสร้างอินเทอร์เฟซ Set ทุกครั้งจะไม่ส่งผลกระทบต่อประสิทธิภาพอย่างรุนแรง ผมคิดว่าการสร้างอินเทอร์เฟซ Set ให้เป็นออบเจ็กต์ที่ไม่เปลี่ยนแปลงเพื่อให้การบำรุงรักษาเสถียรนั้นดีกว่า
การออโต้บ็อกซิง
การออโต้บ็อกซิงเป็นเทคนิคที่แปลงประเภทข้อมูลพื้นฐานและประเภทข้อมูลแรปเปอร์ให้กันและกันโดยอัตโนมัติเมื่อโปรแกรมเมอร์ใช้ผสมกัน แต่การออโต้บ็อกซิงจะทำให้ความแตกต่างระหว่างประเภทข้อมูลพื้นฐานและประเภทข้อมูลแรปเปอร์เบลอลงเท่านั้น ไม่ได้กำจัดความแตกต่างนั้นไปเสียทีเดียว
แม้ว่าในเชิงตรรกะจะไม่มีปัญหา แต่โค้ดนี้ไม่มีประสิทธิภาพอย่างมาก สาเหตุมาจากประเภทข้อมูลของ sum และประเภทข้อมูลของ i ภายในลูป for
ประเภทข้อมูลของ sum คือ Long และ i คือ long นั่นคือ i ซึ่งเป็นประเภทข้อมูล long จะสร้างอินสแตนซ์ Long ใหม่ทุกครั้งที่ถูกบวกเข้ากับ sum ในลูป ผลลัพธ์คือควรใช้ประเภทข้อมูลพื้นฐานแทนที่จะเป็นประเภทข้อมูลแรปเปอร์ และควรระมัดระวังเพื่อไม่ให้เกิดการออโต้บ็อกซิงโดยไม่ตั้งใจ
ส่วนที่ไม่ควรเข้าใจผิด
อย่าเข้าใจผิดว่าคำแนะนำให้หลีกเลี่ยงการสร้างออบเจ็กต์ที่ไม่จำเป็นนั้นหมายความว่าควรหลีกเลี่ยงเนื่องจากต้นทุนการสร้างออบเจ็กต์สูง
โดยเฉพาะอย่างยิ่ง ในปัจจุบัน JVM ไม่ได้รู้สึกว่าการสร้างและกำจัดออบเจ็กต์ขนาดเล็กที่ไม่จำเป็นเป็นภาระมากนัก ดังนั้น หากไม่ใช่ออบเจ็กต์ที่มีต้นทุนสูงมาก เช่น การเชื่อมต่อฐานข้อมูล จึงไม่ควรสร้างคัสตอมออบเจ็กต์พูล
ยิ่งไปกว่านั้น จำไว้ว่าความเสียหายที่เกิดขึ้นจากการนำออบเจ็กต์กลับมาใช้ซ้ำในสถานการณ์ที่จำเป็นต้องมีการคัดลอกแบบป้องกันนั้นมากกว่าความเสียหายที่เกิดขึ้นจากการสร้างออบเจ็กต์ที่ไม่จำเป็นซ้ำๆ ผลข้างเคียงของการสร้างซ้ำจะส่งผลกระทบต่อรูปแบบโค้ดและประสิทธิภาพเท่านั้น แต่หากการคัดลอกแบบป้องกันล้มเหลว จะนำไปสู่ข้อผิดพลาดและปัญหาความปลอดภัยโดยตรง
ความคิดเห็น0