เหตุใดโค้ด Java นี้จึงคอมไพล์


96

ในขอบเขตของวิธีการหรือคลาสบรรทัดด้านล่างคอมไพล์ (พร้อมคำเตือน):

int x = x = 1;

ในขอบเขตระดับที่ตัวแปรได้รับค่าเริ่มต้นสิ่งต่อไปนี้ทำให้เกิดข้อผิดพลาด 'การอ้างอิงที่ไม่ได้กำหนด':

int x = x + 1;

x = x = 1ข้อผิดพลาด "การอ้างอิงที่ไม่ได้กำหนด" เป็นครั้งแรกไม่ใช่หรือ หรือบรรทัดที่สองint x = x + 1ควรรวบรวม? หรือมีบางอย่างที่ฉันขาดหายไป?


1
หากคุณเพิ่มคีย์เวิร์ดstaticในตัวแปรขอบเขตคลาสstatic int x = x + 1;คุณจะได้รับข้อผิดพลาดเดียวกันหรือไม่ เนื่องจากใน C # จะสร้างความแตกต่างหากเป็นแบบคงที่หรือไม่คงที่
Jeppe Stig Nielsen

static int x = x + 1ล้มเหลวใน Java
Marcin

1
ใน c # ทั้งint a = this.a + 1;และint b = 1; int a = b + 1;ในขอบเขตคลาส (ซึ่งทั้งสองอย่างนี้ใช้ได้ใน Java) ล้มเหลวอาจเนื่องมาจาก§17.4.5.2 - "ตัวเริ่มต้นตัวแปรสำหรับฟิลด์อินสแตนซ์ไม่สามารถอ้างอิงอินสแตนซ์ที่สร้างขึ้น" ฉันไม่รู้ว่ามันอนุญาตอย่างชัดเจนที่ไหนสักแห่ง แต่คงไม่มีข้อ จำกัด ดังกล่าว ใน Java กฎระเบียบที่มีแตกต่างกันและstatic int x = x + 1ล้มเหลวด้วยเหตุผลเดียวกับที่int x = x + 1ไม่
msam

anwser ที่มี bytecode ช่วยขจัดข้อสงสัยใด ๆ
rgripper

คำตอบ:


101

tl; dr

สำหรับสาขา , int b = b + 1เป็นสิ่งผิดกฎหมายเพราะคือการอ้างอิงไปข้างหน้าผิดกฎหมายb bคุณสามารถแก้ไขได้จริงโดยการเขียนint b = this.b + 1ซึ่งรวบรวมโดยไม่มีข้อร้องเรียน

สำหรับตัวแปรท้องถิ่น , int d = d + 1เป็นสิ่งผิดกฎหมายเพราะdไม่ได้เริ่มต้นก่อนการใช้งาน นี่ไม่ใช่กรณีสำหรับฟิลด์ซึ่งมักจะเริ่มต้นเป็นค่าเริ่มต้น

คุณสามารถเห็นความแตกต่างได้โดยพยายามรวบรวม

int x = (x = 1) + x;

เป็นการประกาศฟิลด์และเป็นการประกาศตัวแปรโลคัล อดีตจะล้มเหลว แต่หลังจะประสบความสำเร็จเนื่องจากความแตกต่างในความหมาย

บทนำ

ก่อนอื่นกฎสำหรับตัวเริ่มต้นของฟิลด์และตัวแปรโลคัลนั้นแตกต่างกันมาก ดังนั้นคำตอบนี้จะจัดการกับกฎเป็นสองส่วน

เราจะใช้โปรแกรมทดสอบนี้ตลอด:

public class test {
    int a = a = 1;
    int b = b + 1;
    public static void Main(String[] args) {
        int c = c = 1;
        int d = d + 1;
    }
}

การประกาศbไม่ถูกต้องและล้มเหลวด้วยillegal forward referenceข้อผิดพลาด
การประกาศdไม่ถูกต้องและล้มเหลวด้วยvariable d might not have been initializedข้อผิดพลาด

