Qual é a diferença entre dependências de tempo de compilação e tempo de execução em Java? Está relacionado ao caminho da classe, mas como eles diferem?
Respostas:
Dependência de tempo de compilação : você precisa da dependência em seu CLASSPATH
para compilar seu artefato. Eles são produzidos porque você tem algum tipo de "referência" à dependência codificada em seu código, como chamar new
alguma classe, estender ou implementar algo (direta ou indiretamente) ou uma chamada de método usando a reference.method()
notação direta .
Dependência de tempo de execução : você precisa da dependência em seu CLASSPATH
para executar seu artefato. Eles são produzidos porque você executa o código que acessa a dependência (de forma codificada ou por meio de reflexão ou qualquer outra coisa).
Embora a dependência de tempo de compilação geralmente implique dependência de tempo de execução, você pode ter uma dependência apenas de tempo de compilação. Isso se baseia no fato de que o Java apenas vincula dependências de classe no primeiro acesso a essa classe, portanto, se você nunca acessar uma classe específica em tempo de execução porque um caminho de código nunca é percorrido, o Java irá ignorar a classe e suas dependências.
Exemplo disso
Em C.java (gera C.class):
package dependencies;
public class C { }
Em A.java (gera 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());
}
}
}
Neste caso, A
tem uma dependência em tempo de compilação em C
through B
, mas só terá uma dependência em tempo de execução em C se você passar alguns parâmetros durante a execução java dependencies.A
, pois a JVM só tentará resolver B
a dependência de C
quando chegar a executar B b = new B()
. Este recurso permite que você forneça em tempo de execução apenas as dependências das classes que você usa em seus caminhos de código e ignore as dependências do resto das classes no artefato.
Um exemplo fácil é olhar para uma api como a servlet api. Para fazer seus servlets compilarem, você precisa de servlet-api.jar, mas no tempo de execução o contêiner de servlet fornece uma implementação de API de servlet, portanto, você não precisa incluir servlet-api.jar em seu caminho de classe de tempo de execução.
O compilador precisa do classpath correto para compilar chamadas para uma biblioteca (dependências de tempo de compilação)
A JVM precisa do classpath correto para carregar as classes na biblioteca que você está chamando (dependências de tempo de execução).
Eles podem ser diferentes de duas maneiras:
1) se sua classe C1 chama a classe de biblioteca L1 e L1 chama a classe de biblioteca L2, então C1 tem uma dependência de tempo de execução em L1 e L2, mas apenas uma dependência de tempo de compilação em L1.
2) se sua classe C1 instancia dinamicamente uma interface I1 usando Class.forName () ou algum outro mecanismo, e a classe de implementação para a interface I1 é a classe L1, então C1 tem uma dependência de tempo de execução em I1 e L1, mas apenas uma dependência de tempo de compilação em I1.
Outras dependências "indiretas" que são iguais para tempo de compilação e tempo de execução:
3) sua classe C1 estende a classe de biblioteca L1 e L1 implementa a interface I1 e estende a classe de biblioteca L2: C1 tem uma dependência de tempo de compilação em L1, L2 e I1.
4) sua classe C1 tem um método foo(I1 i1)
e um método bar(L1 l1)
onde I1 é uma interface e L1 é uma classe que recebe um parâmetro que é a interface I1: C1 tem uma dependência de tempo de compilação em I1 e L1.
Basicamente, para fazer algo interessante, sua classe precisa fazer interface com outras classes e interfaces no caminho de classe. O gráfico de classe / interface formado por esse conjunto de interfaces de biblioteca produz a cadeia de dependência do tempo de compilação. As implementações da biblioteca geram a cadeia de dependência do tempo de execução. Observe que a cadeia de dependência do tempo de execução é dependente do tempo de execução ou falha lenta: se a implementação de L1 às vezes depende da instanciação de um objeto da classe L2 e essa classe só é instanciada em um cenário específico, então não há dependência, exceto esse cenário.
Na verdade, o Java não vincula nada em tempo de compilação. Ele apenas verifica a sintaxe usando as classes correspondentes que encontra no CLASSPATH. Não é até o tempo de execução que tudo é montado e executado com base no CLASSPATH naquele momento.
As dependências do tempo de compilação são apenas as dependências (outras classes) que você usa diretamente na classe que está compilando. As dependências de tempo de execução abrangem as dependências diretas e indiretas da classe que você está executando. Assim, as dependências de tempo de execução incluem dependências de dependências e quaisquer dependências de reflexão, como nomes de classe que você tem em a String
, mas são usadas em Class#forName()
.
A
, B.jar com B extends A
e C.jar com, C extends B
então C.jar depende do tempo de compilação de A.jar, embora a dependência de C em A seja indireta.
Para Java, a dependência do tempo de compilação é a dependência do código-fonte. Por exemplo, se a classe A chama um método da classe B, então A é dependente de B no momento da compilação, já que A precisa saber sobre B (tipo de B) a ser compilado. O truque aqui deve ser este: o código compilado ainda não é um código completo e executável. Inclui endereços substituíveis (símbolos, metadados) para as fontes que ainda não foram compiladas ou existentes em jars externos. Durante a vinculação, esses endereços devem ser substituídos por endereços reais na memória. Para fazer isso corretamente, símbolos / endereços corretos devem ser criados. E isso pode ser feito com o tipo da aula (B). Acredito que essa seja a principal dependência no momento da compilação.
A dependência do tempo de execução está mais relacionada ao fluxo de controle real. Envolve endereços de memória reais. É uma dependência que você tem quando seu programa está em execução. Você precisa de detalhes de classe B aqui, como implementações, não apenas as informações de tipo. Se a classe não existir, você obterá RuntimeException e a JVM será encerrada.
Ambas as dependências, geralmente e não devem, fluir na mesma direção. No entanto, isso é uma questão de design OO.
Em C ++, a compilação é um pouco diferente (não just-in-time), mas também tem um vinculador. Portanto, o processo pode ser considerado semelhante ao Java, eu acho.