ตัวแปรคงเริ่มต้นเมื่อใด


88

ฉันสงสัยว่าเมื่อใดที่ตัวแปรคงที่เริ่มต้นเป็นค่าเริ่มต้น ถูกต้องหรือไม่ที่เมื่อโหลดคลาสแล้วจะมีการสร้างตัวแปรแบบสแตติก (จัดสรร) จากนั้นตัวเริ่มต้นแบบคงที่และการกำหนดค่าเริ่มต้นในการประกาศจะถูกดำเนินการ มีการกำหนดค่าเริ่มต้นที่จุดใด สิ่งนี้นำไปสู่ปัญหาการอ้างอิงไปข้างหน้า

นอกจากนี้โปรดหากคุณสามารถอธิบายสิ่งนี้โดยอ้างอิงถึงคำถามที่ถามว่าทำไมฟิลด์แบบคงที่ไม่เริ่มต้นในเวลา และโดยเฉพาะอย่างยิ่งคำตอบที่ได้รับจาก Kevin Brock ในเว็บไซต์เดียวกัน ฉันไม่เข้าใจจุดที่ 3


2
โปรดแก้ไขคำถามของคุณเพื่อรวมใบเสนอราคาที่คุณอ้างถึง
Oliver Charlesworth

1
คุณอ่านข้อกำหนดภาษา Java แล้วหรือยัง? มันเป็นเอกสารที่น่าอ่านโดยเจตนา หากคุณได้อ่านแล้วคุณอาจเข้าใจว่าเกิดอะไรขึ้น ถ้าไม่คุณสามารถถามคำถามที่เฉพาะเจาะจงมากขึ้นอย่างน้อยที่สุด ...
Maarten Bodewes

ผมคิดว่า Q & A นี้เป็น dup ของstackoverflow.com/questions/3499214
Stephen C

คำตอบ:


72

จากดู Java Static Variable Methods :

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

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

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

ในกรณีของคลาสภายในพวกเขาไม่สามารถมีฟิลด์คงที่

ระดับชั้นstaticเป็นชั้นซ้อนกันที่ไม่ชัดเจนหรือโดยปริยายประกาศ

...

คลาสภายในไม่สามารถประกาศตัวเริ่มต้นแบบคงที่ (§8.7) หรือส่วนต่อประสานสมาชิก ...

คลาสภายในไม่สามารถประกาศสมาชิกแบบคงที่เว้นแต่จะเป็นตัวแปรคงที่ ...

ดู JLS 8.1.3 Inner Classes และ Enclosing Instances

finalฟิลด์ใน Java สามารถเริ่มต้นแยกต่างหากจากตำแหน่งการประกาศอย่างไรก็ตามไม่สามารถใช้กับstatic finalฟิลด์ได้ ดูตัวอย่างด้านล่าง

final class Demo
{
    private final int x;
    private static final int z;  //must be initialized here.

    static 
    {
        z = 10;  //It can be initialized here.
    }

    public Demo(int x)
    {
        this.x=x;  //This is possible.
        //z=15; compiler-error - can not assign a value to a final variable z
    }
}

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


5
In case of static inner classes, they can not have static fieldsดูเหมือนจะพิมพ์ผิด คลาสภายในไม่คงที่
Daniel Lubarov

อย่างไรก็ตามคุณควรใช้แทนแม้ว่า
Suraj Jain

เมื่อคุณเปิดใช้งาน JVM และโหลดคลาสเป็นครั้งแรก (ซึ่งทำได้โดย classloader เมื่อคลาสถูกอ้างอิงครั้งแรกไม่ว่าด้วยวิธีใดก็ตาม) บล็อกหรือฟิลด์แบบคงที่ใด ๆ จะถูก 'โหลด' ลงใน JVM และสามารถเข้าถึงได้
nhoxbypass

1
น่าเสียดายที่คำตอบนี้มีความไม่ถูกต้องตามข้อเท็จจริงบางประการเกี่ยวกับเวลาเริ่มต้นของสถิตยศาสตร์ โปรดดูstackoverflow.com/a/3499322/139985
Stephen C

15

ดู:

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

ฉันไม่แน่ใจว่าคำถามเฉพาะของคุณเกี่ยวกับจุดที่ 3 (สมมติว่าคุณหมายถึงคำถามที่ซ้อนกัน?) คืออะไร ลำดับโดยละเอียดระบุว่านี่จะเป็นการร้องขอการเริ่มต้นแบบเรียกซ้ำดังนั้นจึงจะเริ่มต้นต่อไป


12

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


10