ความจริงที่ว่าข้อผิดพลาดเหล่านี้แตกต่างกันควรบอกเป็นนัยว่าสาเหตุของข้อผิดพลาดก็แตกต่างกันเช่นกัน

ฟิลด์

ตัวเริ่มต้นฟิลด์ใน Java ถูกควบคุมโดยJLS §8.3.2การเริ่มต้นฟิลด์

ขอบเขตของเขตข้อมูลที่กำหนดไว้ในJLS §6.3 , ขอบเขตของการประกาศ

กฎที่เกี่ยวข้อง ได้แก่ :

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

§8.3.2.3พูดว่า:

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

  • การใช้งานเกิดขึ้นในตัวเริ่มต้นตัวแปรอินสแตนซ์ (ตามลำดับคงที่) ของ C หรือในอินสแตนซ์ (ตามลำดับคงที่) ตัวเริ่มต้นของ C
  • การใช้งานไม่ได้อยู่ทางด้านซ้ายมือของงาน
  • การใช้งานผ่านชื่อธรรมดา
  • C คือคลาสหรืออินเทอร์เฟซด้านในสุดที่ปิดล้อมการใช้งาน

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

int j = i;
int i = j;

จากการรวบรวม ข้อมูลจำเพาะของ Java ระบุว่า "ข้อ จำกัด ข้างต้นได้รับการออกแบบมาเพื่อตรวจจับในเวลาคอมไพล์การเริ่มต้นแบบวงกลมหรือผิดรูปแบบ"

กฎเหล่านี้มีผลต่ออะไร?

โดยทั่วไปแล้วกฎจะบอกว่าคุณต้องประกาศเขตข้อมูลล่วงหน้าของการอ้างอิงไปยังฟิลด์นั้นหาก (a) การอ้างอิงอยู่ในตัวเริ่มต้น (b) การอ้างอิงไม่ได้ถูกกำหนดให้ (c) การอ้างอิงคือ a ชื่อธรรมดา (ไม่มีคุณสมบัติเช่นthis.) และ (d) ไม่สามารถเข้าถึงได้จากภายในคลาสภายใน ดังนั้นการอ้างอิงไปข้างหน้าที่ตรงตามเงื่อนไขทั้งสี่จึงผิดกฎหมาย แต่การอ้างอิงไปข้างหน้าที่ล้มเหลวอย่างน้อยหนึ่งเงื่อนไขก็ใช้ได้

int a = a = 1;รวบรวมเนื่องจากละเมิด (b): a มีการกำหนดข้อมูลอ้างอิงให้ดังนั้นจึงเป็นเรื่องถูกกฎหมายที่จะอ้างถึงaล่วงหน้าก่อนการaประกาศฉบับสมบูรณ์

int b = this.b + 1ยังรวบรวมเพราะละเมิด (c): การอ้างอิงthis.bไม่ใช่ชื่อธรรมดา (มีคุณสมบัติด้วยthis.) โครงสร้างแปลก ๆ นี้ยังคงมีการกำหนดไว้อย่างสมบูรณ์เนื่องจากthis.bมีค่าเป็นศูนย์

ดังนั้นโดยพื้นฐานแล้วข้อ จำกัด ในการอ้างอิงฟิลด์ภายใน initializers ป้องกันไม่ให้int a = a + 1คอมไพล์สำเร็จ

สังเกตว่าการประกาศเขตข้อมูลint b = (b = 1) + bจะไม่สามารถรวบรวมได้เนื่องจากขั้นสุดท้ายbยังคงเป็นการอ้างอิงไปข้างหน้าที่ผิดกฎหมาย

ตัวแปรท้องถิ่น

การประกาศตัวแปรท้องถิ่นอยู่ภายใต้JLS §14.4คำชี้แจงการประกาศตัวแปรท้องถิ่น

