JavaRush /จาวาบล็อก /Random-TH /การมอบหมายและการเริ่มต้นใน Java
Viacheslav
ระดับ

การมอบหมายและการเริ่มต้นใน Java

เผยแพร่ในกลุ่ม

การแนะนำ

วัตถุประสงค์หลักของโปรแกรมคอมพิวเตอร์คือการประมวลผลข้อมูล ในการประมวลผลข้อมูลคุณต้องจัดเก็บข้อมูลไว้ด้วยวิธีใดวิธีหนึ่ง ฉันเสนอให้ทำความเข้าใจวิธีการจัดเก็บข้อมูล
การมอบหมายและการเริ่มต้นใน Java - 1

ตัวแปร

ตัวแปรคือคอนเทนเนอร์ที่เก็บข้อมูลใดๆ ลองดูบทช่วยสอนอย่างเป็นทางการจาก Oracle: Declaring Member Variables ตามบทช่วยสอนนี้มีตัวแปรหลายประเภท:
  • ฟิลด์ : ตัวแปรที่ประกาศในคลาส;
  • ตัวแปรท้องถิ่น : ตัวแปรในวิธีการหรือบล็อกของโค้ด
  • พารามิเตอร์ : ตัวแปรในการประกาศวิธีการ (ในลายเซ็น)
ตัวแปรทั้งหมดจะต้องมีประเภทตัวแปรและชื่อตัวแปร
  • ประเภทของตัวแปรจะบ่งบอกว่าตัวแปรเป็นตัวแทนข้อมูลใด (นั่นคือ ข้อมูลใดที่ตัวแปรสามารถจัดเก็บได้) ดังที่เราทราบ ประเภทของตัวแปรอาจเป็นชนิดดั้งเดิม (primitives )หรือobjectไม่ใช่ชนิดดั้งเดิม (Non-primitive) ด้วยตัวแปรอ็อบเจ็กต์ ประเภทของตัวแปรจะถูกอธิบายโดยคลาสเฉพาะ
  • ชื่อตัวแปรจะต้องเป็นตัวพิมพ์เล็ก ในกรณีอูฐ คุณสามารถอ่านเพิ่มเติมเกี่ยวกับการตั้งชื่อได้ใน " ตัวแปร:การตั้งชื่อ "
นอกจากนี้หากเป็นตัวแปรระดับชั้นเรียนเช่น เป็นฟิลด์คลาส สามารถระบุตัวแก้ไขการเข้าถึงได้ โปรดดู การควบคุมการเข้าถึง สมาชิก ของคลาส สำหรับรายละเอียดเพิ่มเติม

การประกาศตัวแปร

ดังนั้นเราจึงจำได้ว่าตัวแปรคืออะไร ในการเริ่มทำงานกับตัวแปร คุณต้องประกาศตัวแปรนั้น ก่อนอื่น มาดูตัวแปรท้องถิ่นกันก่อน แทนที่จะใช้ IDE เพื่อความสะดวก เราจะใช้โซลูชันออนไลน์จาก Tutorialspoint: Online IDE มารันโปรแกรมง่ายๆ นี้ใน IDE ออนไลน์:
public class HelloWorld{
    public static void main(String []args){
        int number;
        System.out.println(number);
    }
}
อย่างที่คุณเห็น เราได้ประกาศตัวแปรโลคัลด้วยชื่อnumberและintประเภท เรากดปุ่ม "ดำเนินการ" และได้รับข้อผิดพลาด:
HelloWorld.java:5: error: variable number might not have been initialized
        System.out.println(number);
เกิดอะไรขึ้น เราประกาศตัวแปร แต่ไม่ได้เริ่มต้นค่าของมัน เป็นที่น่าสังเกตว่าข้อผิดพลาดนี้ไม่ได้เกิดขึ้นในเวลาดำเนินการ (เช่น ไม่ใช่ในรันไทม์) แต่เกิดขึ้นในเวลาคอมไพล์ สมาร์ทคอมไพเลอร์ตรวจสอบว่าตัวแปรโลคัลจะเริ่มต้นได้ก่อนที่จะเข้าถึงหรือไม่ ดังนั้นข้อความต่อไปนี้จึงเป็นไปตามนี้:
  • ควรเข้าถึงตัวแปรท้องถิ่นหลังจากที่เริ่มต้นแล้วเท่านั้น
  • ตัวแปรท้องถิ่นไม่มีค่าเริ่มต้น
  • ค่าของตัวแปรท้องถิ่นจะถูกตรวจสอบ ณ เวลารวบรวม
