写作目标

记录常见的使用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"));
}
}

fastjsoncommons.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. 编译有依赖关系的源码

两种方法:

  1. 按顺序编译分别编译(编译被依赖,再编译依赖)
  2. javac 自动编译(将要编译的源文件列表全部给到 javac 命令后,顺序无所谓)

示例:

PrintService.java

public class PrintService {
public void print(String msg){
System.out.println(msg);
}
}

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.java

2.自动编译

javac Main.java PrintService.java

9. 使用参数文件简化命令

参数文件可以是javac命令中的部分内容,比如可以是java文件的路径列表文件,将参数保存为文本中供编译时引用,缩短执行命令长度,避免命令行参数过长报错。

可匹配源码目录下的java文件列表作为参数文件

find src -name "*.java" > sourcefiles.txt

sourcefiles.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=libs 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

3. 执行有外部依赖关系的字节码

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

【JDK命令行 一】手动编译Java源码与执行字节码命令合集(含外部依赖引用)的更多相关文章

  1. 用命令行编译java并生成可执行的jar包

    用命令行编译java并生成可执行的jar包 1.编写源代码. 编写源文件:CardLayoutDemo.java并保存,例如:I:\myApp\CardLayoutDemo.java.程序结构如下: ...

  2. Android反编译(一)之反编译JAVA源码

    Android反编译(一) 之反编译JAVA源码 [目录] 1.工具 2.反编译步骤 3.实例 4.装X技巧 1.工具 1).dex反编译JAR工具  dex2jar   http://code.go ...

  3. 《Linux命令行与shell脚本编程大全 第3版》Linux命令行---6

    以下为阅读<Linux命令行与shell脚本编程大全 第3版>的读书笔记,为了方便记录,特地与书的内容保持同步,特意做成一节一次随笔,特记录如下: <Linux命令行与shell脚本 ...

  4. 《Linux命令行与shell脚本编程大全 第3版》Linux命令行---5

    以下为阅读<Linux命令行与shell脚本编程大全 第3版>的读书笔记,为了方便记录,特地与书的内容保持同步,特意做成一节一次随笔,特记录如下: <Linux命令行与shell脚本 ...

  5. 《Linux命令行与shell脚本编程大全 第3版》Linux命令行---4

    以下为阅读<Linux命令行与shell脚本编程大全 第3版>的读书笔记,为了方便记录,特地与书的内容保持同步,特意做成一节一次随笔,特记录如下: <Linux命令行与shell脚本 ...

  6. 《Linux命令行与shell脚本编程大全 第3版》Linux命令行---3

    以下为阅读<Linux命令行与shell脚本编程大全 第3版>的读书笔记,为了方便记录,特地与书的内容保持同步,特意做成一节一次随笔,特记录如下: <Linux命令行与shell脚本 ...

  7. 《Linux命令行与shell脚本编程大全 第3版》Linux命令行---2

    以下为阅读<Linux命令行与shell脚本编程大全 第3版>的读书笔记,为了方便记录,特地与书的内容保持同步,特意做成一节一次随笔,特记录如下: <Linux命令行与shell脚本 ...

  8. 《Linux命令行与shell脚本编程大全 第3版》Linux命令行---1

    以下为阅读<Linux命令行与shell脚本编程大全 第3版>的读书笔记,为了方便记录,特地与书的内容保持同步,特意做成一节一次随笔,特记录如下: <Linux命令行与shell脚本 ...

  9. 《Linux命令行与shell脚本编程大全 第3版》Linux命令行---0

    以下为阅读<Linux命令行与shell脚本编程大全 第3版>的读书笔记,为了方便记录,特地与书的内容保持同步,特意做成一节一次随笔,特记录如下: <Linux命令行与shell脚本 ...

随机推荐

  1. vim与系统剪贴版的交互

    1 概述 vim中的复制,删除,替换(d,r,s,x,y等)的内容都会被保存到默认的未命名的寄存器中,之后可以通过p进行粘贴,但是,这个寄存器不是系统的剪贴版,很多时候需要vim与系统剪贴版的交互,那 ...

  2. Day05_18_类和对象的含义与关系

    Java 类和对象 类的含义? 类属于引用数据类型,java语言中所有的.class都属于引用数据类型, 在类体当中,方法体之外定义的变量被称为 成员变量,成员变量若没有赋值,系统会默认赋值为0: 先 ...

  3. 基于excel的接口自动化测试框架:支持参数化、关联等

    1. 框架结构说明 2. 框架代码实现 action 包  case_action.py business_process 包 main_process.py util 包 global_var.py ...

  4. sql指令,增,删,查,改

    增 insert into table (name,sex,age) value('张三','男','20')   向表中的name,sex,age,分别添加张三,男,20的内容 查 select  ...

  5. 粗浅聊聊Python装饰器

    浅析装饰器 通常情况下,给一个对象添加新功能有三种方式: 直接给对象所属的类添加方法: 使用组合:(在新类中创建原有类的对象,重复利用已有类的功能) 使用继承:(可以使用现有类的,无需重复编写原有类进 ...

  6. Tomcat部署环境

    环境搭建:程序文件(开发人员),相关的软件(web服务器,应用服务器,数据服务器),硬件(服务器设备上),网络环境. 文档:部署文档说明书(操作系统,硬件配置,服务器软件及相关..) 下面举个列子,考 ...

  7. hdu3006 状态压缩+位运算+hash(小想法题)

    题意:        给了n个集合,问你这n个集合可以组合出多少种集合,可以自己,也可以两个,也可以三个....也可以n个集合组在一起. 思路:       是个小想法题目,要用到二进制压缩,位运算, ...

  8. 洛谷P1307 数字反转

    题目描述 给定一个整数,请将该数各个位上数字反转得到一个新数.新数也应满足整数的常见形式,即除非给定的原数为零,否则反转后得到的新数的最高位数字不应为零(参见样例2). 输入输出格式 输入格式: 输入 ...

  9. Linux中的DHCP服务

    目录 DHCP DHCP的报文类型 DHCP的部署 DHCP中继(DHCP代理) DHCP DHCP(Dynamic Host Configuration Protocol,动态主机配置协议)是一个局 ...

  10. 使用DirectX截屏

    网上有很多关于DirectX截屏的文章,但大都是屏幕截图,很少有窗口截图,本文则两者都涉及到,先讲如何截取整个屏幕,再讲如何截取某个窗口,其实二者的区别不大,只是某个参数的设置不同而已,最后我们还将扩 ...