ขอบเขตของตัวแปรท้องถิ่นถูกกำหนดไว้ในJLS §6.3ขอบเขตของประกาศ:

  • ขอบเขตของการประกาศตัวแปรโลคัลในบล็อก (§14.4) คือส่วนที่เหลือของบล็อกที่การประกาศปรากฏขึ้นโดยเริ่มจากตัวเริ่มต้นของตัวเองและรวมถึงตัวประกาศอื่น ๆ ทางด้านขวาในคำสั่งการประกาศตัวแปรโลคัล

โปรดทราบว่าตัวเริ่มต้นอยู่ในขอบเขตของตัวแปรที่ประกาศ แล้วทำไมไม่int d = d + 1;คอมไพล์ล่ะ?

สาเหตุเนื่องมาจากกฎของ Java เกี่ยวกับการกำหนดที่แน่นอน ( JLS §16 ) การกำหนดที่ชัดเจนโดยทั่วไปกล่าวว่าการเข้าถึงตัวแปรโลคัลทุกครั้งจะต้องมีการกำหนดก่อนหน้านี้ให้กับตัวแปรนั้นและคอมไพลเลอร์ Java จะตรวจสอบลูปและกิ่งก้านเพื่อให้แน่ใจว่าการกำหนดจะเกิดขึ้นก่อนการใช้งานใด ๆเสมอ (นี่คือเหตุผลที่การกำหนดที่แน่นอนมีส่วนข้อกำหนดเฉพาะทั้งหมด ไปเลย). กฎพื้นฐานคือ:

  • สำหรับการเข้าถึงของตัวแปรท้องถิ่นหรือสนามสุดท้ายว่างเปล่าทุกx, xต้องกำหนดแน่นอนก่อนที่จะเข้าถึงหรือรวบรวมข้อผิดพลาดเกิดขึ้นเวลา

ในint d = d + 1;การเข้าถึงdได้รับการแก้ไขให้กับตัวแปรโลคัลได้ดี แต่เนื่องจากdไม่ได้ถูกกำหนดก่อนที่dจะเข้าถึงคอมไพลเลอร์จึงแสดงข้อผิดพลาด ในint c = c = 1, c = 1เกิดขึ้นครั้งแรกที่ได้รับมอบหมายcและจากนั้นcจะเริ่มต้นได้จากผลของการกำหนดว่า (ซึ่งก็คือ 1)

หมายเหตุว่าเพราะกฎการกำหนดแน่นอนการประกาศตัวแปรท้องถิ่นint d = (d = 1) + d; จะรวบรวมประสบความสำเร็จ ( ซึ่งแตกต่างจากการประกาศเขตint b = (b = 1) + b) เพราะdได้รับมอบหมายแน่นอนตามเวลาที่สุดท้ายdจะมาถึง


+1 สำหรับการอ้างอิงอย่างไรก็ตามฉันคิดว่าคุณใช้คำนี้ผิด: "int a = a = 1; รวบรวมเพราะละเมิด (b)" หากละเมิดข้อกำหนดข้อใดข้อหนึ่งใน 4 ข้อก็จะไม่รวบรวม แต่ก็ไม่ได้เพราะมันเป็นทางด้านซ้ายมือของงาน (คู่เชิงลบในถ้อยคำของ JLS ไม่ได้ช่วยมากที่นี่) ในint b = b + 1b อยู่ทางขวา (ไม่ใช่ทางซ้าย) ของงานดังนั้นจึงเป็นการละเมิด ...
msam

... สิ่งที่ฉันไม่แน่ใจมีดังต่อไปนี้: เงื่อนไขทั้ง 4 นั้นจะต้องเป็นไปตามหากการประกาศไม่ปรากฏเป็นข้อความก่อนการมอบหมายงานในกรณีนี้ฉันคิดว่าการประกาศจะปรากฏเป็น "ข้อความ" ก่อนงานint x = x = 1ซึ่ง กรณีนี้จะไม่มีผลบังคับใช้
msam