ดังนั้นเราจึงบอกว่าต้องเริ่มต้นตัวแปร การเริ่มต้นตัวแปรคือการกำหนดค่าให้กับตัวแปร ถ้าอย่างนั้นเรามาดูกันว่ามันคืออะไรและทำไม

การเริ่มต้นตัวแปรท้องถิ่น

การเริ่มต้นตัวแปรเป็นหนึ่งในหัวข้อที่ยากที่สุดใน Java เพราะ... มีความสัมพันธ์อย่างใกล้ชิดกับการทำงานกับหน่วยความจำ การใช้งาน JVM ข้อกำหนด JVM และสิ่งอื่น ๆ ที่น่ากลัวและยุ่งยากไม่แพ้กัน แต่คุณสามารถลองคิดออกได้อย่างน้อยก็ในระดับหนึ่ง เริ่มจากง่ายไปซับซ้อนกันดีกว่า ในการเริ่มต้นตัวแปร เราจะใช้ตัวดำเนินการกำหนดและเปลี่ยนบรรทัดในโค้ดก่อนหน้า:
int number = 2;
ในตัวเลือกนี้จะไม่มีข้อผิดพลาดและค่าจะแสดงบนหน้าจอ จะเกิดอะไรขึ้นในกรณีนี้? เรามาลองให้เหตุผลกัน หากเราต้องการกำหนดค่าให้กับตัวแปร เราก็ต้องการให้ตัวแปรนั้นเก็บค่าไว้ ปรากฎว่าต้องเก็บค่าไว้ที่ไหนสักแห่ง แต่ที่ไหน? บนดิสก์? แต่การดำเนินการนี้ช้ามากและอาจกำหนดข้อจำกัดให้กับเรา ปรากฎว่าที่เดียวที่เราสามารถจัดเก็บข้อมูล "ที่นี่และเดี๋ยวนี้" ได้อย่างรวดเร็วและมีประสิทธิภาพคือหน่วยความจำ ซึ่งหมายความว่าเราจำเป็นต้องจัดสรรพื้นที่ในหน่วยความจำบางส่วน นี่เป็นเรื่องจริง เมื่อตัวแปรถูกเตรียมใช้งาน พื้นที่จะถูกจัดสรรให้กับตัวแปรในหน่วยความจำที่จัดสรรให้กับกระบวนการ Java ซึ่งโปรแกรมของเราจะถูกดำเนินการ หน่วยความจำที่จัดสรรให้กับกระบวนการ Java แบ่งออกเป็นหลายพื้นที่หรือโซน สิ่งใดที่จะจัดสรรพื้นที่ขึ้นอยู่กับประเภทของตัวแปรที่ถูกประกาศ หน่วยความจำแบ่งออกเป็นส่วนต่างๆ ดังต่อไปนี้: Heap, Stack และ Non- Heap เริ่มจากหน่วยความจำสแต็กกันก่อน Stackแปลว่ากอง (เช่น กองหนังสือ) เป็นโครงสร้างข้อมูลแบบ LIFO (เข้าหลังออกก่อน) นั่นก็คือเหมือนกองหนังสือ เมื่อเราเพิ่มหนังสือเข้าไป เราจะวางหนังสือไว้ด้านบน และเมื่อเรานำหนังสือออกไป เราก็นำหนังสือที่อยู่ด้านบนสุด (นั่นคือ หนังสือที่เพิ่มเข้ามาล่าสุด) ดังนั้นเราจึงเปิดตัวโปรแกรมของเรา ดังที่เราทราบ โปรแกรม Java ถูกดำเนินการโดย JVM ซึ่งก็คือ Java virtual machine JVM ต้องทราบว่าการเรียกใช้โปรแกรมควรเริ่มต้นที่ใด เมื่อต้องการทำเช่นนี้ เราประกาศวิธีการหลัก ซึ่งเรียกว่า "จุดเริ่มต้น" สำหรับการดำเนินการใน JVM เธรดหลัก (Thread) จะถูกสร้างขึ้น เมื่อเธรดถูกสร้างขึ้น เธรดจะถูกจัดสรรในหน่วยความจำของตัวเอง สแต็กนี้ประกอบด้วยเฟรม เมื่อแต่ละวิธีใหม่ถูกดำเนินการในเธรด เฟรมใหม่จะถูกจัดสรรและเพิ่มไปที่ด้านบนสุดของสแต็ก (เหมือนกับหนังสือเล่มใหม่ในกองหนังสือ) เฟรมนี้จะมีการอ้างอิงถึงออบเจ็กต์และประเภทดั้งเดิม ใช่ ใช่ int ของเราจะถูกเก็บไว้ในสแต็ก เพราะ... int เป็นประเภทดั้งเดิม ก่อนที่จะจัดสรรเฟรม JVM ต้องเข้าใจว่าจะบันทึกอะไรไว้ที่นั่น ด้วยเหตุนี้เราจึงได้รับข้อผิดพลาด “ตัวแปรอาจไม่ได้เตรียมใช้งาน” เพราะหากไม่ได้เตรียมใช้งาน JVM จะไม่สามารถเตรียมสแต็กให้เราได้ ดังนั้นเมื่อคอมไพล์โปรแกรม คอมไพเลอร์อัจฉริยะจะช่วยเราหลีกเลี่ยงข้อผิดพลาดและทำลายทุกสิ่ง (!)เพื่อความชัดเจน ฉันขอแนะนำ บทความ super-duper : “ Java Stack and Heap: Java Memory Allocation Tutorial ” มันเชื่อมโยงไปยังวิดีโอสุดเจ๋งไม่แพ้กัน:
หลังจากที่การดำเนินการของวิธีการเสร็จสิ้น เฟรมที่จัดสรรสำหรับวิธีการเหล่านี้จะถูกลบออกจากสแต็กของเธรด และหน่วยความจำที่จัดสรรสำหรับเฟรมนี้พร้อมกับข้อมูลทั้งหมดจะถูกล้างพร้อมกับเฟรมเหล่านั้น

