Sự khác biệt giữa thời gian biên dịch và thời gian chạy phụ thuộc trong Java là gì? Nó liên quan đến đường dẫn lớp, nhưng chúng khác nhau như thế nào?
Câu trả lời:
Phụ thuộc thời gian biên dịch : Bạn cần phụ thuộc trong của mình CLASSPATH
để biên dịch tạo tác của bạn. Chúng được tạo ra bởi vì bạn có một số loại "tham chiếu" đến phần phụ thuộc được mã hóa cứng trong mã của bạn, chẳng hạn như gọi new
cho một số lớp, mở rộng hoặc triển khai một cái gì đó (trực tiếp hoặc gián tiếp) hoặc một lệnh gọi phương thức sử dụng reference.method()
ký hiệu trực tiếp .
Sự phụ thuộc vào thời gian chạy : Bạn cần sự phụ thuộc trong của mình CLASSPATH
để chạy phần mềm của bạn. Chúng được tạo ra bởi vì bạn thực thi mã truy cập phần phụ thuộc (theo cách được mã hóa cứng hoặc thông qua phản chiếu hoặc bất cứ điều gì).
Mặc dù phụ thuộc thời gian biên dịch thường ngụ ý phụ thuộc thời gian chạy, nhưng bạn có thể có một phụ thuộc chỉ thời gian biên dịch. Điều này dựa trên thực tế là Java chỉ liên kết các lớp phụ thuộc vào lần truy cập đầu tiên vào lớp đó, vì vậy nếu bạn không bao giờ truy cập một lớp cụ thể tại thời điểm chạy vì một đường dẫn mã không bao giờ được duyệt qua, Java sẽ bỏ qua cả lớp và các phụ thuộc của nó.
Ví dụ về điều này
Trong C.java (tạo C.class):
package dependencies;
public class C { }
Trong A.java (tạo 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());
}
}
}
Trong trường hợp này, A
có phụ thuộc thời gian biên dịch vào C
thông qua B
, nhưng nó sẽ chỉ có phụ thuộc thời gian chạy vào C nếu bạn truyền một số tham số khi thực thi java dependencies.A
, vì JVM sẽ chỉ cố gắng giải quyết B
phụ thuộc của C
khi nào nó được thực thi. B b = new B()
. Tính năng này cho phép bạn chỉ cung cấp trong thời gian chạy các phần phụ thuộc của các lớp mà bạn sử dụng trong các đường dẫn mã của mình và bỏ qua các phần phụ thuộc của phần còn lại của các lớp trong tạo tác.
Một ví dụ đơn giản là xem một api giống như api servlet. Để biên dịch các servlet của bạn, bạn cần có servlet-api.jar, nhưng trong thời gian chạy, vùng chứa servlet cung cấp một triển khai api servlet nên bạn không cần thêm servlet-api.jar vào đường dẫn lớp thời gian chạy của mình.
Trình biên dịch cần classpath phù hợp để biên dịch các cuộc gọi đến thư viện (biên dịch phụ thuộc thời gian)
JVM cần classpath phù hợp để tải các lớp trong thư viện mà bạn đang gọi (phụ thuộc thời gian chạy).
Chúng có thể khác nhau theo một số cách:
1) nếu lớp C1 của bạn gọi lớp thư viện L1 và L1 gọi lớp thư viện L2, thì C1 có phụ thuộc thời gian chạy vào L1 và L2, nhưng chỉ phụ thuộc thời gian biên dịch vào L1.
2) nếu lớp C1 của bạn khởi tạo động giao diện I1 bằng cách sử dụng Class.forName () hoặc một số cơ chế khác và lớp triển khai cho giao diện I1 là lớp L1, thì C1 có phụ thuộc thời gian chạy vào I1 và L1, nhưng chỉ phụ thuộc thời gian biên dịch trên I1.
Các phụ thuộc "gián tiếp" khác giống nhau về thời gian biên dịch và thời gian chạy:
3) lớp C1 của bạn mở rộng lớp thư viện L1 và L1 triển khai giao diện I1 và mở rộng lớp thư viện L2: C1 có phụ thuộc thời gian biên dịch vào L1, L2 và I1.
4) lớp C1 của bạn có một phương thức foo(I1 i1)
và một phương thức bar(L1 l1)
trong đó I1 là giao diện và L1 là lớp nhận tham số là giao diện I1: C1 có phụ thuộc thời gian biên dịch vào I1 và L1.
Về cơ bản, để làm bất cứ điều gì thú vị, lớp của bạn cần phải giao tiếp với các lớp và giao diện khác trong classpath. Biểu đồ lớp / giao diện được hình thành bởi tập hợp các giao diện thư viện đó mang lại chuỗi phụ thuộc thời gian biên dịch. Việc triển khai thư viện mang lại chuỗi phụ thuộc thời gian chạy. Lưu ý rằng chuỗi phụ thuộc thời gian chạy phụ thuộc vào thời gian chạy hoặc chạy chậm: nếu việc triển khai L1 đôi khi phụ thuộc vào việc khởi tạo một đối tượng của lớp L2 và lớp đó chỉ được khởi tạo trong một kịch bản cụ thể, thì không có phụ thuộc nào ngoại trừ trong kịch bản đó.
Java không thực sự liên kết bất cứ thứ gì tại thời điểm biên dịch. Nó chỉ xác minh cú pháp bằng cách sử dụng các lớp phù hợp mà nó tìm thấy trong CLASSPATH. Phải đến thời gian chạy, mọi thứ mới được tập hợp lại và thực thi dựa trên CLASSPATH tại thời điểm đó.
Các phụ thuộc thời gian biên dịch chỉ là các phụ thuộc (các lớp khác) mà bạn sử dụng trực tiếp trong lớp mà bạn đang biên dịch. Phụ thuộc thời gian chạy bao gồm cả phụ thuộc trực tiếp và gián tiếp của lớp bạn đang chạy. Do đó, các phụ thuộc thời gian chạy bao gồm các phụ thuộc của phụ thuộc và bất kỳ phụ thuộc phản ánh nào như tên lớp mà bạn có trong a String
, nhưng được sử dụng trong đó Class#forName()
.
A
, B.jar với B extends A
và C.jar với C extends B
thì C.jar phụ thuộc vào thời gian biên dịch trên A.jar mặc dù sự phụ thuộc của C vào A là gián tiếp.
Đối với Java, phụ thuộc thời gian biên dịch là phụ thuộc mã nguồn của bạn. Ví dụ: nếu lớp A gọi một phương thức từ lớp B, thì A phụ thuộc vào B tại thời điểm biên dịch vì A phải biết về B (kiểu B) được biên dịch. Bí quyết ở đây là: Mã đã biên dịch chưa phải là mã hoàn chỉnh và có thể thực thi được. Nó bao gồm các địa chỉ có thể thay thế (ký hiệu, siêu dữ liệu) cho các nguồn chưa được biên dịch hoặc tồn tại trong các lọ bên ngoài. Trong quá trình liên kết, các địa chỉ đó phải được thay thế bằng các địa chỉ thực trong bộ nhớ. Để thực hiện đúng, cần tạo các ký hiệu / địa chỉ chính xác. Và điều này có thể được thực hiện với kiểu của lớp (B). Tôi tin rằng đó là sự phụ thuộc chính tại thời điểm biên dịch.
Sự phụ thuộc thời gian chạy liên quan nhiều hơn đến luồng điều khiển thực tế. Nó xâm nhập địa chỉ bộ nhớ thực tế. Đó là sự phụ thuộc mà bạn có khi chương trình của bạn đang chạy. Bạn cần chi tiết lớp B ở đây như triển khai, không chỉ thông tin loại. Nếu lớp không tồn tại, thì bạn sẽ nhận được RuntimeException và JVM sẽ thoát.
Cả hai phần phụ thuộc, nói chung và không nên, chảy theo cùng một hướng. Đây là một vấn đề của thiết kế OO.
Trong C ++, việc biên dịch hơi khác một chút (không phải chỉ trong thời gian) nhưng nó cũng có một trình liên kết. Vì vậy, tôi đoán quá trình này có thể tương tự như Java.