@msam: มันค่อนข้างสับสน แต่โดยพื้นฐานแล้วคุณต้องละเมิดหนึ่งในสี่เงื่อนไขเพื่อทำการอ้างอิงไปข้างหน้า หากการอ้างอิงไปข้างหน้าของคุณตรงตามเงื่อนไขทั้งสี่ข้อถือว่าผิดกฎหมาย
nneonneo

@msam: นอกจากนี้การประกาศแบบเต็มจะมีผลหลังจากตัวเริ่มต้นเท่านั้น
nneonneo

@mrfishie: คำตอบที่ยิ่งใหญ่ แต่มีความลึกที่น่าประหลาดใจในข้อมูลจำเพาะ Java คำถามไม่ง่ายอย่างที่คิดบนพื้นผิว (ฉันเขียนคอมไพเลอร์ย่อยของ Java เป็นครั้งคราวดังนั้นฉันจึงคุ้นเคยกับ JLS จำนวนมาก)
nneonneo

86
int x = x = 1;

เทียบเท่ากับ

int x = 1;
x = x; //warning here

ขณะที่อยู่ใน

int x = x + 1; 

ก่อนอื่นเราต้องคำนวณx+1แต่ไม่ทราบค่าของ x ดังนั้นคุณจึงได้รับข้อผิดพลาด (คอมไพเลอร์รู้ว่าไม่ทราบค่าของ x)


4
สิ่งนี้รวมถึงคำแนะนำเกี่ยวกับการเชื่อมโยงที่เหมาะสมจาก OpenSauce ฉันพบว่ามีประโยชน์มาก
TobiMcNamobi

1
ฉันคิดว่าค่าที่ส่งคืนของงานคือค่าที่กำหนดไม่ใช่ค่าตัวแปร
zzzzBov

2
@zzzzBov ถูกต้อง int x = x = 1;คือการเทียบเท่าint x = (x = 1), ไม่ x = 1; x = x;คุณไม่ควรได้รับคำเตือนเกี่ยวกับคอมไพเลอร์สำหรับการดำเนินการนี้
nneonneo

int x = x = 1;s เทียบเท่ากับ int x = (x = 1)เนื่องจากความเชื่อมโยงด้านขวาของ=ตัวดำเนินการ
Grijesh Chauhan

1
@nneonneo และint x = (x = 1)เทียบเท่ากับint x; x = 1; x = x;(การประกาศตัวแปรการประเมินค่าเริ่มต้นฟิลด์การกำหนดตัวแปรให้เป็นผลลัพธ์ของการประเมินดังกล่าว) ดังนั้นคำเตือน
msam

41

เทียบเท่ากับ:

int x;
x = 1;
x = 1;

ประการแรกint <var> = <expression>;จะเทียบเท่ากับ

int <var>;
<var> = <expression>;

ในกรณีนี้นิพจน์ของคุณคือx = 1ซึ่งเป็นคำสั่งเช่นกัน x = 1เป็นคำสั่งที่ถูกต้องเนื่องจากxมีการประกาศvar แล้ว นอกจากนี้ยังเป็นนิพจน์ที่มีค่า 1 ซึ่งจะถูกกำหนดให้xอีกครั้ง


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

ลองดูคำตอบของ @izogfif ดูเหมือนจะใช้งานได้เนื่องจากคอมไพเลอร์ C ++ กำหนดค่าเริ่มต้นให้กับตัวแปร เช่นเดียวกับที่ java ทำกับตัวแปรระดับคลาส
มาร์ซิน

@Marcin: ใน Java ints จะไม่เริ่มต้นเป็น 0 เมื่อเป็นตัวแปรโลคัล พวกเขาจะเริ่มต้นเป็น 0 เท่านั้นหากเป็นตัวแปรสมาชิก ดังนั้นในบรรทัดที่สองของคุณx + 1ไม่มีค่าที่กำหนดไว้เนื่องจากไม่ได้กำหนดค่าxเริ่มต้น
OpenSauce

