อะไรคือความแตกต่างระหว่างเวลาคอมไพล์และการอ้างอิงเวลาทำงานใน Java? มันเกี่ยวข้องกับ class path แต่แตกต่างกันอย่างไร?
อะไรคือความแตกต่างระหว่างเวลาคอมไพล์และการอ้างอิงเวลาทำงานใน Java? มันเกี่ยวข้องกับ class path แต่แตกต่างกันอย่างไร?
คำตอบ:
การพึ่งพาเวลาคอมไพล์ : คุณต้องการการพึ่งพาในCLASSPATH
การคอมไพล์สิ่งประดิษฐ์ของคุณ พวกมันถูกสร้างขึ้นเนื่องจากคุณมี "การอ้างอิง" บางอย่างสำหรับการอ้างอิงที่เข้ารหัสในโค้ดของคุณเช่นการเรียกnew
คลาสบางคลาสการขยายหรือการใช้งานบางสิ่งบางอย่าง (ทั้งทางตรงหรือทางอ้อม) หรือการเรียกใช้เมธอดโดยใช้reference.method()
สัญกรณ์โดยตรง
พึ่งพาเวลาทำงาน : คุณจำเป็นต้องพึ่งพาในคุณCLASSPATH
จะเรียกใช้สิ่งประดิษฐ์ของคุณ พวกมันถูกสร้างขึ้นเนื่องจากคุณรันโค้ดที่เข้าถึงการอ้างอิง (ไม่ว่าจะด้วยวิธีฮาร์ดโค้ดหรือผ่านการสะท้อนหรืออะไรก็ตาม)
แม้ว่าการพึ่งพาเวลาคอมไพล์มักหมายถึงการพึ่งพาเวลาทำงาน แต่คุณสามารถมีการพึ่งพาเวลาคอมไพล์เท่านั้น สิ่งนี้ขึ้นอยู่กับข้อเท็จจริงที่ว่า Java เชื่อมโยงการอ้างอิงคลาสเท่านั้นในการเข้าถึงครั้งแรกไปยังคลาสนั้นดังนั้นหากคุณไม่เคยเข้าถึงคลาสใดคลาสหนึ่งในขณะรันไทม์เนื่องจากเส้นทางโค้ดไม่เคยข้ามผ่าน Java จะละเว้นทั้งคลาสและการอ้างอิง
ตัวอย่างนี้
ใน C.java (สร้างคลาส C. ):
package dependencies;
public class C { }
ใน A.java (สร้าง A.class):
package dependencies;
public class A {
public static class B {
public String toString() {
C c = new C();
return c.toString();
}
}
public static void main(String[] args) {
if (args.length > 0) {
B b = new B();
System.out.println(b.toString());
}
}
}
ในกรณีนี้A
มีการพึ่งพารวบรวมเวลาในการC
ผ่านB
แต่มันจะมีเพียงการพึ่งพาเวลาทำงานใน C ถ้าคุณส่งผ่านพารามิเตอร์บางอย่างเมื่อการดำเนินการjava dependencies.A
เป็น JVM จะพยายามที่จะแก้B
's พึ่งพาC
เมื่อได้รับการดำเนินการB b = new B()
. คุณลักษณะนี้ช่วยให้คุณระบุเฉพาะการอ้างอิงของคลาสที่คุณใช้ในพา ธ โค้ดของคุณในขณะรันไทม์และละเว้นการอ้างอิงของคลาสอื่น ๆ ที่เหลือในอาร์ติแฟกต์
ตัวอย่างง่ายๆคือดู api เช่น servlet api ในการคอมไพล์ servlet ของคุณคุณต้องมี servlet-api.jar แต่ที่รันไทม์คอนเทนเนอร์ servlet จะจัดเตรียมการใช้งาน servlet api ดังนั้นคุณไม่จำเป็นต้องเพิ่ม servlet-api.jar ในพา ธ คลาสรันไทม์ของคุณ
คอมไพเลอร์ต้องการคลาสพา ธ ที่ถูกต้องเพื่อรวบรวมการเรียกไปยังไลบรารี (การอ้างอิงเวลาคอมไพล์)
JVM ต้องการคลาสพา ธ ที่ถูกต้องเพื่อโหลดคลาสในไลบรารีที่คุณกำลังเรียกใช้ (การอ้างอิงรันไทม์)
อาจแตกต่างกันในสองวิธี:
1) ถ้าคลาส C1 ของคุณเรียกไลบรารีคลาส L1 และ L1 เรียกไลบรารีคลาส L2 ดังนั้น C1 จะมีการพึ่งพารันไทม์บน L1 และ L2 แต่มีเพียงการพึ่งพาเวลาคอมไพล์บน L1
2) ถ้าคลาส C1 ของคุณสร้างอินสแตนซ์อินเทอร์เฟซ I1 แบบไดนามิกโดยใช้ Class.forName () หรือกลไกอื่น ๆ และคลาสการนำไปใช้สำหรับอินเทอร์เฟซ I1 คือคลาส L1 ดังนั้น C1 จะมีการพึ่งพารันไทม์บน I1 และ L1 แต่มีเพียงการพึ่งพาเวลาคอมไพล์เท่านั้น บน I1.
การอ้างอิง "ทางอ้อม" อื่น ๆ ซึ่งเหมือนกันสำหรับเวลาคอมไพล์และรันไทม์:
3) คลาส C1 ของคุณขยายคลาสไลบรารี L1 และ L1 ใช้อินเทอร์เฟซ I1 และขยายคลาสไลบรารี L2: C1 มีการพึ่งพาเวลาคอมไพล์บน L1, L2 และ I1
4) คลาส C1 ของคุณมีเมธอดfoo(I1 i1)
และเมธอดbar(L1 l1)
โดยที่ I1 เป็นอินเทอร์เฟซและ L1 เป็นคลาสที่รับพารามิเตอร์ซึ่งเป็นอินเทอร์เฟซ I1: C1 มีการพึ่งพาเวลาคอมไพล์บน I1 และ L1
โดยพื้นฐานแล้วในการทำสิ่งที่น่าสนใจคลาสของคุณจะต้องเชื่อมต่อกับคลาสและอินเทอร์เฟซอื่น ๆ ในคลาสพา ธ กราฟคลาส / อินเทอร์เฟซที่สร้างขึ้นโดยชุดของอินเตอร์เฟสไลบรารีนั้นให้ผลต่อห่วงโซ่การพึ่งพาเวลาคอมไพล์ การใช้งานไลบรารีให้ผลเชนการพึ่งพารัน โปรดสังเกตว่าห่วงโซ่การพึ่งพาเวลาทำงานนั้นขึ้นอยู่กับเวลาทำงานหรือล้มเหลว - ช้า: ถ้าการใช้งาน L1 บางครั้งขึ้นอยู่กับการสร้างอินสแตนซ์วัตถุของคลาส L2 และคลาสนั้นจะได้รับการสร้างอินสแตนซ์ในสถานการณ์เฉพาะเท่านั้นดังนั้นจึงไม่มีการพึ่งพายกเว้นใน สถานการณ์นั้น
Java ไม่ได้เชื่อมโยงอะไรเลยในเวลาคอมไพล์ ตรวจสอบไวยากรณ์โดยใช้คลาสที่ตรงกันที่พบใน CLASSPATH เท่านั้น ไม่ใช่จนกว่ารันไทม์ที่ทุกอย่างจะรวมกันและดำเนินการตาม CLASSPATH ในขณะนั้น
การพึ่งพา Compiletime เป็นเพียงการอ้างอิง (คลาสอื่น ๆ ) ซึ่งคุณใช้โดยตรงในคลาสที่คุณกำลังรวบรวม การอ้างอิงรันไทม์ครอบคลุมทั้งการอ้างอิงโดยตรงและโดยอ้อมของคลาสที่คุณกำลังเรียกใช้ ดังนั้นการอ้างอิงรันไทม์รวมถึงการอ้างอิงการอ้างอิงและการอ้างอิงสะท้อนใด ๆ เช่น classnames ที่คุณมีในแต่จะใช้ในการString
Class#forName()
A
B.jar ด้วยB extends A
และ C.jar ด้วยC extends B
C.jar นั้นขึ้นอยู่กับเวลาในการรวบรวมของ A.jar แม้ว่าการพึ่งพา C กับ A จะเป็นทางอ้อม
สำหรับ Java การอ้างอิงเวลาในการคอมไพล์คือการอ้างอิงของซอร์สโค้ดของคุณ ตัวอย่างเช่นถ้าคลาส A เรียกใช้เมธอดจากคลาส B ดังนั้น A จะขึ้นอยู่กับ B ในเวลาคอมไพล์เนื่องจาก A ต้องรู้เกี่ยวกับ B (ชนิดของ B) ที่จะคอมไพล์ เคล็ดลับควรเป็นดังนี้: โค้ดที่คอมไพล์ยังไม่ใช่โค้ดที่สมบูรณ์และใช้งานได้ ประกอบด้วยที่อยู่ที่เปลี่ยนได้ (สัญลักษณ์ข้อมูลเมตา) สำหรับแหล่งที่มาซึ่งยังไม่ได้รวบรวมหรือมีอยู่ในขวดภายนอก ในระหว่างการเชื่อมโยงที่อยู่เหล่านั้นจะต้องถูกแทนที่ด้วยที่อยู่จริงในหน่วยความจำ ในการทำอย่างถูกต้องควรสร้างสัญลักษณ์ / ที่อยู่ที่ถูกต้อง และสามารถทำได้ด้วยประเภทของคลาส (B) ฉันเชื่อว่านั่นคือการพึ่งพาหลักในเวลาคอมไพล์
การพึ่งพารันไทม์เกี่ยวข้องกับโฟลว์ของการควบคุมที่แท้จริงมากกว่า มันเรียกใช้ที่อยู่หน่วยความจำจริง เป็นการพึ่งพาที่คุณมีเมื่อโปรแกรมของคุณกำลังทำงาน คุณต้องการรายละเอียดคลาส B ที่นี่เช่นการใช้งานไม่ใช่เฉพาะข้อมูลประเภท หากคลาสไม่มีอยู่คุณจะได้รับ RuntimeException และ JVM จะออก
การอ้างอิงทั้งสองโดยทั่วไปและไม่ควรไหลไปในทิศทางเดียวกัน นี่เป็นเรื่องของการออกแบบ OO แม้ว่า
ใน C ++ การคอมไพล์จะแตกต่างกันเล็กน้อย (ไม่ใช่เฉพาะเวลา) แต่มีตัวเชื่อมโยงด้วย ดังนั้นกระบวนการอาจคิดว่าคล้ายกับ Java ฉันเดา