เหตุใดตัวแปรท้องถิ่นจึงไม่เริ่มต้นใน Java


104

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

และยังนำไปสู่ปัญหาตามที่อธิบายไว้ในความคิดเห็นนี้ในบล็อกโพสต์ :

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

น่าผิดหวังมาก



1
ขออภัยด้วย ... คำถามนี้ไม่ได้ป๊อปอัปเมื่อฉันพิมพ์คำถาม .. อย่างไรก็ตามฉันเดาว่ามีความแตกต่างระหว่างสองคำถาม ... ฉันต้องการทราบว่าทำไมนักออกแบบของ Java จึงออกแบบด้วยวิธีนี้ในขณะที่ คำถามที่คุณชี้ไปไม่ได้ถามว่า ...
Shivasubramanian A

ดูคำถาม C # ที่เกี่ยวข้อง: stackoverflow.com/questions/1542824/…
Raedwald

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

คำตอบ:


62

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


23

ดูเหมือนว่า"ปัญหา" ที่คุณเชื่อมโยงจะอธิบายสถานการณ์นี้:

SomeObject so;
try {
  // Do some work here ...
  so = new SomeObject();
  so.DoUsefulThings();
} finally {
  so.CleanUp(); // Compiler error here
}

ข้อร้องเรียนของผู้แสดงความคิดเห็นคือคอมไพเลอร์หยุดอยู่ที่บรรทัดในfinallyส่วนโดยอ้างว่าsoอาจไม่ได้เริ่มต้น จากนั้นความคิดเห็นกล่าวถึงวิธีการเขียนโค้ดอีกวิธีหนึ่งซึ่งอาจเป็นดังนี้:

// Do some work here ...
SomeObject so = new SomeObject();
try {
  so.DoUsefulThings();
} finally {
  so.CleanUp();
}

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

อย่างไรก็ตามโค้ดเวอร์ชันที่สองนี้เป็นวิธีที่ถูกต้องในการเขียน ในเวอร์ชันแรกข้อความแสดงข้อผิดพลาดของคอมไพเลอร์ถูกต้อง soตัวแปรอาจจะมีการเตรียม โดยเฉพาะอย่างยิ่งถ้าSomeObjectคอนสตรัคล้มเหลวจะไม่สามารถเริ่มต้นและดังนั้นมันจะเป็นข้อผิดพลาดในการพยายามที่จะเรียกร้องso so.CleanUpเข้าสู่tryส่วนนี้ทุกครั้งหลังจากที่คุณได้รับทรัพยากรที่finallyส่วนนั้นจบลง

try- finallyบล็อกหลังจากที่soเริ่มต้นมีเพียงเพื่อปกป้องSomeObjectเช่นเพื่อให้แน่ใจว่าจะได้รับการทำความสะอาดขึ้นไม่ว่าอะไรเกิดขึ้น หากมีคนอื่น ๆสิ่งที่จำเป็นต้องใช้ แต่พวกเขาจะไม่เกี่ยวข้องกับการไม่ว่าจะเป็นSomeObjectตัวอย่างได้รับการจัดสรรทรัพย์สินแล้วพวกเขาก็ควรจะไปในอีก try - finallyบล็อกอาจเป็นหนึ่งที่ wraps หนึ่งที่ผมเคยแสดง

การกำหนดตัวแปรด้วยตนเองก่อนการใช้งานไม่ได้นำไปสู่ปัญหาที่แท้จริง เพียงนำไปสู่ความยุ่งยากเล็กน้อย แต่รหัสของคุณจะดีกว่าสำหรับมัน คุณจะมีตัวแปรที่มีขอบเขต จำกัด มากขึ้นและtry- finallyบล็อกที่ไม่พยายามปกป้องมากเกินไป

