¿Cuál es la diferencia entre el tiempo de compilación y las dependencias del tiempo de ejecución en Java? Está relacionado con la ruta de clases, pero ¿en qué se diferencian?
Respuestas:
Dependencia en tiempo de compilación : necesita la dependencia en su CLASSPATH
para compilar su artefacto. Se producen porque tiene algún tipo de "referencia" a la dependencia codificada en su código, como llamar new
a alguna clase, extender o implementar algo (ya sea directa o indirectamente), o una llamada a un método usando la reference.method()
notación directa .
Dependencia en tiempo de ejecución : necesita la dependencia en su CLASSPATH
para ejecutar su artefacto. Se producen porque ejecuta código que accede a la dependencia (ya sea de forma codificada o mediante reflexión o lo que sea).
Aunque la dependencia en tiempo de compilación generalmente implica una dependencia en tiempo de ejecución, puede tener una dependencia solo en tiempo de compilación. Esto se basa en el hecho de que Java solo vincula las dependencias de clase en el primer acceso a esa clase, por lo que si nunca accede a una clase en particular en tiempo de ejecución porque nunca se atraviesa una ruta de código, Java ignorará tanto la clase como sus dependencias.
Ejemplo de esto
En C.java (genera C.class):
package dependencies;
public class C { }
En A.java (genera 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());
}
}
}
En este caso, A
tiene una dependencia en tiempo de compilación de a C
través B
, pero solo tendrá una dependencia en tiempo de ejecución en C si pasa algunos parámetros al ejecutar java dependencies.A
, ya que la JVM solo intentará resolver B
la dependencia de C
cuando llegue a ejecutarse B b = new B()
. Esta característica le permite proporcionar en tiempo de ejecución solo las dependencias de las clases que usa en sus rutas de código e ignorar las dependencias del resto de las clases en el artefacto.
Un ejemplo sencillo es mirar una api como la api del servlet. Para hacer que sus servlets se compilen, necesita servlet-api.jar, pero en tiempo de ejecución, el contenedor de servlets proporciona una implementación de api de servlet, por lo que no necesita agregar servlet-api.jar a su ruta de clases en tiempo de ejecución.
El compilador necesita el classpath correcto para compilar llamadas a una biblioteca (compilar dependencias de tiempo)
La JVM necesita la ruta de clases correcta para cargar las clases en la biblioteca que está llamando (dependencias de tiempo de ejecución).
Pueden ser diferentes en un par de formas:
1) si su clase C1 llama a la clase de biblioteca L1 y L1 llama a la clase de biblioteca L2, entonces C1 tiene una dependencia del tiempo de ejecución en L1 y L2, pero solo una dependencia del tiempo de compilación en L1.
2) si su clase C1 crea una instancia dinámica de una interfaz I1 usando Class.forName () o algún otro mecanismo, y la clase de implementación para la interfaz I1 es la clase L1, entonces C1 tiene una dependencia del tiempo de ejecución en I1 y L1, pero solo una dependencia del tiempo de compilación en I1.
Otras dependencias "indirectas" que son las mismas para el tiempo de compilación y el tiempo de ejecución:
3) su clase C1 extiende la clase de biblioteca L1 y L1 implementa la interfaz I1 y extiende la clase de biblioteca L2: C1 tiene una dependencia de tiempo de compilación en L1, L2 e I1.
4) su clase C1 tiene un método foo(I1 i1)
y un método bar(L1 l1)
donde I1 es una interfaz y L1 es una clase que toma un parámetro que es la interfaz I1: C1 tiene una dependencia en tiempo de compilación de I1 y L1.
Básicamente, para hacer algo interesante, su clase necesita interactuar con otras clases e interfaces en el classpath. El gráfico de clase / interfaz formado por ese conjunto de interfaces de biblioteca produce la cadena de dependencia en tiempo de compilación. Las implementaciones de la biblioteca producen la cadena de dependencia en tiempo de ejecución. Tenga en cuenta que la cadena de dependencia en tiempo de ejecución depende del tiempo de ejecución o es lenta: si la implementación de L1 a veces depende de la instanciación de un objeto de clase L2, y esa clase solo se instancia en un escenario en particular, entonces no hay dependencia excepto ese escenario.
Java en realidad no vincula nada en tiempo de compilación. Solo verifica la sintaxis utilizando las clases coincidentes que encuentra en CLASSPATH. No es hasta el tiempo de ejecución que todo se ensambla y ejecuta según el CLASSPATH en ese momento.
Las dependencias en tiempo de compilación son solo las dependencias (otras clases) que usa directamente en la clase que está compilando. Las dependencias en tiempo de ejecución cubren las dependencias directas e indirectas de la clase que está ejecutando. Por lo tanto, las dependencias de tiempo de ejecución incluyen dependencias de dependencias y cualquier dependencia de reflexión como nombres de clase que tiene en a String
, pero que se usan en Class#forName()
.
A
, B.jar con B extends A
y C.jar con, C extends B
entonces C.jar depende del tiempo de compilación en A.jar, aunque la dependencia de C en A es indirecta.
Para Java, la dependencia del tiempo de compilación es la dependencia de su código fuente. Por ejemplo, si la clase A llama a un método de la clase B, entonces A es dependiente de B en el momento de la compilación, ya que A tiene que conocer B (tipo de B) para ser compilado. El truco aquí debería ser el siguiente: el código compilado aún no es un código completo y ejecutable. Incluye direcciones reemplazables (símbolos, metadatos) para las fuentes que aún no están compiladas o que existen en frascos externos. Durante la vinculación, esas direcciones deben reemplazarse por direcciones reales en la memoria. Para hacerlo correctamente, deben crearse los símbolos / direcciones correctos. Y esto se puede hacer con el tipo de clase (B). Creo que esa es la principal dependencia en el momento de la compilación.
La dependencia del tiempo de ejecución está más relacionada con el flujo de control real. Invoca direcciones de memoria reales. Es una dependencia que tiene cuando se ejecuta su programa. Necesita detalles de clase B aquí como implementaciones, no solo la información de tipo. Si la clase no existe, obtendrá RuntimeException y JVM saldrá.
Ambas dependencias, generalmente y no deberían, fluyen en la misma dirección. Sin embargo, esto es una cuestión de diseño OO.
En C ++, la compilación es un poco diferente (no solo a tiempo) pero también tiene un enlazador. Entonces, el proceso podría considerarse similar a Java, supongo.