1
@OpenSauce แต่x ถูกกำหนดให้เป็นตัวแปรสมาชิก ("ในขอบเขตคลาส")
Jacob Raihle

@JacobRaihle: อ่าตกลงไม่ได้เห็นส่วนนั้น ฉันไม่แน่ใจว่า bytecode เพื่อเตรียมใช้งาน var ถึง 0 จะถูกสร้างขึ้นโดยคอมไพลเลอร์หากเห็นว่ามีคำสั่งการเริ่มต้นอย่างชัดเจน มีบทความที่กล่าวถึงรายละเอียดบางอย่างเกี่ยวกับการเริ่มต้นคลาสและอ็อบเจ็กต์แม้ว่าฉันไม่คิดว่าจะแก้ไขปัญหานี้ได้อย่างแน่นอน: javaworld.com/jw-11-2001/jw-1102-java101.html
OpenSauce

12

ในจาวาหรือภาษาสมัยใหม่การมอบหมายงานจะมาจากทางขวา

สมมติว่าคุณมีสองตัวแปร x และ y

int z = x = y = 5;

คำสั่งนี้ถูกต้องและนี่คือวิธีที่คอมไพลเลอร์แยกออก

y = 5;
x = y;
z = x; // which will be 5

แต่ในกรณีของคุณ

int x = x + 1;

คอมไพเลอร์ให้ข้อยกเว้นเพราะมันแยกแบบนี้

x = 1; // oops, it isn't declared because assignment comes from the right.

คำเตือนเปิด x = x ไม่ใช่ x = 1
Asim Ghaffar

8

int x = x = 1; ไม่เท่ากับ:

int x;
x = 1;
x = x;

javap ช่วยเราอีกครั้งนี่คือคำสั่ง JVM ที่สร้างขึ้นสำหรับรหัสนี้:

0: iconst_1    //load constant to stack
1: dup         //duplicate it
2: istore_1    //set x to constant
3: istore_1    //set x to constant

ชอบมากขึ้น:

int x = 1;
x = 1;

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

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

ถ้าเราเขียน:

int b, c, d, e, f;
int a = b = c = d = e = f = 5;

เท่ากับ:

f = 5
e = 5
d = 5
c = 5
b = 5
a = 5

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

a = b = c = f = e = d = a = a = a = a = a = e = f = 5;

7

ในตัวint x = x + 1;คุณเพิ่ม 1 เป็น x ดังนั้นค่าของxมันคืออะไรจึงยังไม่ได้สร้าง

แต่ในint x=x=1;จะรวบรวมข้อผิดพลาดไม่ได้เพราะคุณกำหนด 1 xถึง


5

โค้ดชิ้นแรกของคุณประกอบด้วยวินาที=แทนที่จะเป็นบวก สิ่งนี้จะรวบรวมที่ใดก็ได้ในขณะที่ส่วนที่สองของโค้ดจะไม่รวบรวมในที่ใดที่หนึ่ง


5

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


5

มาแบ่งมันทีละขั้นตอนเชื่อมโยงอย่างถูกต้อง

int x = x = 1

x = 1กำหนด 1 ให้กับตัวแปร x

int x = xกำหนดค่า x ให้กับตัวมันเองเป็น int เนื่องจากก่อนหน้านี้ x ถูกกำหนดให้เป็น 1 จึงยังคงเป็น 1 แม้ว่าจะซ้ำซ้อนก็ตาม

ที่รวบรวมได้ดี

int x = x + 1

x + 1เพิ่มหนึ่งในตัวแปร x อย่างไรก็ตาม x ที่ไม่ได้กำหนดสิ่งนี้จะทำให้เกิดข้อผิดพลาดในการคอมไพล์

int x = x + 1ดังนั้นบรรทัดนี้จะรวบรวมข้อผิดพลาดเนื่องจากส่วนด้านขวาของการเท่ากับจะไม่รวบรวมการเพิ่มหนึ่งในตัวแปรที่ไม่ได้กำหนด


