有关Javac的探究
用惯了IDE式的一键式编程,初次转到命令行中多少还是有些不适应的。 但IDE有IDE的方便,直接编译还是有着直接编译的好处的。毕竟是可以更深层次的接触的编译的过程。
对于javac
,发现并没有多少书来解释这个命令,通过javac -help
能看到一堆对参数的说明。
Usage: javac <options> <source files>
where possible options include:
-g Generate all debugging info
-g:none Generate no debugging info
-g:{lines,vars,source} Generate only some debugging info
-nowarn Generate no warnings
-verbose Output messages about what the compiler is doing
-deprecation Output source locations where deprecated APIs are used
-classpath <path> Specify where to find user class files and annotation processors
-cp <path> Specify where to find user class files and annotation processors
-sourcepath <path> Specify where to find input source files
-bootclasspath <path> Override location of bootstrap class files
-extdirs <dirs> Override location of installed extensions
-endorseddirs <dirs> Override location of endorsed standards path
-proc:{none,only} Control whether annotation processing and/or compilation is done.
-processor <class1>[,<class2>,<class3>...] Names of the annotation processors to run; bypasses default discovery process
-processorpath <path> Specify where to find annotation processors
-parameters Generate metadata for reflection on method parameters
-d <directory> Specify where to place generated class files
-s <directory> Specify where to place generated source files
-h <directory> Specify where to place generated native header files
-implicit:{none,class} Specify whether or not to generate class files for implicitly referenced files
-encoding <encoding> Specify character encoding used by source files
-source <release> Provide source compatibility with specified release
-target <release> Generate class files for specific VM version
-profile <profile> Check that API used is available in the specified profile
-version Version information
-help Print a synopsis of standard options
-Akey[=value] Options to pass to annotation processors
-X Print a synopsis of nonstandard options
-J<flag> Pass <flag> directly to the runtime system
-Werror Terminate compilation if warnings occur
@<filename> Read options and filenames from file
看着都眼晕,不过我们只需要对某几个Option来关注就可以。
@argfiles
话说用了这么久的javac
还头一次知道有这么个option
,在javac编译的时候我们可以不必每次都要
敲那么长的一串命令,可以先通过把编译的option
或是sourcefiles
写入到一个文件中然后通过@
来编译。
比如说,我们把javac
的参数放到options文件中,把sourcefile
放到source文件中,类似下边例子:
#Options
-d ../bin/
-g
-sourcepath .
#srcFiles
testA.java
testB.java
之后,通过命令javac @Options @srcFiles
来编译文件。
-classpath 与 -sourcepath
classpath
设定java要搜索的类的路径,即编译sourcefile
所需要依赖的class的路径。
sourcepath
设定java要搜索类的源码的路径,即编译sourcefile
所需要依赖的class的源码的路径。
一开始并不明白sourcepath的意义,以为是需要编译的源码的路径。后来看了看文档以及相关资料,才知道
sourcepath与classpath其实都是指要编译文件需要搜寻的依赖类的路径。只不过classpath指明的是已经编译好的
.class
文件的位置,而sourcepath指明的是依赖的class的源码的位置,如果javac
发现不存在或者现在的.class
不是最新版文件,会重新编译依赖文件来产生class文件。
我们可以通过-verbose
来查看编译过程。
$javac -verbose -sourcepath . testB.java
[解析开始时间 RegularFileObject[testB.java]]
[解析已完成, 用时 14 毫秒]
[源文件的搜索路径: .]
[类文件的搜索路径: /lib/jvm/jdk7/jre/lib/resources.jar,/lib/jvm/jdk7/jre/lib/rt.jar,/lib/jvm/jdk7/jre/lib/sunrsasign.jar,/lib/jvm/jdk7/jre/lib/jsse.jar,/lib/jvm/jdk7/jre/lib/jce.jar,/lib/jvm/jdk7/jre/lib/charsets.jar,/lib/jvm/jdk7/jre/lib/jfr.jar,/lib/jvm/jdk7/jre/classes,/lib/jvm/jdk7/jre/lib/ext/zipfs.jar,/lib/jvm/jdk7/jre/lib/ext/sunpkcs11.jar,/lib/jvm/jdk7/jre/lib/ext/localedata.jar,/lib/jvm/jdk7/jre/lib/ext/dnsns.jar,/lib/jvm/jdk7/jre/lib/ext/sunec.jar,/lib/jvm/jdk7/jre/lib/ext/sunjce_provider.jar,.,/lib/jvm/jdk7/lib.tools.jar,/home/yeoman123/Workspace/java/MOECPM,/home/yeoman123/Workspace/java/public/colt-1.2.0.jar,/home/yeoman123/Workspace/java/public/slf4j-api-1.7.12.jar]
[正在加载ZipFileIndexFileObject[/lib/jvm/jdk7/lib/ct.sym(META-INF/sym/rt.jar/java/lang/Object.class)]]
[正在加载ZipFileIndexFileObject[/lib/jvm/jdk7/lib/ct.sym(META-INF/sym/rt.jar/java/lang/String.class)]]
[正在检查testB]
[正在加载ZipFileIndexFileObject[/lib/jvm/jdk7/lib/ct.sym(META-INF/sym/rt.jar/java/lang/AutoCloseable.class)]]
[正在加载ZipFileIndexFileObject[/lib/jvm/jdk7/lib/ct.sym(META-INF/sym/rt.jar/java/lang/System.class)]]
[正在加载ZipFileIndexFileObject[/lib/jvm/jdk7/lib/ct.sym(META-INF/sym/rt.jar/java/io/PrintStream.class)]]
[正在加载ZipFileIndexFileObject[/lib/jvm/jdk7/lib/ct.sym(META-INF/sym/rt.jar/java/io/FilterOutputStream.class)]]
[正在加载ZipFileIndexFileObject[/lib/jvm/jdk7/lib/ct.sym(META-INF/sym/rt.jar/java/io/OutputStream.class)]]
[正在加载RegularFileObject[./testA.java]]
[解析开始时间 RegularFileObject[./testA.java]]
[解析已完成, 用时 1 毫秒]
[已写入RegularFileObject[testB.class]]
[正在检查testA]
[已写入RegularFileObject[./testA.class]]
[共 307 毫秒]
其中文件testB.java
里边对testA.java
里的类进行调用,然而并没有事先编译testA.java
文件,而是指定了sourcepath
为
当前目录,我们可以看到编译进程中首先找到testA.java
对其进行编译生成testA.class
,然后再对testB.java
进行编译。
需要注意的是:sourcepath
的默认路径与classpath
路径相同,但如果指定了之后会将原路径覆盖。
-d
-d
选项设定了类文件的目标目录,即你编译出来的class文件所存放的位置,这里是指存放的根目录。什么意思呢?如果你的文件中没有进行打包(package)
那么直接就把class文件生成到该目录下边。如果文件中声明了package的信息,那么就会以-d
后的path为根目录生成整个package的文件目录中。举个例子
package cn.edu.school.group.project;
public class testA{
//...
}
$ javac -d /home/user/java/bin/ testA.java
之后会将testA.class
生成到/home/user/java/bin/cn/edu/school/group/project中。如果想要import
该类,则需要指定
该类的根目录,即-cp /home/user/java/bin
即可。例如我在testB
中import
:
import cn.edu.school.group.project.testA;
public class testB{
public testB{
System.out.println("This is B Initialize");
}
public static void main(String[] args){
testA ta = new testA();
}
}
$ javac -d /home/user/java/bin -cp /home/user/java/bin testB.java
如果不指定cp
的话就会编译出错,或者将该目录添加到环境变量中其实也可以,但如果每个project都把classpath添加到环境变量中,
那环境变量会变得相当臃肿。
另外,关于classpath的搜索规则,参考@haolujun 大神的说明,java的类的寻找规则是这样子的:
class文件所在目录=Classpath+'\'+Package路径中'.'换成'\'
debug 选项
-g
选项为编译提供了调试信息,包括行号信息,源文件信息以及局部变量信息。缺省情况只生成行号以及源文件信息。
-g:none
不生成任何调试信息。
-g:{keylist}
指定生成调试信息的类型,keylist
包括source
提供源文件调试信息,lines
提供行号调试信息,以及
vars
提供局部变量调试信息。类型之间用逗号来分隔。
如果你不想生成任何调试信息的话,需要-g:none
来指定,不指定的话,即使不加-g
默认也会生成行号以及源文件信息的。
10 Nov 2015
Post by: MetaCoder