หากตัวแปรท้องถิ่นมีค่าเริ่มต้นแล้วในตัวอย่างแรกจะได้รับso nullนั่นจะไม่สามารถแก้ไขอะไรได้จริงๆ แทนที่จะได้รับข้อผิดพลาดเวลาคอมไพล์ในfinallyบล็อกคุณต้องNullPointerExceptionซุ่มซ่อนอยู่ที่นั่นซึ่งอาจซ่อนข้อยกเว้นอื่น ๆ ที่อาจเกิดขึ้นในส่วน "ทำงานที่นี่" ของโค้ด (หรือทำข้อยกเว้นในfinallyส่วนที่เชื่อมโยงกับข้อยกเว้นก่อนหน้าโดยอัตโนมัติฉันจำไม่ได้ถึงอย่างนั้นคุณจะมีข้อยกเว้นพิเศษในแบบของจริง)


2
ทำไมไม่มี if (so! = null) ... ในบล็อกสุดท้ายล่ะ?
izb

ที่จะยังคงทำให้เกิดคำเตือน / ข้อผิดพลาดของคอมไพเลอร์ - ฉันไม่คิดว่าคอมไพเลอร์เข้าใจว่าถ้าตรวจสอบ (แต่ฉันแค่ทำสิ่งนี้ออกจากหน่วยความจำไม่ได้ทดสอบ)
Chii

6
ฉันจะใส่แค่ SomeObject so = null ก่อนที่จะลองและใส่ null check ในท้ายที่สุด วิธีนี้จะไม่มีคำเตือนของคอมไพเลอร์
Juha Syrjälä

ทำไมสิ่งที่ซับซ้อน? เขียน try-block ในที่สุดด้วยวิธีนี้และคุณรู้ว่าตัวแปรมีค่าที่ถูกต้อง ไม่จำเป็นต้องมีการตรวจสอบค่าว่าง
Rob Kennedy

1
Rob ตัวอย่าง "SomeObject () ใหม่" ของคุณนั้นเรียบง่ายและไม่ควรสร้างข้อยกเว้นที่นั่น แต่ถ้าการโทรสามารถสร้างข้อยกเว้นได้ก็จะเป็นการดีกว่าที่จะให้มันเกิดขึ้นภายในบล็อกการลองเพื่อให้สามารถจัดการได้
Sarel Botha

12

ยิ่งไปกว่านั้นในตัวอย่างด้านล่างอาจมีข้อยกเว้นเกิดขึ้นภายในโครงสร้าง SomeObject ซึ่งในกรณีนี้ตัวแปร 'so' จะเป็นโมฆะและการเรียก CleanUp จะทำให้ NullPointerException

SomeObject so;
try {
  // Do some work here ...
  so = new SomeObject();
  so.DoUsefulThings();
} finally {
  so.CleanUp(); // Compiler error here
}

สิ่งที่ฉันมักจะทำคือ:

SomeObject so = null;
try {
  // Do some work here ...
  so = new SomeObject();
  so.DoUsefulThings();
} finally {
  if (so != null) {
     so.CleanUp(); // safe
  }
}

12
คุณอยากทำอะไร
พระช่างไฟฟ้า

2
ใช่น่าเกลียด ใช่มันเป็นสิ่งที่ฉันทำเกินไป
SMBiggs

@ElectricMonk รูปแบบไหนที่คุณคิดว่าดีกว่ารูปแบบที่คุณแสดงหรือที่แสดงไว้ที่นี่ในเมธอด getContents (.. ): javapractices.com/topic/TopicAction.do?Id=126
Atom

11

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

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


9

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

ทำไมไม่ทำตามขั้นตอนพิเศษ? ย้อนกลับไป - ไม่มีใครพูดถึงว่า "คำเตือน" ในกรณีนี้เป็นสิ่งที่ดีมาก

คุณไม่ควรเริ่มต้นตัวแปรของคุณเป็นศูนย์หรือว่างในการส่งครั้งแรก (เมื่อคุณเข้ารหัสครั้งแรก) ไม่ว่าจะกำหนดให้เป็นค่าจริงหรือไม่กำหนดเลยเพราะถ้าคุณไม่ทำ java จะสามารถบอกคุณได้เมื่อคุณทำผิดพลาดจริงๆ ใช้คำตอบของ Electric Monk เป็นตัวอย่างที่ดี ในกรณีแรกมันมีประโยชน์อย่างน่าอัศจรรย์จริง ๆ ที่จะบอกคุณว่าถ้าการลอง () ล้มเหลวเนื่องจากตัวสร้างของ SomeObject มีข้อยกเว้นคุณจะได้ NPE ในที่สุด หากตัวสร้างไม่สามารถโยนข้อยกเว้นได้ก็ไม่ควรอยู่ในการลอง

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

