การทำงานกับตัวสร้างสแตติกใน Java


12

ฉันไม่ได้รับความเข้าใจที่สมบูรณ์ของตัวสร้างแบบคงที่ใน Java หากได้รับอนุญาตทำไมถึงได้รับอนุญาต คุณจะใช้มันในสถานการณ์อะไร มันจะมีจุดประสงค์อะไร ใครช่วยยกตัวอย่างง่ายๆให้ฉันได้ไหม


2
การแชร์งานวิจัยของคุณช่วยให้ทุกคน บอกเราว่าคุณได้ลองทำอะไรและทำไมมันถึงไม่ตรงกับความต้องการของคุณ สิ่งนี้แสดงให้เห็นว่าคุณใช้เวลาในการพยายามช่วยตัวเองมันช่วยให้เราไม่ต้องย้ำคำตอบที่ชัดเจนและส่วนใหญ่จะช่วยให้คุณได้คำตอบที่เฉพาะเจาะจงและเกี่ยวข้องมากขึ้น ดูวิธีถาม
gnat

3
ไม่มีสิ่งเช่น "ตัวสร้างแบบคงที่"
David Conrad

คำตอบ:


28

การพูดอย่างเคร่งครัด 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

บางอินสแตนซ์จะแสดงรายการเมื่อบล็อกคงมีประโยชน์:

  • หากคุณกำลังโหลดไดรเวอร์และรายการอื่น ๆ ลงในเนมสเปซ ตัวอย่างเช่นคลาส Class มีบล็อกแบบสแตติกซึ่งมันลงทะเบียนพื้นเมือง
  • หากคุณจำเป็นต้องทำการคำนวณเพื่อเริ่มต้นตัวแปรสแตติกของคุณคุณสามารถประกาศบล็อกแบบสแตติกซึ่งได้รับการดำเนินการเพียงครั้งเดียวเมื่อคลาสถูกโหลดครั้งแรก
  • ปัญหาด้านความปลอดภัยหรืองานบันทึกที่เกี่ยวข้อง

เหตุผลบางอย่างที่จะไม่ใช้บล็อกแบบคงที่ (ในสถานการณ์อื่น ๆ ):

  • มีข้อ จำกัด ของ JVM ที่บล็อก initializer แบบสแตติกไม่ควรเกิน 64K
  • คุณไม่สามารถโยนข้อยกเว้นที่ตรวจสอบแล้ว
  • คุณไม่สามารถใช้thisคำหลักเนื่องจากไม่มีอินสแตนซ์
  • คุณไม่ควรพยายามเข้าถึง super เนื่องจากไม่มีสิ่งดังกล่าวสำหรับบล็อกแบบคงที่
  • คุณไม่ควรส่งคืนสิ่งใดจากบล็อกนี้
  • บล็อกแบบคงที่ทำให้การทดสอบเป็นฝันร้าย

ฉันควรทราบ: ในขณะที่บางภาษา (เช่น C #) อาจมีไวยากรณ์สำหรับ "ตัวสร้าง" ที่เป็นแบบสแตติกฟังก์ชัน "ตัวสร้าง" เหล่านั้นจะทำงานในลักษณะเดียวกับที่บล็อกเริ่มต้นแบบคงที่ทำใน Java และถูกมองโดยหลายคน misnomers ในภาษาให้แนวคิดพื้นฐานของOOP คอนสตรั


ลิงค์เกี่ยวกับบล็อกคงไม่ทำงานอีกต่อไป
ASH

5

มันถูกใช้เพื่อเริ่มต้นฟิลด์ที่ยากกว่าการกำหนด:

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 ที่ไม่ระบุชื่อ) ดังนั้นนี่เป็นวิธีที่ดีที่สุดที่จะรับประกันว่ามันจะถูกเติมก่อนการใช้งานครั้งแรก

นอกจากนี้คุณยังสามารถทำเช่นนี้เพื่อตรวจจับข้อยกเว้นที่เลือกไว้เมื่อเริ่มต้น


นี่เป็นหนึ่งในการใช้งานหลักที่ฉันได้เห็น แต่ด้วยการรวมวิธีการคงที่จากโรงงานสำหรับเฟรมเวิร์กคอลเลกชันใน Java 9 กรณีการใช้งานนี้ได้รับการคัดค้านจริงๆ
Hangman4358

3