การเริ่มต้นตัวแปรออบเจ็กต์ในเครื่อง

มาเปลี่ยนโค้ดของเราอีกครั้งให้ซับซ้อนกว่านี้หน่อย:
public class HelloWorld{

    private int number = 2;

    public static void main(String []args){
        HelloWorld object = new HelloWorld();
        System.out.println(object.number);
    }

}
จะเกิดอะไรขึ้นที่นี่? มาพูดถึงมันอีกครั้ง JVM รู้ว่าควรรันโปรแกรมจากที่ใด เช่น เธอเห็นวิธีการหลัก มันสร้างเธรดและจัดสรรหน่วยความจำให้ (ท้ายที่สุดแล้ว เธรดจำเป็นต้องจัดเก็บข้อมูลที่จำเป็นสำหรับการดำเนินการที่ไหนสักแห่ง) ในเธรดนี้ เฟรมจะถูกจัดสรรสำหรับวิธีการหลัก ต่อไปเราจะสร้างวัตถุ HelloWorld ออบเจ็กต์นี้ไม่ได้สร้างบนสแต็กอีกต่อไป แต่สร้างบนฮีป เนื่องจากวัตถุไม่ใช่ประเภทดั้งเดิม แต่เป็นประเภทวัตถุ และสแต็กจะจัดเก็บการอ้างอิงไปยังวัตถุในฮีปเท่านั้น (เราต้องเข้าถึงวัตถุนี้ด้วยวิธีใดวิธีหนึ่ง) ถัดไป ในสแต็กของเมธอดหลัก เฟรมจะถูกจัดสรรเพื่อดำเนินการเมธอด println หลังจากดำเนินการตามวิธีการหลักแล้ว เฟรมทั้งหมดจะถูกทำลาย หากเฟรมถูกทำลาย ข้อมูลทั้งหมดจะถูกทำลาย วัตถุวัตถุจะไม่ถูกทำลายทันที ประการแรก การอ้างอิงถึงวัตถุนั้นจะถูกทำลาย และจะไม่มีใครอ้างถึงวัตถุวัตถุอีกต่อไป และการเข้าถึงวัตถุนี้ในหน่วยความจำจะไม่สามารถทำได้อีกต่อไป JVM ที่ชาญฉลาดมีกลไกของตัวเองสำหรับสิ่งนี้ - ตัวรวบรวมขยะ (ตัวเก็บขยะหรือ GC สั้น ๆ ) จากนั้นจะลบออบเจ็กต์หน่วยความจำที่ไม่มีใครอ้างอิงออกจากออบเจ็กต์ กระบวนการนี้ได้รับการอธิบายอีกครั้งในลิงก์ที่ให้ไว้ด้านบน มีแม้กระทั่งวิดีโอพร้อมคำอธิบาย