ยิ่งไปกว่านั้นการพูดอย่างชัดเจนว่า "int size = 0" แทนที่จะเป็น "int size" อย่างชัดเจนและทำให้โปรแกรมเมอร์คนต่อไปคิดออกว่าคุณตั้งใจให้เป็นศูนย์หรือไม่

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


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

4

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


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

2
ใช่ แต่อาจมีคนโต้แย้งว่ามันช่วยให้โปรแกรมเมอร์รู้ว่าเขาหรือเธอทิ้งตัวแปรไว้โดยไม่ได้เริ่มต้นโดยไม่ได้ตั้งใจหรือไม่
Mehrdad Afshari

1
คอมไพเลอร์สามารถทำเช่นนั้นได้ในทั้งสองกรณีเช่นกัน :) โดยส่วนตัวแล้วฉันต้องการให้คอมไพเลอร์ถือว่าตัวแปรที่ไม่ได้เริ่มต้นเป็นข้อผิดพลาด หมายความว่าฉันอาจทำผิดพลาดที่ไหนสักแห่ง
Greg Hewgill

ฉันไม่ใช่คนชวา แต่ฉันชอบวิธีการจัดการแบบ C # ความแตกต่างคือในกรณีนั้นคอมไพเลอร์ต้องออกคำเตือนซึ่งอาจทำให้คุณได้รับคำเตือนสองสามร้อยรายการสำหรับโปรแกรมที่ถูกต้องของคุณ)
Mehrdad Afshari

มันเตือนสำหรับตัวแปรสมาชิกด้วยหรือไม่?
Adeel Ansari

4

(อาจดูแปลกที่จะโพสต์คำตอบใหม่หลังจากคำถามนั้นนาน แต่กลับมีคำตอบที่ซ้ำกัน )

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

ที่กล่าวว่าฉันสามารถทำได้โดยสิ้นเชิงที่ต้องการให้ตัวแปรอินสแตนซ์นั้นเริ่มต้นอย่างชัดเจนเสมอ ข้อผิดพลาดจะเกิดขึ้นกับตัวสร้างใด ๆ ที่ผลลัพธ์อนุญาตให้มีตัวแปรอินสแตนซ์ที่ไม่ได้กำหนดค่าเริ่มต้น (เช่นไม่ได้เริ่มต้นเมื่อประกาศและไม่ได้อยู่ในตัวสร้าง) แต่นั่นไม่ใช่การตัดสินใจของ Gosling และอื่น ๆ อัลใช้เวลาในช่วงต้นทศวรรษที่ 90 ดังนั้นเราจึงอยู่ที่นี่ (และฉันไม่ได้บอกว่าพวกเขาโทรผิด)

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


"ที่กล่าวว่าฉันสามารถทำได้โดยสิ้นเชิงที่ต้องการให้ตัวแปรอินสแตนซ์เริ่มต้นอย่างชัดเจนเสมอ ... "ซึ่ง FWIW เป็นแนวทางที่พวกเขาใช้ใน TypeScript
TJ Crowder

3

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

ในกรณีที่คุณต้องการตัวแปรเพื่อเริ่มต้นคุณสามารถทำได้ด้วยตัวเองเสมอดังนั้นจึงไม่ใช่ปัญหา


3

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

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

System.out.println("Enter grade");
int grade = new Scanner(System.in).nextInt(); //I won't bother with exception handling here, to cut down on lines.
char letterGrade; //let us assume the default value for a char is '\0'
if (grade >= 90)
    letterGrade = 'A';
else if (grade >= 80)
    letterGrade = 'B';
