【JDK命令行 一】手動編譯Java源碼與執行位元組碼命令合集(含外部依賴引用)
- 2021 年 5 月 27 日
- 筆記
- Java基礎/JVM
寫作目標
記錄常見的使用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);
}
}
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參考://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
參考資料:
//docs.oracle.com/javase/8/docs/technotes/tools/windows/javac.html
//docs.oracle.com/javase/8/docs/technotes/tools/windows/java.html
//zhuanlan.zhihu.com/p/74229762
本文首發部落格園Hellxz部落格,//www.cnblogs.com/hellxz/p/14819191.html
同步於CSDN 拾級而上,//blog.csdn.net/u012586326/article/details/117335227