กำลังเริ่มต้นฟิลด์

การเริ่มต้นฟิลด์ที่ระบุในคลาสจะเกิดขึ้นในลักษณะพิเศษ ขึ้นอยู่กับว่าฟิลด์เป็นแบบคงที่หรือไม่ หากฟิลด์มีคีย์เวิร์ด static ฟิลด์นี้จะอ้างอิงถึงคลาสนั้นเอง และหากไม่ได้ระบุคำว่า static ฟิลด์นี้จะอ้างอิงถึงอินสแตนซ์ของคลาส ลองดูตัวอย่างนี้:
public class HelloWorld{
    private int number;
    private static int count;

    public static void main(String []args){
        HelloWorld object = new HelloWorld();
        System.out.println(object.number);
    }
}
ในตัวอย่างนี้ ฟิลด์ต่างๆ จะถูกเตรียมใช้งานในเวลาที่ต่างกัน ฟิลด์หมายเลขจะเริ่มต้นได้หลังจากสร้างออบเจ็กต์คลาส HelloWorld แต่ฟิลด์การนับจะเริ่มต้นได้เมื่อคลาสถูกโหลดโดยเครื่องเสมือน Java การโหลดชั้นเรียนเป็นหัวข้อที่แยกจากกัน ดังนั้นเราจะไม่รวมไว้ที่นี่ เป็นเรื่องที่คุ้มค่าที่จะรู้ว่าตัวแปรสแตติกจะเริ่มต้นได้เมื่อคลาสเป็นที่รู้จักในขณะรันไทม์ มีอย่างอื่นที่สำคัญกว่าที่นี่และคุณได้สังเกตเห็นสิ่งนี้แล้ว เราไม่ได้ระบุค่าใดๆ แต่ใช้งานได้ และแน่นอน ตัวแปรที่เป็นฟิลด์ หากไม่มีการระบุค่า ตัวแปรเหล่านั้นจะถูกเตรียมใช้งานด้วยค่าเริ่มต้น สำหรับค่าตัวเลขจะเป็น 0 หรือ 0.0 สำหรับตัวเลขทศนิยม สำหรับบูลีนนี่เป็นเท็จ และสำหรับตัวแปรประเภทวัตถุทั้งหมด ค่าจะเป็นโมฆะ (เราจะพูดถึงเรื่องนี้ในภายหลัง) ดูเหมือนว่าทำไมถึงเป็นเช่นนั้น? แต่เนื่องจากวัตถุถูกสร้างขึ้นในฮีป (ในฮีป) การทำงานกับพื้นที่นี้ดำเนินการในรันไทม์ และเราสามารถเริ่มต้นตัวแปรเหล่านี้ได้ในขณะรันไทม์ ซึ่งต่างจากสแต็กตรงที่หน่วยความจำจะต้องเตรียมไว้ก่อนดำเนินการ นี่คือการทำงานของหน่วยความจำใน Java แต่มีคุณสมบัติอีกอย่างหนึ่งที่นี่ ชิ้นส่วนเล็กๆ นี้สัมผัสถึงมุมต่างๆ ของความทรงจำ อย่างที่เราจำได้ เฟรมจะถูกจัดสรรในหน่วยความจำ Stack สำหรับวิธีการหลัก เฟรมนี้จัดเก็บการอ้างอิงไปยังวัตถุในหน่วยความจำฮีป แต่นับแล้วเก็บไว้ที่ไหน? อย่างที่เราจำได้ ตัวแปรนี้จะถูกเตรียมใช้งานทันที ก่อนที่ออบเจ็กต์จะถูกสร้างขึ้นในฮีป นี่เป็นคำถามที่ยุ่งยากจริงๆ ก่อน Java 8 มีพื้นที่หน่วยความจำที่เรียกว่า PERMGEN ตั้งแต่ Java 8 เป็นต้นไป พื้นที่นี้มีการเปลี่ยนแปลงและเรียกว่า METASPACE โดยพื้นฐานแล้ว ตัวแปรคงที่เป็นส่วนหนึ่งของคำจำกัดความของคลาส เช่น ข้อมูลเมตาของมัน ดังนั้นจึงมีเหตุผลที่จะจัดเก็บไว้ในที่เก็บข้อมูลเมตา METASPACE MetaSpace อยู่ในพื้นที่หน่วยความจำ Non-Heap เดียวกันและเป็นส่วนหนึ่งของพื้นที่นั้น สิ่งสำคัญคือต้องคำนึงถึงลำดับการประกาศตัวแปรด้วย ตัวอย่างเช่น มีข้อผิดพลาดในรหัสนี้:
public class HelloWorld{

