在编译几个.java文件时,它们之间有一些依赖关系,我们是否需要按某种顺序编译它们?
依赖项必须是.class文件吗?或者依赖项可以是.java文件吗?
具体来说,当A.java依赖于从B.java文件编译的B.class文件,但是B.class尚未创建(即B.java文件尚未编译成B.class),我们可以编译A. java通过在java -cp
中指定B.java的目录?或者我们是否需要先将B.java编译成B.class,然后在编译A.java时在java -cp
中指定B.class的目录?
例如,来自https://dzone.com/articles/java-8-how-to-create-executable-fatjar-without-ide,./src/main/java/com/exec/one/Main.java
依赖于./src/main/java/com/exec/one/service/MagicService.java
,两者都尚未编译。
为什么以下编译失败?
$ javac ./src/main/java/com/exec/one/*.java -d ./out/
./src/main/java/com/exec/one/Main.java:3: error: package com.exec.one.service does not exist
import com.exec.one.service.MagicService;
^
./src/main/java/com/exec/one/Main.java:8: error: cannot find symbol
MagicService service = new MagicService();
^
symbol: class MagicService
location: class Main
./src/main/java/com/exec/one/Main.java:8: error: cannot find symbol
MagicService service = new MagicService();
^
symbol: class MagicService
location: class Main
3 errors
为什么以下编译成功?如何在一个javac
命令中编译它们?如何在汇编中使用-cp ./src/main/java
?编译过程中会发生什么?
$ javac -cp ./src/main/java ./src/main/java/com/exec/one/*.java ./src/main/java/com/exec/one/**/*.java
./双人床/卖弄/Java/com/exec/one/main.Java
package com.exec.one;
import com.exec.one.service.MagicService;
public class Main {
public static void main(String[] args){
System.out.println("Main Class Start");
MagicService service = new MagicService();
System.out.println("MESSAGE : " + service.getMessage());
}
}
./双人床/卖弄/Java/com/exec/one/service/magic service.Java
package com.exec.one.service;
public class MagicService {
private final String message;
public MagicService(){
this.message = "Magic Message";
}
public String getMessage(){
return message;
}
}
TL; DR如下所述,如果使用这个更简单的命令编译,只需要编译Main
类,编译器仍然会找到并编译所需的MagicService
类,因为它可以在类路径中找到源文件。
javac -cp ./src/main/java ./src/main/java/com/exec/one/Main.java
请参阅编译器文档页面的"Searching for Types" section。
为了您的方便,请在此处引用所有内容,并添加了突出显示(粗体和/或斜体):
要编译源文件,编译器通常需要有关类型的信息,但类型定义不在命令行中指定的源文件中。编译器需要在源文件中使用,扩展或实现的每个类或接口的类型信息。这包括源文件中未明确提及但通过继承提供信息的类和接口。
例如,当您创建子类
java.applet.Applet
时,您还使用Applet的祖先类:java.awt.Panel
,java.awt.Container
,java.awt.Component
和java.lang.Object
。当编译器需要类型信息时,它会搜索定义类型的源文件或类文件。编译器首先在引导程序和扩展类中搜索类文件,然后在用户类路径(默认情况下是当前目录)中搜索。通过设置
CLASSPATH
环境变量或使用-classpath
选项来定义用户类路径。如果设置
-sourcepath
选项,则编译器会在指示的路径中搜索源文件。否则,编译器将在用户类路径中搜索类文件和源文件。您可以使用
-bootclasspath
和-extdirs
选项指定不同的引导程序或扩展类。见Cross-Compilation Options。成功的类型搜索可以生成类文件,源文件或两者。如果两者都找到,那么您可以使用
-Xprefer
选项指示编译器使用哪个。如果指定了newer
,则编译器将使用这两个文件中较新的文件。如果指定了source
,则编译器使用源文件。默认值为newer
。如果类型搜索单独找到所需类型的源文件,或者作为
-Xprefer
选项设置的结果,则编译器会读取源文件以获取所需的信息。默认情况下,编译器也会编译源文件。您可以使用-implicit
选项指定行为。如果指定了none
,则不会为源文件生成类文件。如果指定了class
,则会为源文件生成类文件。在注释处理完成之前,编译器可能不会发现某些类型信息的需要。如果在源文件中找到类型信息且未指定
-implicit
选项,则编译器会发出警告,指出正在编译该文件而不受注释处理的影响。要禁用警告,请在命令行上指定文件(以便进行注释处理)或使用-implicit
选项指定是否应为此类源文件生成类文件。
为什么以下编译失败?
$ javac ./src/main/java/com/exec/one/*.java -d ./out/
因为Main.java
(在该命令中拾取的唯一文件)使用了一个名为com.exec.one.service.MagicService
的类,该类在类路径中不可用,也不是正在编译的文件之一。
为什么以下编译成功?
$ javac -cp ./src/main/java ./src/main/java/com/exec/one/*.java ./src/main/java/com/exec/one/**/*.java
因为Main.java
使用了一个名为com.exec.one.service.MagicService
的类,它也是正在编译的文件之一。
如何在一个
javac
命令中编译它们?
你拥有的只是一个命令。 javac
程序接受要编译的源文件列表
Usage: javac <options> <source files>
如何在汇编中使用
-cp ./src/main/java
?
它用于设置类路径,即。它包括编译期间可能需要的类文件。在你的例子中,它没用。
但是,如果您已经单独编译了MagicService
并将-cp
指向相应的MagicServe.class
文件所在的位置(考虑到与其包含的包匹配的目录结构),那么它将非常有用。这就是Java项目中包含第三方库的方式。
Java编译器不强加排序。简单地说,在编译时,所有必需的类必须是可用的,可以是通过编译的源文件,也可以是类路径中可用的类。
看起来你应该从路径“/ src / main / java”开始。只有在您的文件夹名称下才能包含(com.exec.one)。所以做一个“cd src / main / java”并尝试:
javac ./com/exec/one/*.java