如果多个.java文件之间存在依赖关系,我们是否需要按一定顺序进行编译?

时间:2019-04-11 16:01:22

标签: java javac

在编译多个具有相互依赖关系的.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;                                                                                                                                                                

    }                                                                                                                                                                                  

}

3 个答案:

答案 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.Appletjava.awt.Paneljava.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