    private static int b = a;
    private static int a = 1;

    public static void main(String []args){
        System.out.println(b);
    }

}

อะไรเป็นโมฆะ

ตามที่ระบุไว้ข้างต้น ตัวแปรของประเภทวัตถุ หากเป็นฟิลด์ของคลาส จะถูกเตรียมใช้งานเป็นค่าเริ่มต้น และค่าเริ่มต้นนั้นเป็นโมฆะ แต่อะไรเป็นโมฆะใน Java? สิ่งแรกที่ต้องจำคือประเภทดั้งเดิมไม่สามารถเป็นค่าว่างได้ และทั้งหมดเนื่องจาก null คือการอ้างอิงพิเศษที่ไม่ได้อ้างอิงถึงวัตถุใด ๆ ดังนั้นเฉพาะตัวแปรอ็อบเจ็กต์เท่านั้นที่สามารถเป็นโมฆะได้ สิ่งที่สองที่สำคัญที่ต้องเข้าใจคือ null คือการอ้างอิง ฉันอ้างอิงยังมีน้ำหนักของพวกเขา ในหัวข้อนี้ คุณสามารถอ่านคำถามเกี่ยวกับ stackoverflow: " ตัวแปร null ต้องการพื้นที่ในหน่วยความจำ " หรือไม่

บล็อกการเริ่มต้น

เมื่อพิจารณาการกำหนดค่าเริ่มต้นของตัวแปร การไม่พิจารณาบล็อกการกำหนดค่าเริ่มต้นถือเป็นบาป ดูเหมือนว่านี้:
public class HelloWorld{

    static {
        System.out.println("static block");
    }

    {
        System.out.println("block");
    }

    public HelloWorld () {
        System.out.println("Constructor");
    }

    public static void main(String []args){
        HelloWorld obj = new HelloWorld();
    }

}
ลำดับเอาต์พุตจะเป็น: บล็อกแบบคงที่, บล็อก, ตัวสร้าง ดังที่เราเห็น บล็อกการเริ่มต้นจะถูกดำเนินการก่อนตัวสร้าง และบางครั้งนี่อาจเป็นวิธีเริ่มต้นที่สะดวก

บทสรุป

ฉันหวังว่าภาพรวมสั้นๆ นี้สามารถให้ข้อมูลเชิงลึกเกี่ยวกับวิธีการทำงานและเหตุผลได้ #เวียเชสลาฟ
ความคิดเห็น
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION