ทำไม enum singleton ถึงขี้เกียจ?
ผมเห็นคำตอบเช่นนี้พยายามที่จะชี้แจงผ่านทางความคิดเห็นและยังไม่รับหนังสือจากตัวอย่างที่นี่
อาจถึงเวลาสำหรับคำถามเฉพาะนี้ ...
ทำไมการดำเนิน enum เดี่ยวที่เรียกว่าขี้เกียจ ?
public enum EnumLazySingleton {
INSTANCE;
EnumLazySingleton() {
System.out.println("constructing: " + this);
}
public static void touchClass() {}
}
มันแตกต่างจากการใช้งานอย่างกระตือรือร้นอย่างไร?
public class BasicEagerSingleton {
private static final BasicEagerSingleton instance = new BasicEagerSingleton();
public static BasicEagerSingleton getInstance() {
return instance;
}
private BasicEagerSingleton() {
System.out.println("constructing: " + this);
}
public static void touchClass() {}
}
ทั้งสองจะ init อินสแตนซ์โดยไม่ต้องเข้าถึงINSTANCE/getInstance()- touchClass()เช่นการโทร
public class TestSingleton {
public static void main(String... args) {
System.out.println("sleeping for 5 sec...");
System.out.println("touching " + BasicEagerSingleton.class.getSimpleName());
BasicEagerSingleton.touchClass();
System.out.println("touching " + EnumLazySingleton.class.getSimpleName());
EnumLazySingleton.touchClass();
}
}
เอาท์พุต:
sleeping for 5 sec...
touching BasicEagerSingleton
constructing: BasicEagerSingleton@7bfcd12c
touching EnumLazySingleton
constructing: INSTANCE
ตอนนี้เราสามารถพูดได้ว่าทั้งสองมีความขี้เกียจ อะไรคือความกระตือรือร้น ?
เป็นที่ชัดเจนว่า (เช่น) วิธี "ตรวจสอบการล็อกซ้ำ" นั้นขี้เกียจจริง ๆ อย่างไร (ยุ่งและช้า) แต่ถ้า enum ขี้เกียจแสดงว่าคนโสดขี้เกียจเนื่องจากการโหลดคลาสที่หลีกเลี่ยงไม่ได้ - อันที่จริงทุกอย่างขี้เกียจ ความแตกต่างนี้จะหยุดลงเมื่อถึงจุดใด
คำตอบ
สองคำตอบแรกที่เชื่อมโยงกัน (โดยPeter LawreyและJoachim Sauer ) ต่างยอมรับว่า enums ไม่ได้เริ่มต้นอย่างเกียจคร้าน คำตอบในลิงค์ที่สามนั้นผิดเกี่ยวกับความหมายของการเริ่มต้นที่ขี้เกียจ
คำแนะนำในการใช้ enums เป็น singletons มาจาก Effective Java ของ Josh Bloch โดยเฉพาะอย่างยิ่งบทที่เกี่ยวกับ enum singletons ไม่ได้กล่าวถึงความเกียจคร้าน มีบทต่อมาที่อุทิศให้กับการเริ่มต้นที่ขี้เกียจซึ่งในทำนองเดียวกันก็ไม่มีการกล่าวถึง enums บทนี้มีสองไฮไลท์
- หากคุณจำเป็นต้องใช้การกำหนดค่าเริ่มต้นแบบขี้เกียจเพื่อประสิทธิภาพบนฟิลด์แบบคงที่ให้ใช้สำนวนคลาสผู้ถือการเริ่มต้นขี้เกียจ
- หากคุณจำเป็นต้องใช้การเริ่มต้นแบบขี้เกียจเพื่อประสิทธิภาพในช่องอินสแตนซ์ให้ใช้สำนวนการตรวจสอบอีกครั้ง
ไม่ต้องสงสัยเลยว่า enums จะเป็นอีกสำนวนหนึ่งในรายการนี้หากพวกเขาเริ่มต้นอย่างเฉื่อยชา ในความเป็นจริงพวกเขาไม่ได้แม้ว่าความสับสนเกี่ยวกับความหมายของการเริ่มต้นแบบขี้เกียจจะส่งผลให้คำตอบที่ไม่ถูกต้องตามที่ OP แสดง
ฉันขอเดิมพันดังต่อไปนี้:
คุณกำลังพยายามระบุ 2 "กระบวนการ" หรือ ... "things"(มาทำให้เข้าใจง่าย - เพราะถ้าฉันเริ่มพูดว่า "Code Blocks" ฟังดูยากกว่า) ...
- เมื่อถึงจุดหนึ่ง class-loader จะทำงานและคุณต้องการทราบว่า
"things"จะดำเนินการอย่างไรเมื่อ class-loader โหลดคลาส - ในอีกจุดหนึ่งการเรียกใช้เมธอดในคลาสจะทำให้อีกอันหนึ่ง
"thing"รัน / รันและคุณต้องการทราบว่า (ซึ่ง"processes") จะเริ่มต้นอย่างไร ..
ข้อเท็จจริงต่อไปนี้เกี่ยวข้อง:
- Static initializers ถูกรันเมื่อ class-loader โหลดคลาส class-loader จะไม่โหลดคลาสจนกว่าโค้ดที่รันอยู่พบความจำเป็นในการโหลด (เนื่องจากมีการเรียกใช้เมธอดหรือฟิลด์) เช่น:
touchClass()- หากอินสแตนซ์ซิงเกิลตันของคลาสใดคลาสหนึ่งหรือประเภทที่แจกแจงมี
fieldที่กำลังเริ่มต้นในstaticส่วนของคลาสคลาสนั้นจะถูกโหลดทันทีที่คุณ 'แตะ' คลาส - เนื่องจากคลาส - โหลดเดอร์รันทั้งหมดstatic initializationsสำหรับคลาสหรือ enum ในการโหลด- ขี้เกียจโหลดน่าจะเป็น (และนี่คือ "การตีความ" ของฉันเกี่ยวกับสิ่งที่คุณกำลังถาม) จะเกิดขึ้นเมื่อการเรียกใช้เมธอดขอให้ชั้นเรียนสร้างอินสแตนซ์ซิงเกิลตันซึ่งอาจเกิดขึ้นได้ไม่นานหลังจาก "โหลด" ของ
classหรือenum.
ชั้นเรียนดังต่อไปนี้:
public class LazySingleton
{
// At time of class-loading, this singleton is set to 'null'
private static singleton = null;
// This is a method that will not be invoked until it is called by
// some other code-block (some other "thing")... When "touchClass()"
// is called, the singleton instance is not created.
public static LazySingleton retrieveSingleton()
{
if (singleton == null) singleton = new LazySingleton();
return singleton;
}
// DOES NOTHING... The Singleton is *not* loaded, even though the
// Class Loader has already loaded this Java ".class" file
// into memory.
public static void touchClass() { }
private LazySingleton()
{ System.out.println("constructing: LazySingleton"); }
}
ในทางกลับกัน:
public enum EagerEnum
{
// The class loader will run this constructor as soon as this 'enum'
// is loaded from a '.class' file (in JAR or on disk) into memory
MyEnumConstant();
private EagerEnum()
{ System.out.println("Eager Enum Constructed"); }
// This will cause the Class Loader to Load this enum from the
// Java ".class" File immediately, and the "MyEnumConstant" will
// also have to be loaded - meaning the constructor will be called.
public static void touchEnum() { }
}
ดังนั้นรหัสต่อไปนี้จะสร้างผลลัพธ์
LazySingleton.touchClass(); // Prints nothing
EagerEnum.touchClass(); // Prints "Eager Enum Constructed"
LazySingleton.getSingletonInstance(); // Prints "constructing: LazySingleton