В чем разница между зависимостями времени компиляции и времени выполнения в 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 того времени.
Зависимости времени компиляции - это только зависимости (другие классы), которые вы используете непосредственно в компилируемом классе. Зависимости времени выполнения охватывают как прямые, так и косвенные зависимости используемого вами класса. Таким образом, зависимости времени выполнения включают в себя зависимости зависимостей и любые зависимости отражения, такие как имена классов, которые у вас есть в 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), который будет скомпилирован. Уловка здесь должна заключаться в следующем: скомпилированный код еще не является полным и исполняемым кодом. Он включает заменяемые адреса (символы, метаданные) для источников, которые еще не скомпилированы или не существуют во внешних jar-файлах. Во время связывания эти адреса должны быть заменены фактическими адресами в памяти. Чтобы сделать это правильно, должны быть созданы правильные символы / адреса. И это можно сделать с помощью типа класса (B). Я считаю, что это основная зависимость во время компиляции.
Зависимость во время выполнения больше связана с фактическим потоком управления. Он вызывает фактические адреса памяти. Это зависимость, которая возникает во время работы вашей программы. Здесь вам нужны детали класса B, такие как реализации, а не только информация о типе. Если класс не существует, вы получите исключение RuntimeException и JVM выйдет.
Обе зависимости, как правило, не должны идти в одном направлении. Однако это вопрос объектно-ориентированного дизайна.
В C ++ компиляция немного отличается (не вовремя), но у нее тоже есть компоновщик. Полагаю, этот процесс можно было бы считать похожим на Java.