คำตอบของ 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 ใช้ "ตัวโหลดคลาส" ใหม่ซึ่งจะไม่ทราบเกี่ยวกับซิงเกิลตันที่ถูกจัดเก็บแบบสแตติกโดยตัวโหลดคลาสก่อนหน้านี้


0

ในขณะที่ฉันเข้าใจว่ามีหลายคนที่เห็นความคิดของ "ตัวสร้างแบบคงที่" ในฐานะที่เรียกชื่อผิดฉันไม่เชื่อว่าจะเป็นกรณี ปัญหาก็คือในกระบวนการของการก่อสร้างทั้งในชั้นเรียนและวัตถุอินสแตนซ์ของพวกเขา มีการระบุไว้ในหัวข้ออื่น ๆ ว่าการสร้างคลาสเป็นหน้าที่ของคอมไพเลอร์ แม้แต่ในชวาก็แค่ครึ่งเดียวเท่านั้น

คอมไพเลอร์สร้างโครงสร้างสำหรับแต่ละนิยามคลาส โครงนั่งร้านมีข้อมูลเมตาเกี่ยวกับคลาสและอินสแตนซ์ใดที่ควรมีในช่วงเวลาที่สร้าง หากคลาสกำหนดเขตข้อมูลที่ได้รับการกำหนดค่าดั้งเดิมคงที่ค่านั้นจะรวมอยู่ในนั่งร้านโดยคอมไพเลอร์ สำหรับประเภทค่าอื่น ๆ ที่ได้รับมอบหมายคอมไพเลอร์จะสร้างรูทีนการกำหนดค่าเริ่มต้นที่จะต้องรัน 1 ครั้งก่อนการสร้างอินสแตนซ์คลาสแรกที่อัปเดตโครงด้วยค่าที่เหมาะสม คอมไพเลอร์ไม่สามารถทำการอัพเดทนี้ได้

เนื่องจากการอัปเดตครั้งเดียวไปยังโครงนั่งร้านนี้มักเป็นสิ่งที่จำเป็นอย่างยิ่งยวดต่อการทำงานของตัวสร้างอินสแตนซ์ที่เหมาะสม นี่คือเหตุผลในภาษา OO ทั่วไปที่สนับสนุนแนวคิดมันถูกเรียกว่าตัวสร้างแบบคงที่ แนวคิดเบื้องหลังบล็อกการเริ่มต้นแบบสแตติกใน Java นั้นน้อยกว่าการเปลี่ยนแปลงทางความหมายเพื่อให้สอดคล้องกับความคิดที่ว่าโปรแกรมเมอร์ Java ควรเป็นทั้งระบบและการใช้งานไม่เชื่อเรื่องพระเจ้า


0

ดังที่คำตอบอื่น ๆ ได้กล่าวไว้คุณสามารถเขียนวิธีการคงที่ที่สร้างวัตถุ สิ่งนี้ทำให้เกิดการขาดคอนสตรัคเตอร์ที่มีชื่อใน Java (และภาษาอื่น ๆ อีกมากมายเดลฟายรองรับคอนสตรัคเตอร์ที่มีชื่อ) คุณสามารถเล่นกับประเภทและลำดับของพารามิเตอร์ แต่อาจนำไปสู่รหัสที่ไม่ชัดเจนและเปราะบาง

ตัวอย่างเช่นเราสามารถคิดค้นสถานการณ์ที่วัตถุของคุณสามารถสร้างจากสตริง XML หรือสตริง JSON คุณสามารถเขียนวิธีการเช่น:

static MyObject createFromXml(String xml);
static MyObject createFromJson(String json);

ฉันใช้สิ่งนี้เป็นครั้งคราวเป็นทางเลือกแทนตัวสร้างพารามิเตอร์ที่ไม่มีวิธีการกำหนดชื่อเริ่มต้น:

MyObject myObject = new MyObject();
myObject.loadXml(xml). 

คุณสามารถดูวิธีการสร้างแบบคงที่เป็นการใช้รูปแบบการสร้างภายในชั้นเรียนของคุณ


เมื่อฉันอ่านคำถามมันเป็นเรื่องของ initializers คงที่ไม่ใช่เกี่ยวกับ emthods แบบคงที่ปกติ
CodesInChaos

-2

คุณสามารถดูส่วน "คงที่" เหมือนตัวสร้างระดับคลาสเพื่อเริ่มต้นคุณสมบัติคลาส (คงที่ใน java) เช่นเดียวกับคอนสตรัค "ปกติ" มันถูกใช้เพื่อเริ่มต้นคุณสมบัติระดับอินสแตนซ์

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.