การเริ่มต้นคลาสแบบคงที่จะเกิดขึ้นเมื่อใด


110

ฟิลด์คงที่จะเริ่มต้นเมื่อใด ถ้าฉันไม่เคยสร้างอินสแตนซ์ของคลาส แต่ฉันเข้าถึงฟิลด์แบบคงที่บล็อกแบบคงที่และวิธีการคงที่ส่วนตัวทั้งหมดจะใช้เพื่อสร้างอินสแตนซ์ฟิลด์สแตติกส่วนตัวที่เรียกว่า (ตามลำดับ) ในทันทีนั้นหรือไม่

จะเกิดอะไรขึ้นถ้าฉันเรียกวิธีการแบบคงที่? มันยังเรียกใช้บล็อกคงที่ทั้งหมดหรือไม่ ก่อนวิธี?


คล้ายกันสำหรับบล็อก initializer แบบคงที่: stackoverflow.com/questions/2007666/…
Ciro Santilli 郝海东冠状病六四事件法轮功

คำตอบ:


156

โดยปกติการเริ่มต้นแบบคงที่ของคลาสจะเกิดขึ้นทันทีก่อนที่เหตุการณ์ต่อไปนี้จะเกิดขึ้นครั้งแรก:

  • มีการสร้างอินสแตนซ์ของคลาส
  • วิธีการคงที่ของคลาสถูกเรียกใช้
  • มีการกำหนดเขตข้อมูลคงที่ของชั้นเรียน
  • ใช้ฟิลด์คงที่ไม่คงที่หรือ
  • สำหรับคลาสระดับบนสุดคำสั่ง assert ที่ซ้อนอยู่ภายในคลาสจะถูกเรียกใช้1 .

ดูJLS 12.4.1

นอกจากนี้ยังสามารถบังคับให้คลาสเริ่มต้นได้ (หากยังไม่ได้เริ่มต้น) โดยใช้Class.forName(fqn, true, classLoader)หรือรูปแบบสั้น ๆClass.forName(fqn)


1 - สัญลักษณ์แสดงหัวข้อย่อยสุดท้ายมีอยู่ใน JLS สำหรับ Java 6 ถึง Java 8 แต่เห็นได้ชัดว่าเป็นข้อผิดพลาดในข้อกำหนด มันได้รับการแก้ไขในที่สุดชวา 9 JLS: ดูแหล่งที่มา


9
แม้ว่าจะมีข้อผิดพลาดทั่วไป Primitives และStrings ถูกแทนที่และไม่มีการอ้างอิง หากคุณอ้างอิงclass Other { public static final int VAL = 10; }จากคลาสบางคลาสMyClass { private int = Other.VAL; }คลาสOtherจะไม่ถูกโหลด แต่คอมไพเลอร์จะแทนที่ฟิลด์สุดท้ายในเวลาคอมไพล์
Rafael Winterhalter

6
@RafaelWinterhalter - ใช่ ... นั่นคือคงที่กรณีข้อมูลแบบคงที่
Stephen C

2
@RafaelWinterhalter สิ่งนี้ไม่เป็นความจริงสำหรับStringตัวแปรหรือตัวแปร'static final' ทั้งหมดมีเพียงตัวแปรที่เริ่มต้นด้วยนิพจน์คงที่
Lew Bloch

1
ใช่และไม่จำเป็นต้องเป็นฟิลด์staticในขณะที่เป็นกรณีทั่วไป
Rafael Winterhalter

1
มันเป็นภาษาโปรแกรมเดียวกัน ใช่.
Stephen C

14

ฟิลด์สแตติกจะเริ่มต้นในระหว่างการเตรียมใช้งาน "เฟส" ของการโหลดคลาส (การโหลดการเชื่อมโยงและการเตรียมใช้งาน) ซึ่งรวมถึงการเริ่มต้นแบบคงที่และการกำหนดค่าเริ่มต้นของฟิลด์แบบคงที่ ตัวเริ่มต้นแบบคงที่จะดำเนินการตามลำดับข้อความตามที่กำหนดไว้ในคลาส

ลองพิจารณาตัวอย่าง:

public class Test {

   static String sayHello()  {
      return a;
   }

   static String b = sayHello(); // a static method is called to assign value to b.
                                 // but its a has not been initialized yet.

   static String a = "hello";

   static String c = sayHello(); // assignes "hello" to variable c

    public static void main(String[] arg) throws Throwable {
         System.out.println(Test.b); // prints null
         System.out.println(Test.sayHello()); // prints "hello"
    }
}

Test.b พิมพ์nullเนื่องจากเมื่อsayHelloถูกเรียกในขอบเขตแบบคงที่ตัวแปรคงaไม่ได้ถูกเตรียมใช้งาน


6
พูดอย่างเคร่งครัดการเริ่มต้นไม่ใช่ "เฟส" ของการโหลดคลาส อันที่จริงอาจมีการโหลดคลาสบางคลาสแต่จะไม่เริ่มต้นหากแอปพลิเคชันไม่ได้ใช้ประโยชน์
Stephen C

@Stephen C คุณพูดถูกฉันใช้มันโดยขาดคำที่ดีกว่าบางทีฉันอาจจะพูดมัน
naikus

@StephenC หมายความว่าในขณะที่การโหลดคลาสเกิดขึ้นจะกำหนดหน่วยความจำให้กับตัวแปรคงที่ (& วิธีการ) แต่ตัวแปรคงที่ไม่ได้เริ่มต้นด้วยค่าที่ระบุในโค้ด? เพราะที่นี่ดูเหมือนเมื่อ b-> sayHello () -> a, 'a' อยู่ในหน่วยความจำ แต่ยังไม่ได้กำหนดค่าให้กับมัน
Shabbir Essaji

โดยพื้นฐานแล้วใช่
Stephen C

1

ใช่ตัวเริ่มต้นแบบคงที่ทั้งหมดจะถูกเรียกใช้ก่อนที่คุณจะเข้าถึงคลาสในครั้งแรก ถ้าเป็นวิธีอื่นฉันจะเรียกว่าบั๊ก


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