ไม่มีก็ขวาเชื่อมโยงเมื่อมีสองผู้ประกอบการดังนั้นจึงเป็นเรื่องเดียวกับ= int x = (x = 1);
Jeppe Stig Nielsen

อาคำสั่งของฉันปิด ขอโทษด้วยกับเรื่องนั้น. ควรทำย้อนหลัง ฉันเปลี่ยนไปแล้ว
steventnorris

3

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


2
int x = x + 1;

คอมไพล์สำเร็จใน Visual Studio 2008 พร้อมคำเตือน

warning C4700: uninitialized local variable 'x' used`

2
การขัดขวาง มันคือ C / C ++ หรือไม่?
Marcin

@ Marcin: ใช่มันคือ C ++ @msam: ขออภัยฉันคิดว่าฉันเห็นแท็กcแทนjavaแต่เห็นได้ชัดว่าเป็นคำถามอื่น
izogfif

คอมไพเลอร์เนื่องจากใน C ++ คอมไพเลอร์กำหนดค่าเริ่มต้นสำหรับชนิดดั้งเดิม ใช้bool y;และy==trueจะส่งกลับเท็จ
Sri Harsha Chilakapati

@SriHarshaChilakapati มันเป็นมาตรฐานบางอย่างในคอมไพเลอร์ C ++ หรือไม่? เนื่องจากเมื่อฉันคอมไพล์void main() { int x = x + 1; printf("%d ", x); }ใน Visual Studio 2008 ใน Debug ฉันได้รับข้อยกเว้นRun-Time Check Failure #3 - The variable 'x' is being used without being initialized.และใน Release ฉันได้รับหมายเลขที่1896199921พิมพ์ในคอนโซล
izogfif

1
@SriHarshaChilakapati พูดถึงภาษาอื่น: ใน C # สำหรับstaticฟิลด์ (ตัวแปรคงที่ระดับคลาส) จะใช้กฎเดียวกัน ตัวอย่างเช่นเขตข้อมูลที่ประกาศเป็นpublic static int x = x + 1;คอมไพล์โดยไม่มีคำเตือนใน Visual C # อาจจะเหมือนกันใน Java?
Jeppe Stig Nielsen

2

x ไม่ได้เริ่มต้นในx = x + 1;.

ภาษาการเขียนโปรแกรม Java ถูกพิมพ์แบบคงที่ซึ่งหมายความว่าต้องประกาศตัวแปรทั้งหมดก่อนจึงจะใช้งานได้

ดูประเภทข้อมูลดั้งเดิม


3
ความจำเป็นในการเริ่มต้นตัวแปรก่อนที่จะใช้ค่าของพวกเขาไม่เกี่ยวข้องกับการพิมพ์แบบคงที่ พิมพ์แบบคงที่: คุณต้องประกาศว่าตัวแปรคืออะไร Initialise-before-use: จำเป็นต้องมีค่าที่พิสูจน์ได้ก่อนจึงจะใช้ค่าได้
Jon Bright

@JonBright: ความจำเป็นในการประกาศประเภทของตัวแปรยังไม่เกี่ยวข้องกับการพิมพ์แบบคงที่ ตัวอย่างเช่นมีภาษาที่พิมพ์แบบคงที่โดยมีการอนุมานประเภท
hammar

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

2

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

int x;
x = (x = 1); // (x = 1) returns 1 so there is no error

อย่างไรก็ตามในส่วนที่สองของคุณของรหัสint x = x + 1ที่+ 1คำสั่งต้องxมีการกำหนดไว้ซึ่งตอนนั้นมันไม่ได้ เนื่องจากคำสั่งมอบหมายหมายถึงรหัสทางด้านขวาของ=รันก่อนเสมอรหัสจะล้มเหลวเนื่องจากxไม่ได้กำหนด Java จะรันโค้ดดังนี้:

int x;
x = x + 1; // this line causes the error because `x` is undefined

-1

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

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