在编译多个具有相互依赖关系的.java文件时,我们是否需要按一定顺序进行编译?
.class文件必须是依赖项吗?还是依赖项可以是.java文件?
具体来说,当A.java依赖于从B.java文件编译而来的B.class文件,但尚未创建B.class时(即B.java文件尚未编译为B.class),我们可以通过在java -cp
中为B.java指定目录来编译A.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
谢谢。
./ src / main / 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());
}
}
./ src / main / java / com / exec / one / service / MagicService.java
package com.exec.one.service;
public class MagicService {
private final String message;
public MagicService(){
this.message = "Magic Message";
}
public String getMessage(){
return message;
}
}
答案 0 :(得分:3)
TL; DR 如下所述,如果使用此更简单的命令进行编译,而您仅要求编译Main
类,则编译器仍会找到并编译所需的{{ 1}}类,因为它可以在类路径中找到源文件。
MagicService
请参见编译器文档页面的"Searching for Types" section。
为方便起见,在这里全部引用,并加了我的亮点(粗体和/或斜体):
要编译源文件,编译器通常需要有关类型的信息,但是类型定义不在命令行上指定的源文件中。编译器需要在源文件中使用,扩展或实现的每个类或接口的类型信息。其中包括源文件中未明确提及的类和接口,但它们通过继承提供信息。
例如,当您创建子类
javac -cp ./src/main/java ./src/main/java/com/exec/one/Main.java
时,您还将使用Applet的祖先类:java.applet.Applet
,java.awt.Panel
,java.awt.Container
和{{1} }。当编译器需要类型信息时,它会搜索定义类型的 source 文件或 class 文件。 编译器首先在引导类和扩展类中搜索 class 文件,然后在用户类路径(默认情况下为当前目录)中搜索。通过设置
java.awt.Component
环境变量或使用java.lang.Object
选项来定义用户类路径。如果设置了
CLASSPATH
选项,则编译器将在指示的路径中搜索源文件。否则,编译器会在用户类路径中搜索 class 文件和 source 文件。您可以使用
-classpath
和-sourcepath
选项指定不同的引导程序或扩展名类。参见Cross-Compilation Options。成功的类型搜索可能会产生类文件,源文件或两者。如果两者都找到,则可以使用
-bootclasspath
选项来指示编译器使用哪个。如果指定了-extdirs
,则编译器将使用两个文件中的较新版本。如果指定了-Xprefer
,则编译器将使用源文件。默认值为newer
。如果类型搜索本身或由于
source
选项的设置而找到所需类型的 source 文件,然后,编译器读取源文件以获得所需的信息。 默认情况下,编译器还会编译源文件。。您可以使用newer
选项指定行为。如果指定了-Xprefer
,则不会为源文件生成任何类文件。如果指定了-implicit
,则将为源文件生成类文件。直到注释处理完成后,编译器才可能发现需要某些类型信息。当在源文件中找到类型信息且未指定
none
选项时,编译器将发出警告,表明该文件正在编译中,没有经过注释处理。要禁用该警告,请在命令行上指定文件(以便对其进行注释处理),或使用class
选项指定是否应为此类源文件生成类文件。>
答案 1 :(得分:2)
为什么以下编译失败?
$ 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编译器不施加顺序。简而言之,在编译时,所有必需的类都必须可用,无论是通过正在编译的源文件还是通过类路径中的可用类。
答案 2 :(得分:1)
似乎您应该从路径“ / src / main / java”开始。仅在该文件夹下方,您就有与您的文件夹名称匹配的软件包(com.exec.one)。因此,执行“ cd src / main / java”并尝试:
javac ./com/exec/one/*.java