else if (grade >= 70)
    letterGrade = 'C';
else if (grade >= 60)
    letterGrade = 'D';
else
    letterGrade = 'F';
System.out.println("Your grade is " + letterGrade);

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

การทดสอบโค้ดของเราอาจส่งผลดังต่อไปนี้

Enter grade
43
Your grade is

ผลลัพธ์นี้ไม่ได้เป็นไปตามเจตนาของผู้เขียนโค้ด อันที่จริงอาจเป็นไปได้ว่าในกรณีส่วนใหญ่ (หรืออย่างน้อยก็คือตัวเลขที่มีนัยสำคัญ) ค่าเริ่มต้นจะไม่เป็นค่าที่ต้องการดังนั้นในกรณีส่วนใหญ่ค่าเริ่มต้นจะทำให้เกิดข้อผิดพลาด มันทำให้รู้สึกมากขึ้นในการบังคับ coder ในการกำหนดค่าเริ่มต้นให้กับตัวแปรท้องถิ่นก่อนที่จะใช้มันตั้งแต่ความเศร้าโศกของการแก้ไขข้อบกพร่องที่เกิดจากการลืม= 1ในfor(int i = 1; i < 10; i++)มากเมื่อเทียบกับความสะดวกในการไม่ต้องรวมถึงที่ใน= 0for(int i; i < 10; i++)

เป็นเรื่องจริงที่การบล็อกแบบ try-catch ในที่สุดอาจยุ่งเล็กน้อย (แต่จริงๆแล้วมันไม่ใช่ catch-22 อย่างที่อ้างถึง) ตัวอย่างเช่นเมื่อวัตถุพ่นข้อยกเว้นที่ตรวจสอบแล้วในตัวสร้าง แต่สำหรับหนึ่ง เหตุผลหรืออย่างอื่นต้องทำบางอย่างกับวัตถุนี้ในตอนท้ายของบล็อกในที่สุด ตัวอย่างที่สมบูรณ์แบบคือเมื่อต้องจัดการกับทรัพยากรซึ่งต้องปิด

วิธีหนึ่งในการจัดการเรื่องนี้ในอดีตอาจเป็นเช่นนั้น ...

Scanner s = null; //declared and initialized to null outside the block. This gives us the needed scope, and an initial value.
try {
    s = new Scanner(new FileInputStream(new File("filename.txt")));
    int someInt = s.nextInt();
} catch (InputMismatchException e) {
    System.out.println("Some error message");
} catch (IOException e) {
    System.out.println("different error message"); 
} finally {
    if (s != null) //in case exception during initialization prevents assignment of new non-null value to s.
        s.close();
}

อย่างไรก็ตามสำหรับ Java 7 ในที่สุดการบล็อกนี้ก็ไม่จำเป็นอีกต่อไปโดยใช้ try-with-resources เช่นนั้น

try (Scanner s = new Scanner(new FileInputStream(new File("filename.txt")))) {
...
...
} catch(IOException e) {
    System.out.println("different error message");
}

ที่กล่าวว่า (ตามชื่อ) สิ่งนี้ใช้ได้กับทรัพยากรเท่านั้น

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

เป็นความจริงที่ว่าช่องต่างๆจะเริ่มต้นเป็นค่าเริ่มต้น แต่จะแตกต่างกันเล็กน้อย ตัวอย่างเช่นเมื่อคุณพูดว่าint[] arr = new int[10];ทันทีที่คุณเริ่มต้นอาร์เรย์นี้วัตถุจะอยู่ในหน่วยความจำ ณ ตำแหน่งที่กำหนด สมมติว่าไม่มีค่าเริ่มต้นสักครู่ แต่แทนค่าเริ่มต้นคือชุดของ 1s และ 0s ใด ๆ ที่เกิดขึ้นในตำแหน่งหน่วยความจำนั้นในขณะนั้น สิ่งนี้อาจนำไปสู่พฤติกรรมที่ไม่ได้กำหนดในหลาย ๆ กรณี

สมมติว่าเรามี ...

