У чому різниця між залежністю часу компіляції та часу виконання в Java? Це пов’язано з класом, але чим вони відрізняються?
Відповіді:
Залежність від часу компіляції : Вам потрібна залежність у вашому CLASSPATH
для компіляції артефакту. Вони створюються через те, що у вас є якесь "посилання" на жорстко закодовану у вашому коді залежність, таку як виклик new
якогось класу, розширення або реалізація чогось (прямо чи побічно) або виклик методу із використанням прямого reference.method()
позначення.
Залежність від часу виконання : вам потрібна залежність у вашому CLASSPATH
для запуску артефакту. Вони створюються через те, що ви виконуєте код, який отримує доступ до залежності (або в жорсткому коді, або за допомогою відображення, або що завгодно).
Хоча залежність від часу компіляції зазвичай передбачає залежність від часу виконання, ви можете мати залежність лише від часу компіляції. Це базується на тому, що Java пов'язує залежності класів лише при першому доступі до цього класу, тому, якщо ви ніколи не отримуєте доступ до певного класу під час виконання, оскільки шлях коду ніколи не пройдений, Java буде ігнорувати як клас, так і його залежності.
Приклад цього
У C.java (генерує C.class):
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
залежність від того, C
коли він повинен виконати B b = new B()
. Ця функція дозволяє надавати під час виконання лише залежності класів, які ви використовуєте у своїх шляхах коду, а також ігнорувати залежності решти класів в артефакті.
Простий приклад - розглянути такий api, як сервлет api. Щоб змушувати ваші сервлети компілюватись, вам потрібен servlet-api.jar, але під час виконання контейнер сервлета забезпечує реалізацію 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 того часу.
Залежності компіляції - це лише залежності (інші класи), які ви використовуєте безпосередньо у класі, який компілюєте. Залежності середовища виконання охоплюють як пряму, так і непряму залежності класу, який ви запускаєте. Таким чином, залежності часу виконання включають залежності залежностей та будь-які залежності відображення, такі як імена класів, які ви маєте в a 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 вийде.
Обидві залежності, як правило, і не повинні, мають однаковий напрямок. Однак це питання ОО-дизайну.
У C ++ компіляція дещо інша (не просто своєчасна), але вона також має компонувальник. Тож процес можна вважати схожим на Java, я думаю.