ลำดับของการเริ่มต้นคือ:

  1. บล็อกการเริ่มต้นแบบคงที่
  2. บล็อกการเริ่มต้นอินสแตนซ์
  3. ตัวสร้าง

รายละเอียดของกระบวนการอธิบายไว้ในเอกสารข้อกำหนด JVM


6

ตัวแปรคงที่

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

4

เริ่มต้นด้วยรหัสจากคำถามอื่น:

class MyClass {
  private static MyClass myClass = new MyClass();
  private static final Object obj = new Object();
  public MyClass() {
    System.out.println(obj); // will print null once
  }
}

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

ตอนนี้กลับไปที่การเริ่มต้นคลาส: objมีการอ้างอิงถึงวัตถุจริงใหม่และเราทำเสร็จแล้ว

หากสิ่งนี้ถูกกำหนดโดยคำสั่งเช่น: MyClass mc = new MyClass();พื้นที่สำหรับอินสแตนซ์ MyClass ใหม่จะถูกจัดสรรอีกครั้ง (และการอ้างอิงที่อยู่ในmc) ตัวสร้างถูกเรียกใช้งานอีกครั้งและพิมพ์อีกครั้งobjซึ่งตอนนี้ไม่เป็นโมฆะ

เคล็ดลับที่แท้จริงที่นี่คือเมื่อคุณใช้newเนื่องจาก in WhatEverItIs weii = new WhatEverItIs( p1, p2 ); weiiจะได้รับการอ้างอิงถึงหน่วยความจำที่เป็นศูนย์ทันที JVM จะเริ่มต้นค่าและเรียกใช้ตัวสร้าง แต่ถ้าคุณอ้างอิงweii ก่อนหน้านั้น - โดยการอ้างอิงจากเธรดอื่นหรือหรือโดยการอ้างอิงจากการเริ่มต้นคลาสเช่นคุณกำลังดูอินสแตนซ์คลาสที่เต็มไปด้วยค่า null


1
คลาสจะไม่ถูกทำเครื่องหมายว่าเริ่มต้นจนกว่าการเริ่มต้นจะเสร็จสิ้นการทำอย่างอื่นจะไม่สมเหตุสมผล การทำเครื่องหมายว่าเริ่มต้นเป็นขั้นตอนสุดท้าย ดูJLS 12.4.2
Dave Newton

@DaveNewton: เมื่อมีบางสิ่งอ้างอิงถึงคลาสและเริ่มต้นการเริ่มต้นการอ้างอิงเพิ่มเติมทั้งหมดจะถือว่าคลาสนั้นเริ่มต้น พวกเขาจะไม่พยายามเริ่มต้นและจะไม่รอให้เริ่มต้น ดังนั้นฟิลด์ที่ดูเหมือนจะไม่เป็นค่าว่างจากการเริ่มต้นของโปรแกรมอาจเป็นค่าว่างได้ชั่วขณะ การไม่เข้าใจนี่คือสิ่งที่ทำให้เกิดความสับสน ฉันคิดว่าง่ายที่สุดที่จะบอกว่าคลาสที่ไม่ได้เริ่มต้นถูก "ทำเครื่องหมาย" ว่าเริ่มต้นในการอ้างอิงครั้งแรกการอ้างอิงอื่น ๆ ทั้งหมดถือว่าเป็นแบบเริ่มต้นและนั่นคือสาเหตุที่เกิดขึ้น
RalphChapin

การแก้ไขความคิดเห็นก่อนหน้านี้: ตามที่อธิบายไว้ JLS 12.4.2 ของ Dave Newton คลาสจะถูกล็อคขณะเริ่มต้น เธรดอื่น ๆจะรอให้คลาสเริ่มต้น อย่างไรก็ตามนั่นไม่ส่งผลกระทบต่อกรณีนี้ซึ่งทั้งหมดเกิดขึ้นในเธรดเดียว
RalphChapin

4

ตัวแปรคงที่สามารถกำหนดค่าเริ่มต้นได้สามวิธีดังต่อไปนี้เลือกตัวเลือกที่คุณต้องการ

  1. คุณสามารถเริ่มต้นได้ในเวลาที่ประกาศ
  2. หรือคุณสามารถทำได้โดยสร้างบล็อกแบบคงที่เช่น:

    static {
            // whatever code is needed for initialization goes here
        }
    
  3. มีทางเลือกอื่นสำหรับบล็อกแบบคงที่ - คุณสามารถเขียนวิธีการคงที่ส่วนตัวได้

    class name {
        public static varType myVar = initializeVar();
    
        private static varType initializeVar() {
            // initialization code goes here
        }
    }
    
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.