ฉันไม่ได้รับความเข้าใจที่สมบูรณ์ของตัวสร้างแบบคงที่ใน Java หากได้รับอนุญาตทำไมถึงได้รับอนุญาต คุณจะใช้มันในสถานการณ์อะไร มันจะมีจุดประสงค์อะไร ใครช่วยยกตัวอย่างง่ายๆให้ฉันได้ไหม
ฉันไม่ได้รับความเข้าใจที่สมบูรณ์ของตัวสร้างแบบคงที่ใน Java หากได้รับอนุญาตทำไมถึงได้รับอนุญาต คุณจะใช้มันในสถานการณ์อะไร มันจะมีจุดประสงค์อะไร ใครช่วยยกตัวอย่างง่ายๆให้ฉันได้ไหม
คำตอบ:
การพูดอย่างเคร่งครัด Java ไม่มีตัวสร้างแบบสแตติกเนื่องจากตัวสร้างตามนิยามไม่สามารถเป็นแบบคงที่ได้ สิ่งที่คุณอ้างถึงเรียกว่า "บล็อกการเริ่มต้นคงที่" ตัวสร้างหมายความว่าคุณกำลังสร้างวัตถุ คุณไม่สามารถมีคอนสตรัคเตอร์สำหรับคลาสได้เนื่องจากคลาสไม่ใช่อินสแตนซ์ของตัวเอง มันเป็นเพียงชั้นเรียน สิ่งที่ "สร้าง" คลาสเรียกว่าคอมไพเลอร์ (หรือเครื่องเสมือนขึ้นอยู่กับสิ่งที่มีความหมายโดย "สร้าง") และถ้าคุณได้รับการสร้างรหัสภายในรหัสอื่น ๆ คุณจะได้รับการสร้างรหัสซึ่งเป็น สัตว์ร้ายที่แตกต่างอย่างสิ้นเชิง
การหยิบยกกันทั้งหมดบล็อกการเริ่มต้นแบบคงที่ถูกใช้เพื่อเริ่มต้นฟิลด์แบบคงที่ (หรือระดับคลาส) ที่ซับซ้อนสำหรับคลาส โดยปกติแล้วสิ่งเหล่านี้จะใช้ในการเริ่มต้นสิ่งต่าง ๆ ที่ไม่สามารถเริ่มต้นในหนึ่งบรรทัดหรือต้องการให้วัตถุอื่น ๆ (ซึ่งอาจหรืออาจจะไม่อยู่ในชั้นเรียนที่มีการใช้งานบล็อกแบบคงที่) เริ่มต้นก่อน
โดยพื้นฐานแล้วคนหนึ่งอาจใช้พวกเขาเพื่อบอกคลาส "เฮ้ตั้งค่าตัวแปร A เป็นค่านี้เป็นครั้งแรกหลังจากนั้นให้ใช้ค่าของ A เพื่อเริ่มต้น B" เนื่องจาก Java ต้องการให้การกำหนดค่าเริ่มต้นของฟิลด์มาตรฐานทำได้ทั้งภายในตัวสร้างหรือวิธีการหรือผ่านการเรียกใช้ตัวสร้างหรือวิธีการ (เว้นแต่จะเป็นตัวอักษร) สิ่งเหล่านี้จึงเป็นวิธีที่สะดวกในการเริ่มต้นวัตถุที่ซับซ้อนและคงที่
บล็อกการเริ่มต้นแบบคงที่ไม่จำเป็นต้องใช้บ่อยเกินไปและควรหลีกเลี่ยงโดยทั่วไปเว้นแต่จะมีการใช้งานจริง อย่าเข้าใจฉันผิดพวกเขามีที่ของพวกเขาใน Java แต่ก็เหมือนกับสิ่งอื่น ๆ (เช่น break, return, switch, and goto statement) พวกเขาสามารถใช้งานได้ง่ายเกินไปซึ่งช่วยลดความสามารถในการอ่านและการบำรุงรักษาของโค้ด - ฐานที่ใช้
ตัวอย่างสั้น ๆ ของบล็อกการเริ่มต้นแบบคงที่จะใช้ต่อไปนี้ (ตามคำอธิบายที่ยอดเยี่ยมของบล็อกการเริ่มต้นแบบคงที่พบได้ที่นี่ ):
รหัส:
public class StaticExample{
static {
System.out.println("This is first static block");
}
public StaticExample(){
System.out.println("This is constructor");
}
public static String staticString = "Static Variable";
static {
System.out.println("This is second static block and "
+ staticString);
}
public static void main(String[] args){
StaticExample statEx = new StaticExample();
StaticExample.staticMethod2();
}
static {
staticMethod();
System.out.println("This is third static block");
}
public static void staticMethod() {
System.out.println("This is static method");
}
public static void staticMethod2() {
System.out.println("This is static method2");
}
}
เอาท์พุท:
This is first static block
This is second static block and Static Variable
This is static method
This is third static block
This is constructor
This is static method2
บางอินสแตนซ์จะแสดงรายการเมื่อบล็อกคงมีประโยชน์:
เหตุผลบางอย่างที่จะไม่ใช้บล็อกแบบคงที่ (ในสถานการณ์อื่น ๆ ):
thisคำหลักเนื่องจากไม่มีอินสแตนซ์ฉันควรทราบ: ในขณะที่บางภาษา (เช่น C #) อาจมีไวยากรณ์สำหรับ "ตัวสร้าง" ที่เป็นแบบสแตติกฟังก์ชัน "ตัวสร้าง" เหล่านั้นจะทำงานในลักษณะเดียวกับที่บล็อกเริ่มต้นแบบคงที่ทำใน Java และถูกมองโดยหลายคน misnomers ในภาษาให้แนวคิดพื้นฐานของOOP คอนสตรัค
มันถูกใช้เพื่อเริ่มต้นฟิลด์ที่ยากกว่าการกำหนด:
public class Example{
public final static Map<String, String> preFilledField;
static{
Map<String, String> tmp = new HashMap<>();
//fill map
preFilledField = Collections.unmodifiableMap(tmp);
}
}
มันเป็นไปไม่ได้ที่จะเติมแผนที่ในการเริ่มต้น (เว้นแต่คุณจะใช้ subclass hack ที่ไม่ระบุชื่อ) ดังนั้นนี่เป็นวิธีที่ดีที่สุดที่จะรับประกันว่ามันจะถูกเติมก่อนการใช้งานครั้งแรก
นอกจากนี้คุณยังสามารถทำเช่นนี้เพื่อตรวจจับข้อยกเว้นที่เลือกไว้เมื่อเริ่มต้น
คำตอบของ Gurgadurgen อาจเป็นสิ่งที่คุณกำลังมองหา แต่ฉันจะเพิ่มจุดอื่น ๆ สองสามอย่างที่บางครั้งถูกละเลยเมื่อมีคนต้องการ "ตัวสร้างแบบคงที่"
ถ้าคุณต้องการวิธีสแตติกที่สร้างอินสแตนซ์ของคลาสของคุณคุณสามารถสร้างวิธีสแตติกที่เรียกใช้ตัวสร้างคลาส
public class Example
{
/** Static method to create an instance. */
public static Example build()
{ return new Example() ; }
/** A field of each instance. */
private String stuff ;
/** The class's actual constructor. */
public Example()
{ stuff = new String() ; }
public String getStuff()
{ return this.stuff ; }
/**
* Mutator for "stuff" property. By convention this returns "void"
* but you might want to return the object itself, to support the
* sort of chained invocations that are becoming trendy now. You'll
* see the stylistic benefits of chaining later in this example.
*/
public Example setStuff( String newStuff )
{
this.stuff = newStuff ;
return this ;
}
}
public class ExampleTest
{
public static void main( String[] args )
{
// The usual instance model.
Example first = new Example() ;
System.out.println( first.setStuff("stuff").getStuff() ) ;
// Using your static method to construct an instance:
Example second = Example.build() ;
System.out.println( second.setStuff("more stuff").getStuff() ) ;
// Chaining all the invocations at once:
System.out.println( Example.build().setStuff("even more stuff").getStuff() ) ;
}
}
สิ่งนี้สร้างผลลัพธ์:
stuff
more stuff
even more stuff
เหตุผลอื่นในการสร้างวิธีการแบบคงที่เพื่อสร้างอินสแตนซ์เป็นกรณีเมื่อคุณต้องการให้แน่ใจว่าอินสแตนซ์ของคลาสของคุณมีอยู่จริง ณ เวลาใดก็ตาม นี้เรียกว่าเดี่ยว โดยการประชุมคลาสดังกล่าวจะให้วิธีการคงที่เรียกว่าgetInstance()จะได้รับหนึ่งและอินสแตนซ์เดียวที่ถือว่าเป็น "ซิงเกิล"
public class SingletonExample extends Example
{
// Note: This extends my previous example, which has a "stuff"
// property, and a trivial constructor.
/** The singleton instance, statically initialized as null. */
private static SingletonExample singleton = null ;
/**
* The static accessor for the singleton. If no instance exists,
* then it will be created; otherwise, the one that already exists
* will be returned.
*/
public static SingletonExample getInstance()
{
if( singleton == null )
singleton = new SingletonExample() ;
return singleton ;
}
}
public class SingletonExampleTest
{
public static void main( String[] args )
{
System.out.println( SingletonExample.getInstance().setStuff("stuff").getStuff() ) ;
// You could still create instances of this class normally if you want to.
SingletonExample otherstuff = new SingletonExample() ;
otherstuff.setStuff("other stuff") ;
// But watch what happens to this.
System.out.println( SingletonExample.getInstance().getStuff() ) ;
System.out.println( otherstuff.getStuff() ) ;
// Now we show what happens when you start modifying the singleton.
SingletonExample theoneandonly = SingletonExample.getInstance() ;
theoneandonly.setStuff("changed stuff") ;
System.out.println( SingletonExample.getInstance().getStuff() ) ;
}
}
สิ่งนี้สร้างสิ่งต่อไปนี้
stuff
stuff
other stuff
changed stuff
โดยการจัดเก็บการอ้างอิงไปยังซิงเกิลแล้วปรับเปลี่ยนการเรียกครั้งถัดไปเพื่อgetInstance()รับซิงเกิลที่แก้ไขนั้น
การพยายามใช้ซิงเกิลตันเป็นวิธีเริ่มต้นสร้างตัวแปรทั่วโลกสำหรับแอปพลิเคชันของคุณ ในบางบริบทสิ่งนี้อาจมีประโยชน์ แต่อาจทำให้คุณเดือดร้อน โดยเฉพาะอย่างยิ่งฉันพบข้อผิดพลาดที่น่าสนใจในขณะที่พัฒนาแอพ Android ที่อินสแตนซ์ซิงเกิลอาจหายไป การกระโดดจากกิจกรรมหนึ่งไปยังอีกกิจกรรมหนึ่งอาจทำให้ JVM ใช้ "ตัวโหลดคลาส" ใหม่ซึ่งจะไม่ทราบเกี่ยวกับซิงเกิลตันที่ถูกจัดเก็บแบบสแตติกโดยตัวโหลดคลาสก่อนหน้านี้
ในขณะที่ฉันเข้าใจว่ามีหลายคนที่เห็นความคิดของ "ตัวสร้างแบบคงที่" ในฐานะที่เรียกชื่อผิดฉันไม่เชื่อว่าจะเป็นกรณี ปัญหาก็คือในกระบวนการของการก่อสร้างทั้งในชั้นเรียนและวัตถุอินสแตนซ์ของพวกเขา มีการระบุไว้ในหัวข้ออื่น ๆ ว่าการสร้างคลาสเป็นหน้าที่ของคอมไพเลอร์ แม้แต่ในชวาก็แค่ครึ่งเดียวเท่านั้น
คอมไพเลอร์สร้างโครงสร้างสำหรับแต่ละนิยามคลาส โครงนั่งร้านมีข้อมูลเมตาเกี่ยวกับคลาสและอินสแตนซ์ใดที่ควรมีในช่วงเวลาที่สร้าง หากคลาสกำหนดเขตข้อมูลที่ได้รับการกำหนดค่าดั้งเดิมคงที่ค่านั้นจะรวมอยู่ในนั่งร้านโดยคอมไพเลอร์ สำหรับประเภทค่าอื่น ๆ ที่ได้รับมอบหมายคอมไพเลอร์จะสร้างรูทีนการกำหนดค่าเริ่มต้นที่จะต้องรัน 1 ครั้งก่อนการสร้างอินสแตนซ์คลาสแรกที่อัปเดตโครงด้วยค่าที่เหมาะสม คอมไพเลอร์ไม่สามารถทำการอัพเดทนี้ได้
เนื่องจากการอัปเดตครั้งเดียวไปยังโครงนั่งร้านนี้มักเป็นสิ่งที่จำเป็นอย่างยิ่งยวดต่อการทำงานของตัวสร้างอินสแตนซ์ที่เหมาะสม นี่คือเหตุผลในภาษา OO ทั่วไปที่สนับสนุนแนวคิดมันถูกเรียกว่าตัวสร้างแบบคงที่ แนวคิดเบื้องหลังบล็อกการเริ่มต้นแบบสแตติกใน Java นั้นน้อยกว่าการเปลี่ยนแปลงทางความหมายเพื่อให้สอดคล้องกับความคิดที่ว่าโปรแกรมเมอร์ Java ควรเป็นทั้งระบบและการใช้งานไม่เชื่อเรื่องพระเจ้า
ดังที่คำตอบอื่น ๆ ได้กล่าวไว้คุณสามารถเขียนวิธีการคงที่ที่สร้างวัตถุ สิ่งนี้ทำให้เกิดการขาดคอนสตรัคเตอร์ที่มีชื่อใน Java (และภาษาอื่น ๆ อีกมากมายเดลฟายรองรับคอนสตรัคเตอร์ที่มีชื่อ) คุณสามารถเล่นกับประเภทและลำดับของพารามิเตอร์ แต่อาจนำไปสู่รหัสที่ไม่ชัดเจนและเปราะบาง
ตัวอย่างเช่นเราสามารถคิดค้นสถานการณ์ที่วัตถุของคุณสามารถสร้างจากสตริง XML หรือสตริง JSON คุณสามารถเขียนวิธีการเช่น:
static MyObject createFromXml(String xml);
static MyObject createFromJson(String json);
ฉันใช้สิ่งนี้เป็นครั้งคราวเป็นทางเลือกแทนตัวสร้างพารามิเตอร์ที่ไม่มีวิธีการกำหนดชื่อเริ่มต้น:
MyObject myObject = new MyObject();
myObject.loadXml(xml).
คุณสามารถดูวิธีการสร้างแบบคงที่เป็นการใช้รูปแบบการสร้างภายในชั้นเรียนของคุณ
คุณสามารถดูส่วน "คงที่" เหมือนตัวสร้างระดับคลาสเพื่อเริ่มต้นคุณสมบัติคลาส (คงที่ใน java) เช่นเดียวกับคอนสตรัค "ปกติ" มันถูกใช้เพื่อเริ่มต้นคุณสมบัติระดับอินสแตนซ์