【JDK命令行 一】手动编译Java源码与执行字节码命令合集(含外部依赖引用)
写作目标
记录常见的使用javac手动编译Java源码和java手动执行字节码的命令,一方面用于应对 Maven 和 Gradle 暂时无法使用的情况,临时生成class文件(使用自己的jar包);另一方面了解下构建工具做了哪些工作。
作者水平有限,行文中如有错误,希望评论告知,自当尽快修复。
一、编译源码
1. javac 命令
编译Java源码都是使用 javac 命令完成的,其语法如下:
javac [ options ] [ sourcefiles ] [ classes] [ @argfiles ]
- options:选项参数,比如-cp,-d
- sourcefiles:java源文件,多个文件以空格分开
- classes:用来处理处理注解
- @argfiles,就是包含 option 或 java 文件列表的文件路径,用@符号开头,就像上面的@javaOptions.txt和@javaFiles.txt
2. 编译仅使用 JDK 类库源码
javac sourcefiles
示例:
Main.java
public class Main { public static void main(String[] args) { System.out.println("Hello World"); } }
编译Main.java
javac Main.java
3. 指定文字编码
默认将使用平台的字符集,如Windows是GBK。为了防止乱码一般指定为 utf-8
javac -encoding encoding sourcefiles
示例:
#指定utf-8编码编译 javac -encoding utf-8 Main.java
4. 指定输出字节码路径
class文件将输出到指定路径下,如果有package,也会一并在指定路径下创建
javac -d path sourcefiles
示例:
#生成字节码到classes目录中 javac -d classes Main.java
注意:指定的目录需要提前创建
5. 指定classpath
指定JVM查找用户类文件、注解解释器和源文件的目录,即字节码、源码等的查找位置。
classpath确定流程:先从环境变量
CLASSPATH中获取,当用户指定classpath时将覆盖环境变量,如果没有环境变量且未用户设定,将以执行javac的路径向下查找。
#有以下两种写法,二者等效 javac -cp path sourcefiles javac -classpath path sourcefiles #path可以使用通配符*来匹配目录下一级jar包或class文件,比如下列写法 #javac -cp "libs/*" sourcefiles
示例:
引用 FastJson 的Main.java
import com.alibaba.fastjson.JSONObject; public class Main { public static void main(String[] args) { JSONObject json = new JSONObject(); json.put("hello", "world"); System.out.println(json.toJSONString()); } }
编译Main.java,fastjson的jar与Main.java同级目录,直接写jar包作为classpath仅适用于单个jar包引用时
javac -cp fastjson-1.2.73.jar Main.java javac -classpath fastjson-1.2.73.jar Main.java
当设置需要设置多个目录作为classpath时,在不同平台的写法不大一样
Linux/Unix平台
javac -cp "path1/*:path2/*" sourcefiles javac -classpath "path1/*:path2/*" sourcefiles
Windows平台
javac -cp "path1\*;path2\*" sourcefiles javac -classpath "path1\*;path2\*" sourcefiles
不同点仅在于多个目录间使用
:还是
;作为路径分割符、目录分割符是
/还是
\
6. 指定外部目录
指定外部目录,javac 在编译字节码时将会从下列目录中读取字节码或Jar包,完成编译。
#有以下两种写法,二者等效 javac -extdirs directories sourcefiles -Djava.ext.dirs=directories sourcefiles
示例:
编写Main.java引用多个jar包,指定外部目录编译
import com.alibaba.fastjson.JSONObject; import org.apache.commons.lang3.StringUtils; public class Main { public static void main(String[] args) { JSONObject json = new JSONObject(); json.put("hello", "world"); System.out.println(json.toJSONString()); System.out.println(StringUtils.equals("1", "1")); } }
fastjson与
commons.lang3处于同目录
libs时,编译命令:
javac -extdirs libs Main.java javac -Djava.ext.dirs=libs Main.java
7. 带package源码编译
Java使用目录作为package定位字节码,减少了重名问题,编译方式是类似的,可使用通配符来匹配待编译的
.java文件
#src目录下一级目录查找java源码文件编译 javac src/*.java #使用以上方式可能会少编译一些深层次目录下的源码,推荐使用操作系统的命令来查找 #Linux平台 javac $(find src -name "*.java") #Windows平台 where -r src *.java #收集源文件列表 javac <源文件列表> #手动拼接源文件路径,多个源文件以空格分开,如 javac package/A.java package/B.java
此种方式编译少量源文件还可以,源文件过长就会出现命令参数过长报错,可以参考下面章节中的 使用参数文件简化命令 解决此问题
8. 编译有依赖关系的源码
两种方法:
- 按顺序编译分别编译(编译被依赖,再编译依赖)
- 由
javac
自动编译(将要编译的源文件列表全部给到javac
命令后,顺序无所谓)
示例:
PrintService.java
public class PrintService { public void print(String msg){ System.out.println(msg); } }< ad8 /pre>
Main.java
public class Main { public static void main(String[] args) { PrintService printService = new PrintService(); printService.print("Hello World!"); } }1.按顺序编译:
javac PrintService.java javac Main.java2.自动编译
javac Main.java PrintService.java9. 使用参数文件简化命令
参数文件可以是javac命令中的部分内容,比如可以是java文件的路径列表文件,将参数保存为文本中供编译时引用,缩短执行命令长度,避免命令行参数过长报错。
可匹配源码目录下的java文件列表作为参数文件
find src -name "*.java" > sourcefiles.txtsourcefiles.txt
Main.java PrintService.java
接着就可以通过
@+sourcefiles.txt对列表文件进行引用,放到javac命令行中#生成字节码与源码同目录 javac @sourcefiles.txt #指定存在的目录输出字节码 javac -d target @sourcefiles.txt当然不仅是源码列表,还可以加上参数,如图
10. 编译脚本示例
10.1. Linux编译脚本 compile.sh
源码文件处在
src目录中,创建输出目录target, 依赖包目录lib在工程目录下创建如下编译脚本:
compile.sh
#!/bin/bash # 编译脚本 # @author: Hellxz #出现变量取值失败、报错立即停止 set -eu #定义变量 SOURCE=src TARGET=target LIBRARY=libs #清理历史编译结果 [ -d "${TARGET}" ] && rm -rf ${TARGET}/* #输出参数文件 echo "-d ${TARGET} -encoding utf-8" > argsfile find ${SOURCE} -name "*.java" >> argsfile #编译源文件 if [ -d "${LIBRARY}" ]; then javac -cp "${LIBRARY}/*" @argsfile else javac @argsfile fi #删除参数文件 rm -rf argsfile echo "compile success!"10.2. Windows编译脚本 compile.bat
@echo off REM 源码目录 set srcdir=src REM 目标目录 set targetdir=target REM jar包外部依赖目录 set libsdir=lib 2080 s REM 清理缓存 if exist %targetdir% ( echo clean target caches... del /f /s /q %targetdir%\*.* rd /s /q %targetdir% md %targetdir% echo clean caches success! ) else ( md %targetdir% ) REM 生成参数文件 echo generating argsfile.txt echo -d %targetdir% -encoding utf-8 > argsfile.txt where -r %srcdir% *.java >> argsfile.txt echo generate argsfile success! REM 编译 echo compiling... if exist %libsdir% ( javac -cp "%libsdir%\*" @argsfile.txt ) else ( javac @argsfile.txt ) REM 删除参数文件 del /f /q /a argsfile.txt echo compile success! pause二、执行字节码
1. java 执行字节码命令
java 命令用于执行 javac编译出的字节码文件,启动 Java 虚拟机。
java 命令语法为:
java [options] classname [args]
- options:选项参数,包含Java虚拟机参数等设定
- classname:字节码文件,
.class
后缀的文件 - args:参数,将作为 main 方法的参数传入程序中
options参考:https://docs.oracle.com/javase/8/docs/technotes/tools/windows/java.html#CBBIJCHG
2. 执行字节码文件
一般而言,执行 Java 程序直接用
java命令就可以
#执行带main方法的字节码 java mainclass
3. 执行带package的字节码
当源码中有package提示包名时,执行的class需要放在层层包名目录中,举例:
package samples; public class Main { public static void main(String[] args) { System.out.println("Hello World"); } }
编译后Main.class 的位置在
/opt/target/samples/Main.class
执行java命令就需要进到
/opt/target下,与第一层包目录平级
cd /opt/target java samples/Main.class
不进入包名目录上级,可以设置 classpath 来指定待执行查找class的起点
java -cp /opt/target samples/Main
4. 执行有外部依赖关系的字节码
src/samples/Main.java
package samples; import com.alibaba.fastjson.JSONObject; public class Main { public static void main(String[] args) { JSONObject json = new JSONObject(); json.put("hello", "world"); System.out.println(json.toJSONString()); } }
外部依赖libs目录、源码目录src、生成class目录target,src下有一个包为samples的Main.java,如下图
编译src目录源码,生成字节码到target下
javac -cp "src:libs/*" -d target $(find src -name "*.java")
设置classpath,执行字节码文件
java -cp "target:libs/*" samples.Main
参考资料:
https://docs.oracle.com/javase/8/docs/technotes/tools/windows/javac.html
https://docs.oracle.com/javase/8/docs/technotes/tools/windows/java.html
https://zhuanlan.zhihu.com/p/74229762
本文首发博客园Hellxz博客,https://www.cnblogs.com/hellxz/p/14819191.html
同步于CSDN 拾级而上,https://blog.csdn.net/u012586326/article/details/117335227
- Maven 手动添加第三方依赖包及编译打包和java命令行编译JAVA文件并使用jar命令打包
- 用命令编译、运行java项目(多个源码包,依赖第三方jar包)
- JAVA和JAVAC 命令行;java 带有包名编译并运行,附带外部依赖jar包运行
- 使用java命令行执行依赖外部jar包的class文件
- java命令行执行程序解决依赖外部jar包的问题
- (转)JAVA和JAVAC 命令行;java 带有包名编译并运行,附带外部依赖jar包运行
- 命令行下JDK自带编译javac和执行java,以及环境变量的原理总结
- java中执行外部命令
- Java执行外部命令,并把结果回显到控制台
- 实例源码:利用Java调用可执行命令
- 利用源码编译Android系统可执行命令
- java jdk安装 环境变量配置 cmd 命令行 编译java文件 运行.class文件 CLASSPATH配置
- Java执行外部命令,并把结果回显到控制台
- Java执行命令行下命令方法
- 手工编译和执行java程序的命令
- Java.SE.第1讲.Java.SE入门、JDK的下载与安装、第一个Java程序、Java程序的编译与执行
- Java执行外部命令,并把结果回显到控制台
- java JDK安装 编译 执行
- java 编译-执行 命令
- 怎么通过命令行编译和执行java文件