0. ข้อแตกต่างระหว่างสองข้อผิดพลาดหรือไม่?
ไม่ได้จริงๆ "ไม่สามารถหาสัญลักษณ์" และ "ไม่สามารถแก้ไขสัญลักษณ์" หมายถึงสิ่งเดียวกัน คอมไพเลอร์ Java บางตัวใช้วลีเดียวและอีกอันหนึ่งคอมไพเลอร์อื่น ๆ
1. ข้อผิดพลาด "ไม่สามารถหาสัญลักษณ์" หมายความว่าอย่างไร
ประการแรกก็คือการรวบรวมข้อผิดพลาด 1 หมายความว่าอาจมีปัญหาในซอร์สโค้ด Java ของคุณหรือมีปัญหาในแบบที่คุณคอมไพล์
ซอร์สโค้ด Java ของคุณประกอบด้วยสิ่งต่าง ๆ ดังต่อไปนี้:
- คำสำคัญ: เช่น
true
, false
, class
, while
และอื่น ๆ
- ตัวอักษร: เหมือน
42
และและ'X'
"Hi mum!"
- ผู้ประกอบการและราชสกุลไม่ใช่ตัวเลขอื่น ๆ เช่น
+
, =
, {
และอื่น ๆ
- ตัวบ่งชี้: เช่น
Reader
, i
, toString
, processEquibalancedElephants
และอื่น ๆ
- ความคิดเห็นและช่องว่าง
ข้อผิดพลาด "ไม่พบสัญลักษณ์" เป็นเรื่องเกี่ยวกับตัวระบุ เมื่อคอมไพล์โค้ดของคุณคอมไพเลอร์จำเป็นต้องหาตัวบ่งชี้แต่ละตัวในโค้ดของคุณ
ข้อผิดพลาด "ไม่สามารถหาสัญลักษณ์" หมายความว่าคอมไพเลอร์ไม่สามารถทำได้ รหัสของคุณดูเหมือนจะอ้างถึงบางสิ่งที่คอมไพเลอร์ไม่เข้าใจ
2. ข้อผิดพลาด "หาสัญลักษณ์ไม่พบ" คืออะไร
ในการสั่งซื้อครั้งแรกมีเพียงสาเหตุเดียวเท่านั้น คอมไพเลอร์ดูในสถานที่ทั้งหมดที่ควรกำหนดตัวระบุและไม่พบคำจำกัดความ สิ่งนี้อาจเกิดจากหลายสิ่ง คนทั่วไปมีดังนี้:
- สำหรับตัวระบุโดยทั่วไป:
- บางทีคุณสะกดชื่อไม่ถูกต้อง คือแทน
StringBiulder
StringBuilder
Java ไม่สามารถและจะไม่พยายามชดเชยการสะกดผิดหรือการพิมพ์ผิดพลาด
- คุณอาจเข้าใจผิด คือแทน
stringBuilder
StringBuilder
ตัวระบุ Java ทั้งหมดต้องตรงตามตัวพิมพ์ใหญ่และตัวพิมพ์เล็ก
- บางทีคุณอาจใช้ขีดเส้นใต้ที่ไม่เหมาะสม เช่น
mystring
และmy_string
แตกต่างกัน (ถ้าคุณยึดติดกับกฎสไตล์ Java คุณจะได้รับการปกป้องส่วนใหญ่จากความผิดพลาดนี้ ... )
- บางทีคุณกำลังพยายามใช้บางสิ่งที่ถูกประกาศว่า "ที่อื่น"; เช่นในบริบทที่แตกต่างกันซึ่งคุณได้บอกให้คอมไพเลอร์ดูโดยปริยาย (คลาสที่แตกต่างกันอย่างไรขอบเขตที่แตกต่างกันแพคเกจที่แตกต่างกันหรือมีรหัสฐานที่แตกต่างกัน)
- สำหรับตัวระบุที่ควรอ้างถึงตัวแปร:
- บางทีคุณลืมที่จะประกาศตัวแปร
- บางทีการประกาศตัวแปรนั้นไม่อยู่ในขอบเขตที่คุณพยายามจะใช้ (ดูตัวอย่างด้านล่าง)
สำหรับตัวระบุที่ควรเป็นชื่อเมธอดหรือฟิลด์:
สำหรับตัวระบุที่ควรเป็นชื่อคลาส:
สำหรับกรณีที่ประเภทหรืออินสแตนซ์ไม่ปรากฏว่ามีสมาชิกคุณคาดว่าจะมี:
- บางทีคุณอาจจะมีการประกาศให้เป็นชั้นซ้อนกันหรือพารามิเตอร์ทั่วไปที่เงาชนิดที่คุณหมายถึงการใช้
- บางทีคุณอาจกำลังแรเงาตัวแปรแบบคงที่หรืออินสแตนซ์
- บางทีคุณอาจนำเข้าประเภทที่ไม่ถูกต้อง เช่นเนื่องจากความสมบูรณ์ของ IDE หรือการแก้ไขอัตโนมัติ
- บางทีคุณอาจกำลังใช้ API ที่รวบรวมผิดเวอร์ชัน
- บางทีคุณอาจลืมโยนวัตถุของคุณไปยังคลาสย่อยที่เหมาะสม
ปัญหานี้มักจะเป็นการรวมกันของข้างต้น ตัวอย่างเช่นคุณอาจ "ดาว" นำเข้าjava.io.*
แล้วลองใช้Files
คลาส ... ซึ่งjava.nio
ไม่java.io
อยู่ หรือบางทีคุณอาจหมายถึงการเขียนFile
... ซึ่งเป็นjava.io
ชั้นใน
นี่คือตัวอย่างของการกำหนดขอบเขตตัวแปรที่ไม่ถูกต้องสามารถนำไปสู่ข้อผิดพลาด "ไม่พบสัญลักษณ์":
List<String> strings = ...
for (int i = 0; i < strings.size(); i++) {
if (strings.get(i).equalsIgnoreCase("fnord")) {
break;
}
}
if (i < strings.size()) {
...
}
สิ่งนี้จะทำให้เกิดข้อผิดพลาด "ไม่พบสัญลักษณ์" i
ในif
คำสั่ง แม้ว่าเราจะประกาศก่อนหน้านี้การประกาศi
นั้นอยู่ในขอบเขตของfor
ข้อความและเนื้อหาเท่านั้น อ้างอิงถึงi
ในif
คำสั่งไม่สามารถดูi
การประกาศว่า มันเป็นออกจากขอบเขต
(การแก้ไขที่เหมาะสมที่นี่อาจเป็นการย้ายif
คำสั่งภายในลูปหรือประกาศi
ก่อนเริ่มลูป)
นี่คือตัวอย่างที่ทำให้เกิดข้อผิดพลาดที่การพิมพ์ผิดนำไปสู่ข้อผิดพลาด "ไม่พบสัญลักษณ์" ที่อธิบายไม่ได้:
for (int i = 0; i < 100; i++); {
System.out.println("i is " + i);
}
สิ่งนี้จะทำให้คุณรวบรวมข้อผิดพลาดในการprintln
โทรโดยบอกว่าi
ไม่พบ แต่ (ฉันได้ยินคุณพูด) ฉันประกาศ!
ปัญหาคืออัฒภาคส่อเสียด ( ;
) {
ก่อน ไวยากรณ์ภาษา Java กำหนดอัฒภาคในบริบทที่จะเป็นคำสั่งที่ว่างเปล่า คำสั่งที่ว่างเปล่าจะกลายเป็นเนื้อความของfor
ลูป ดังนั้นรหัสที่จริงหมายถึงสิ่งนี้:
for (int i = 0; i < 100; i++);
// The previous and following are separate statements!!
{
System.out.println("i is " + i);
}
{ ... }
บล็อกไม่ได้ร่างกายของfor
วงและดังนั้นจึงประกาศก่อนหน้านี้i
ในfor
คำสั่งออกจากขอบเขตในบล็อก
นี่เป็นอีกตัวอย่างของข้อผิดพลาด "ไม่สามารถหาสัญลักษณ์" ที่เกิดจากการพิมพ์ผิด
int tmp = ...
int res = tmp(a + b);
แม้จะมีการประกาศก่อนหน้านี้tmp
ในการtmp(...)
แสดงออกที่ไม่ถูกต้อง คอมไพเลอร์จะมองหาวิธีที่เรียกว่าtmp
และจะไม่พบวิธีใดวิธีหนึ่ง ประกาศก่อนหน้านี้tmp
อยู่ใน namespace สำหรับตัวแปรไม่ใช่ namespace สำหรับวิธีการ
ในตัวอย่างที่ฉันเจอผู้เขียนได้ทิ้งโอเปอเรเตอร์จริงๆ สิ่งที่เขาตั้งใจจะเขียนคือ:
int res = tmp * (a + b);
มีอีกเหตุผลที่คอมไพเลอร์อาจไม่พบสัญลักษณ์หากคุณกำลังรวบรวมจากบรรทัดคำสั่ง คุณอาจลืมรวบรวมหรือคอมไพล์คลาสอื่น ๆ ตัวอย่างเช่นถ้าคุณมีชั้นเรียนFoo
และBar
ที่ใช้Foo
Bar
หากคุณไม่เคยคอมไพล์Bar
และรันjavac Foo.java
คุณจะต้องพบว่าคอมไพเลอร์ไม่สามารถหาสัญลักษณ์Bar
ได้ คำตอบง่ายๆคือรวบรวมFoo
และBar
รวมกัน; เช่นหรือjavac Foo.java Bar.java
javac *.java
หรือดีกว่ายังคงใช้เครื่องมือสร้าง Java; เช่น Ant, Maven, Gradle และอื่น ๆ
มีสาเหตุอื่นที่ไม่ชัดเจนอีกด้วย ... ซึ่งฉันจะจัดการกับด้านล่าง
3. ฉันจะแก้ไขข้อผิดพลาดเหล่านี้ได้อย่างไร
โดยทั่วไปคุณจะเริ่มต้นด้วยการหาสาเหตุที่ทำให้เกิดข้อผิดพลาดในการรวบรวม
- ดูบรรทัดในไฟล์ที่ระบุโดยข้อความแสดงข้อผิดพลาดการคอมไพล์
- ระบุสัญลักษณ์ที่ข้อความแสดงข้อผิดพลาดกำลังพูดถึง
- หาสาเหตุที่คอมไพเลอร์บอกว่าไม่สามารถหาสัญลักษณ์ได้ ดูด้านบน!
จากนั้นคุณคิดว่ารหัสของคุณควรจะพูดอะไร จากนั้นในที่สุดคุณก็จะทำการแก้ไขสิ่งที่คุณต้องทำกับซอร์สโค้ดของคุณเพื่อทำสิ่งที่คุณต้องการ
โปรดทราบว่าไม่ใช่ "การแก้ไข" ทุกอย่างที่ถูกต้อง พิจารณาสิ่งนี้:
for (int i = 1; i < 10; i++) {
for (j = 1; j < 10; j++) {
...
}
}
สมมติว่าคอมไพเลอร์กล่าวว่า "คุณไม่พบสัญลักษณ์" j
สำหรับ มีหลายวิธีที่ฉันสามารถ "แก้ไข" ที่:
- ฉันสามารถเปลี่ยนด้านใน
for
เป็นfor (int j = 1; j < 10; j++)
- อาจถูกต้อง
- ฉันสามารถเพิ่มการประกาศสำหรับ
j
ก่อนfor
วงด้านในหรือfor
วงด้านนอก- อาจถูกต้อง
- ผมอาจมีการเปลี่ยนแปลง
j
ไปi
ในด้านfor
ห่วง - อาจผิด!
- และอื่น ๆ
ประเด็นก็คือคุณต้องเข้าใจว่าโค้ดของคุณพยายามทำอะไรเพื่อหาวิธีแก้ไขที่ถูกต้อง
4. สาเหตุที่ไม่ชัดเจน
นี่คือบางกรณีที่ "ไม่สามารถหาสัญลักษณ์" ดูเหมือนจะอธิบายไม่ได้ ... จนกว่าคุณจะเข้าใกล้
การอ้างอิงที่ไม่ถูกต้อง : ถ้าคุณใช้ IDE หรือเครื่องมือบิลด์ที่จัดการพา ธ การ build และการพึ่งพาโปรเจ็กต์คุณอาจทำผิดกับการพึ่งพา เช่นออกจากการพึ่งพาหรือเลือกรุ่นที่ไม่ถูกต้อง หากคุณใช้เครื่องมือบิลด์ (Ant, Maven, Gradle, ฯลฯ ) ให้ตรวจสอบไฟล์บิลด์ของโปรเจ็กต์ หากคุณใช้ IDE ให้ตรวจสอบการกำหนดค่าเส้นทางของโครงการ
คุณไม่ได้ทำการคอมไพล์ใหม่ : บางครั้งมันเกิดขึ้นที่โปรแกรมเมอร์ Java คนใหม่ไม่เข้าใจว่าเครื่องมือของ Java ทำงานอย่างไรหรือไม่ได้ใช้ "build process" ซ้ำ เช่นใช้ IDE, Ant, Maven, Gradle และอื่น ๆ ในสถานการณ์เช่นโปรแกรมเมอร์ที่สามารถจบลงไล่หางของเขามองหาข้อผิดพลาดที่ไม่จริงที่เป็นจริงที่เกิดจากการไม่ recompiling รหัสได้อย่างถูกต้องและไม่ชอบ ...
ปัญหาบิลด์ก่อนหน้า : เป็นไปได้ว่าบิลด์ก่อนหน้านี้ล้มเหลวในลักษณะที่ให้ไฟล์ JAR ที่มีคลาสที่หายไป ความล้มเหลวดังกล่าวมักจะสังเกตเห็นว่าคุณกำลังใช้เครื่องมือสร้าง อย่างไรก็ตามหากคุณได้รับไฟล์ JAR จากบุคคลอื่นคุณจะต้องพึ่งพาพวกเขาในการสร้างไฟล์อย่างถูกต้องและสังเกตเห็นข้อผิดพลาด หากคุณสงสัยว่าสิ่งนี้ใช้tar -tvf
เพื่อแสดงรายการเนื้อหาของไฟล์ JAR ที่น่าสงสัย
ปัญหา IDE : ผู้คนรายงานกรณีที่ IDE สับสนและคอมไพเลอร์ใน IDE ไม่สามารถหาคลาสที่มีอยู่ ... หรือสถานการณ์ย้อนกลับ
สิ่งนี้อาจเกิดขึ้นหาก IDE ได้รับการกำหนดค่าด้วยรุ่น JDK ผิด
สิ่งนี้อาจเกิดขึ้นได้หากแคชของ IDE ไม่สอดคล้องกับระบบไฟล์ มีวิธีเฉพาะของ IDE ในการแก้ไขปัญหานี้
นี่อาจเป็นข้อบกพร่องของ IDE ตัวอย่างเช่น @Joel Costigliola อธิบายสถานการณ์ที่ Eclipse ไม่จัดการทรี Maven "test" อย่างถูกต้อง: ดูคำตอบนี้
ประเด็น Android : เมื่อคุณเขียนโปรแกรมสำหรับ Android และคุณมี "ไม่สามารถหาสัญลักษณ์" ข้อผิดพลาดที่เกี่ยวข้องกับการR
จะทราบว่าR
สัญลักษณ์ถูกกำหนดโดยcontext.xml
ไฟล์ ตรวจสอบว่าcontext.xml
ไฟล์ของคุณถูกต้องและอยู่ในตำแหน่งที่ถูกต้องและR
ไฟล์คลาสที่เกี่ยวข้องนั้นถูกสร้างขึ้น / รวบรวม โปรดทราบว่าสัญลักษณ์ Java เป็นแบบตรงตามตัวพิมพ์ใหญ่ - เล็กดังนั้นรหัส XML ที่สอดคล้องกันจะต้องตรงตามตัวพิมพ์ใหญ่และตัวพิมพ์เล็ก
ข้อผิดพลาดของสัญลักษณ์อื่น ๆ บน Android น่าจะเกิดจากสาเหตุที่กล่าวถึงก่อนหน้านี้; เช่นการพึ่งพาที่ขาดหายไปหรือไม่ถูกต้องชื่อแพคเกจที่ไม่ถูกต้องวิธีการหรือฟิลด์ที่ไม่มีอยู่ใน API รุ่นใดรุ่นหนึ่งข้อผิดพลาดด้านการสะกดคำ / การพิมพ์และอื่น ๆ
การกำหนดคลาสระบบใหม่ : ฉันเคยเห็นกรณีที่คอมไพเลอร์บ่นว่าsubstring
เป็นสัญลักษณ์ที่ไม่รู้จักในบางสิ่งดังต่อไปนี้
String s = ...
String s1 = s.substring(1);
มันกลับกลายเป็นว่าโปรแกรมเมอร์ได้สร้างเวอร์ชันของตนเองขึ้นมาString
และรุ่นของคลาสนั้นไม่ได้กำหนดsubstring
วิธีการ
บทเรียน: อย่ากำหนดคลาสของคุณเองด้วยชื่อเดียวกับคลาสไลบรารีทั่วไป!
Homoglyphs: หากคุณใช้การเข้ารหัส UTF-8 สำหรับไฟล์ต้นฉบับของคุณเป็นไปได้ที่จะมีตัวระบุที่มีลักษณะเหมือนกัน แต่ในความเป็นจริงแตกต่างกันเนื่องจากมี homoglyphs ดูหน้านี้สำหรับข้อมูลเพิ่มเติม
คุณสามารถหลีกเลี่ยงสิ่งนี้ได้โดย จำกัด ตัวเองเป็น ASCII หรือ Latin-1 เป็นการเข้ารหัสไฟล์ต้นฉบับและใช้ Java \uxxxx
escapes สำหรับอักขระอื่น ๆ
1 - ถ้าบางทีคุณไม่เห็นนี้ในข้อยกเว้นรันไทม์หรือข้อผิดพลาดแล้วทั้งคุณได้กำหนดค่า IDE ของคุณเพื่อเรียกใช้รหัสมีข้อผิดพลาดการรวบรวมหรือใบสมัครของคุณคือการสร้างและรวบรวมรหัส .. ที่รันไทม์
2 - หลักการพื้นฐานสามประการของวิศวกรรมโยธา: น้ำไม่ไหลขึ้นเขากระดานมีความแข็งแรงกว่าและคุณไม่สามารถดันสายได้