int[] arr = new int[10];
if(arr[0] == 0)
    System.out.println("Same.");
else
    System.out.println("Not same.");

จะเป็นไปได้อย่างสมบูรณ์แบบที่Same.อาจแสดงในการรันหนึ่งและNot same.อาจแสดงในอีกรันหนึ่ง ปัญหาอาจจะยิ่งหนักใจเมื่อคุณเริ่มพูดถึงตัวแปรอ้างอิง

String[] s = new String[5];

ตามคำจำกัดความแต่ละองค์ประกอบของ s ควรชี้ไปที่สตริง (หรือเป็นโมฆะ) อย่างไรก็ตามหากค่าเริ่มต้นเป็นอนุกรม 0 และ 1 ใด ๆ ที่เกิดขึ้นที่ตำแหน่งหน่วยความจำนี้ไม่เพียง แต่จะไม่มีการรับประกันว่าคุณจะได้ผลลัพธ์เดียวกันในแต่ละครั้ง แต่ยังไม่มีการรับประกันว่าวัตถุนั้นมี [0] คะแนน ถึง (สมมติว่ามันชี้ไปที่อะไรก็ตามที่มีความหมาย) แม้จะเป็นสตริง (อาจจะเป็นกระต่ายก็ได้: p )! การขาดความกังวลสำหรับประเภทนี้จะบินไปเผชิญหน้ากับทุกสิ่งที่ทำให้ Java Java ดังนั้นในขณะที่มีค่าเริ่มต้นสำหรับตัวแปรท้องถิ่นอาจจะเห็นเป็นตัวเลือกที่ดีที่สุดที่มีค่าเริ่มต้นสำหรับตัวแปรเช่นเป็นผู้ใกล้ชิดกับความจำเป็น


1

ถ้าฉันไม่ผิดเหตุผลอื่นก็อาจเป็นได้

การให้ค่าเริ่มต้นของตัวแปรสมาชิกเป็นส่วนหนึ่งของการโหลดคลาส

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


0

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


0

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


2
ไม่ถูกต้อง ... Java ที่ไม่ใช่แบบดั้งเดิมทั้งหมดจะถูกเก็บไว้ในฮีปโดยไม่คำนึงถึงเวลาและวิธีการสร้าง
gshauger

ก่อน Java 7 ตัวแปรอินสแตนซ์จะถูกเก็บไว้ในฮีปและพบตัวแปรโลคัลบนสแต็ก อย่างไรก็ตามอ็อบเจ็กต์ใด ๆ ที่อ้างอิงตัวแปรโลคัลจะพบในฮีป สำหรับ Java 7 "Java Hotspot Server Compiler" อาจทำการ "Escape analysis" และตัดสินใจที่จะจัดสรรอ็อบเจ็กต์บางอย่างบนสแต็กแทนฮีป
mamills

0

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


-1

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


-2

ฉันคิดได้ 2 เหตุผลต่อไปนี้

  1. ดังที่คำตอบส่วนใหญ่กล่าวโดยการใส่ข้อ จำกัด ของการเริ่มต้นตัวแปรโลคัลจึงมั่นใจได้ว่าตัวแปรโลคัลจะได้รับการกำหนดค่าตามที่โปรแกรมเมอร์ต้องการและทำให้แน่ใจว่าได้คำนวณผลลัพธ์ที่คาดหวัง
  2. สามารถซ่อนตัวแปรอินสแตนซ์ได้โดยการประกาศตัวแปรโลคัล (ชื่อเดียวกัน) - เพื่อให้แน่ใจว่าพฤติกรรมที่คาดหวังตัวแปรโลคัลจะถูกบังคับให้เริ่มต้นค่า (จะหลีกเลี่ยงสิ่งนี้โดยสิ้นเชิง)

ไม่สามารถลบล้างช่องได้ อย่างมากพวกเขาสามารถซ่อนได้และฉันไม่เห็นว่าการซ่อนจะรบกวนการตรวจสอบการเริ่มต้นอย่างไร?
ทำบุญ

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